Compare commits

...
Author SHA1 Message Date
François-X. T. 6257940747 fix query cache 2026-06-24 00:41:33 -04:00
François-Xavier TalbotandGitHub 47c60788ff attributions fixes (#6487)
* feat(labrinth): begin & commit one transaction per file scans

* chore(labrinth): use ro pool for the query
2026-06-24 06:19:10 +02:00
Michael H. d1e8a9c574 perf: split attribution_enforced_versions subquery to use indexes
The original view used a single correlated `not exists` with an `or`,
which prevented the partial unique indexes on `attributions_exemptions`
from being used. Splitting into two `not exists` clauses lets each
anti-join use its own index instead of a full sequential scan.
2026-06-24 00:19:16 +02:00
aecsocketandGitHub 7bf85239de Improve error handling in account balance notifications (#6478)
* Improve error handling in account balance notifications

* fix compile
2026-06-23 21:11:02 +00:00
6380dd16d5 chore: modpack permissions blog (#6474)
* chore: add blog

* update bullets

* Update blog

* remove old files

---------

Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
2026-06-23 20:24:38 +00:00
Prospector 2376d571bb changelog 2026-06-23 12:31:41 -07:00
e7926083fb feat: new modpack permissions system (#6005)
* Begin external projects moderator database frontend

* add copy link button

* begin project page permissions settings

* MEL database backend routes

* include filename in external files

* wip: when uploading a version file, fetch its overrides as a list

* wip: override license checks

* improve FileHost ref counting

* file host read capability

* scan files when inserting version file

* add dependency sha1 field

* clean up version files

* wip: attributions

* update s3 file host

* attribution scanning basic works

* works

* insert attribution info after resolving

* add routes

* remove dep sha1 stuff

* prepr

* wip: override file sources

* add files_missing_attributions to versions

* return extended version info + attributed at/by

* hook up frontend to backend (mostly)

* expose version date published

* withholding version visibility

* frontend work

* prepr

* use api-client for img upload

* moar frontend

* prepr

* Add schema to attribution resolution and Flame project results

* sqlx prepare

* changes

* remove feature flag, fix optional proof images

* fix schema

* fmt

* fix deletion and file fetch

* prepare

* fix admonition

* update frontend stuff to new schema

* prepr

* attribution on dependencies

* fixes

* sqlx prepare

* fixes

* routes

* fix routes

* Version grandfathering

* prepare

* wip: bulk routes

* pushing what i've got rn

* include link in NoPermission

* change hash insert to bulk route

* query flame even if entry in MEL

* delete file with weird name

* Prioritise putting override files in existing groups even with ExternalLicense

* fix how hex bytes are handled in route

* feat: coolbot moderation changes (#6215)

* Update moderator checklist

* move permissions stage order

* Updated nagContext.versions to v3, added nag for permissions

* Update permissions.vue default messages

* prepr

---------

Co-authored-by: coolbot100s <76798835+coolbot100s@users.noreply.github.com>

* QA

* prepr

* should group by project

* return attribution resolution correctly

* updated by moderator info

* Track what moderator reviewed an attribution moderation status

* default deser FMA field

* new version page

* clean up fetching + add a couple missing features

* qa items

* prepr

* provide moderation package stuff with DI

* format?

* don't redact moderated_at

* move supplementary resources

* Reorganize moderation messages.

* Quick replies for external content permissions.

* prepare

* QA

* allow exempting projects

* Ignore Flame projects which 404

* fix ci

* fix cross project attribution stuff

* Fix permission error

* change what files get cscanned

* add more logging

* QA Jun 22

* fix

* idempotency

* Expose route for rescanning

* update blog link

---------

Co-authored-by: aecsocket <aecsocket@tutanota.com>
Co-authored-by: coolbot100s <76798835+coolbot100s@users.noreply.github.com>
Co-authored-by: aecsocket <43144841+aecsocket@users.noreply.github.com>
2026-06-23 21:27:51 +02:00
Michael H. a686a93858 fix: preview deployment incorrect branch data 2026-06-23 20:57:47 +02:00
Modrinth BotandGitHub 2876890fe5 New translations from Crowdin (main) (#6466) 2026-06-23 18:36:07 +00:00
Calum H.andGitHub 4c869d2a37 fix: broken icu script (#6480) 2026-06-23 14:43:12 +00:00
ThatGravyBoatandGitHub 525f60c389 fix(docs): missing environment field from v2 createVersion route (#6476)
* fix(docs): missing environment field from v2 createVersion route

* fix(docs): missing environment field from v2 createVersion route
2026-06-23 00:35:41 +00:00
Truman GaoandGitHub 042a27a17c fix: small breakdowns fixes (#6473)
* fix: members to member, no dependents to none

* feat: add fuzzy search to multiselect options search and projects dropdown

* fix: duplicate key
2026-06-22 20:40:53 +00:00
Prospector a747c2f290 changelog 2026-06-22 11:15:40 -07:00
Truman GaoandGitHub d756e5ab93 fix: homepage search relying on generated state (#6471) 2026-06-22 18:09:51 +00:00
Truman GaoandGitHub 1f8b1bd2bb feat: implement dependent projects + members breakdown/filter (#6395)
* feat: implement dependent project breakdown

* implement dependent project type filter

* feat: show dependent on column if there are multiple projects selected

* feat: different tooltip for when both project version and dependent breakdowns are on

* feat: implement dependents query filter

* feat: add project icon to query filter

* fix: dont show dependent on column when project breakdown is selected

* feat: add org icon

* remove: dependent on project column and project version project column

* refactor: pnpm prepr

* fix: all project selection gets unselected when reload page

* pnpm prepr

* fix: still using old search formatter

* feat: implement projects in table click to link to project page

* feat: handle analytics values that dont have a dependent project as a "No dependent" row

* pnpm prepr

* feat: remove org icon for dependents projects and change unknown label

* feat: do not include unknown rows for showing top 8

* feat: separate out no dependents based on download reason

* fix: use compatible dependencies for query filter options

* feat: implement members breakdown/filter frontend (#6470)

* feat: add link to user in table

* fix: do not allow breakdowns to occur without shared stats

* pnpm prepr

* fix: i18n and passing params for formatter
2026-06-22 18:09:00 +00:00
aecsocketandGitHub 23edb2351d Filter servers out from analytics dependents (#6445) 2026-06-22 15:07:02 +00:00
2c53e1240e feat: add analytics fetch users mapping (#6439)
* feat: add users mapping

* remove skip_serialize_if

---------

Co-authored-by: aecsocket <43144841+aecsocket@users.noreply.github.com>
2026-06-22 15:06:12 +00:00
aecsocketandGitHub b7d097d74d Batch incremental search event consumption (#6468)
* Batch incremental search event consumption

* fmt

* fix
2026-06-22 14:54:02 +00:00
aecsocketandGitHub 0a0ef1ce81 Fix account deletion deleting incorrect report threads (#6465) 2026-06-22 14:27:31 +00:00
d7917048d5 fix: allow PAT scope labels to wrap on long strings (#6456)
Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com>
2026-06-21 23:03:11 +00:00
Emma F.andGitHub a0017cf58f fix: nested details tags in markdown bodies (#6458) 2026-06-21 18:56:24 +00:00
SychicandGitHub b188bbf617 fix(maven): return requested file extension (#6455)
* fix(maven): return requested file extension

closes #2428, #2663
why was this hard coded lol

* fix(maven): handle other file types properly
2026-06-21 13:09:14 +00:00
Michael H.andGitHub 79ce1adf9d chore: kebab-case topic name 2026-06-21 14:42:24 +02:00
aecsocketandGitHub c6ef10e798 Incremental search indexing (#6453)
* wip: initial kafka support

* wip: kafka search queue

* wip: kafka consumer

* wip: create/delete docs

* reindexing works

* reindex in more places

* also incrementally update server pings

* cleanup

* tombi

* fix ci

* update tombi

* prepare

* fix ci

* fix

* fix PLEASE

* 🙏

* add route to force reindex project
2026-06-21 10:03:20 +00:00
aecsocketandGitHub be65589e4d Allow v2 version creation route to accept environment (#6454)
* Allow v2 version creation route to accept environment

* fix
2026-06-20 19:30:32 +00:00
ThatGravyBoatandGitHub 86a224419c fix: users projects shifting around while in queue (#6452)
fix: users projects shifting around while in queue due to download count changes
2026-06-20 15:55:03 +00:00
Calum H.andGitHub 49dd844fe1 feat: changelog app 0.14.8 (#6450) 2026-06-19 20:34:28 +00:00
Calum H.andGitHub 56536abb24 fix: modal shift on close + readd settings preload (#6448)
* fix: NewModal layout shift on close

* fix: preload queries in settings modals

* fix: any
2026-06-19 20:23:35 +00:00
aecsocketandGitHub ac5daad280 Tiltify API query backoff (#6447)
* Tiltify API querying backoff

* fix backoff

* fix clippy

* fix2
2026-06-19 19:02:22 +00:00
checkrdandGitHub 87eec7741b fix: remove max-width cap on floating action bar for mobile screens (#6430) 2026-06-19 18:38:33 +00:00
Modrinth BotandGitHub 4c9ee5b1c4 New translations from Crowdin (main) (#6403) 2026-06-19 18:08:03 +00:00
Truman GaoandGitHub caeab46ca6 fix: clicking scrollbar closes combobox (#6443)
* fix: clicking overlay scrollbar closes combobox

* feat: adjust multiselect scrollbar style

* feat: for dropdown mutliselect too
2026-06-19 18:07:13 +00:00
Calum H.andGitHub 8e6004fdd5 fix: billing fix for medal servers (#6437)
* fix: billing fix for medal servers

* fix: lint
2026-06-19 18:04:25 +00:00
aecsocketandGitHub 50b2b9567c Fix token separators in game version search (#6435)
* Fix token separators in game version search

* fix
2026-06-19 15:50:02 +00:00
Truman GaoandGitHub e0ea14226e feat: add dawn launcher user agent (#6441)
* feat: add dawn launcher user agent

* rename to Dawn
2026-06-19 14:31:47 +00:00
Calum H.andGitHub d2b85c9f8e fix: pre-migration for skins drag and drop change (#6402) 2026-06-19 14:16:51 +00:00
aecsocketandGitHub d33f00d2b1 Tighten URL slug validation (#6442)
* Tighten URL slug validation

* slug sanitization in frontend
2026-06-19 13:52:24 +00:00
aecsocketandGitHub b3257a0614 Allow revenue analytics to bucket by user ID (#6428)
* adjust

* convert to sqlx macros

* Allow users on a team to see rev splits of other users

* prepare

* clarify comment
2026-06-18 12:12:56 +00:00
François-Xavier TalbotandGitHub 486b467af2 cont: fix(frontend): dont assume wss for panel pingtest as well (#6421)
* fix(frontend): dont assume wss for panel pingtest as well

* fix(frontend): wss assumptions

* chore: fix export

* chore: prettier
2026-06-17 22:54:09 +00:00
aecsocketandGitHub 336050f4df Clear owned projects cache when deleting an org with projects (#6429) 2026-06-17 19:24:44 +00:00
aecsocketandGitHub 2bb1ef775c Serve stale Tiltify data if API is not available (#6427) 2026-06-17 14:58:07 +00:00
François-Xavier TalbotandGitHub 5ed322d281 feat(frontend): don't assume secure protocol for kyros websocket/fs (#6420)
* feat(frontend): don't assume https as websocket/fs protocol

* fix(frontend): actually do the same for websocket

* fix(frontend): don't strip ws path
2026-06-16 21:39:38 +00:00
Calum H.andGitHub 8591bc8ebc feat: changelog app 0.14.7 (#6419) 2026-06-16 19:05:19 +00:00
01fd18d550 Fix: update modpack export filter (#6359)
* Update Modpack export filters

* fix: move file export filtering from vue to rust

* chore: remove unused import

---------

Co-authored-by: Gravy Boat <gravy@thatgravyboat.tech>
2026-06-16 18:51:45 +00:00
François-Xavier TalbotandGitHub 3aaa2ef071 feat(app-backend): key circuit breaker by URI path pattern (#6383)
* feat(app-lib): split `FetchFence` on base URI path

* chore: fmt

* fix(app-lib): fix test timing
2026-06-16 18:49:06 +00:00
Truman GaoandGitHub a3aeeac2c3 fix: babric mods incorrectly detected as fabric (#6407)
* fix: babric mods incorrectly detected as fabric

* remove: standard version, just check for fabric normalized beta version number

* add comment
2026-06-16 18:47:16 +00:00
Calum H.andGitHub dfba212be4 fix: update checking version match reliability (#6411) 2026-06-16 18:45:31 +00:00
Calum H.andGitHub 3877999e53 fix: update browse page when installing dependencies (#6414)
* fix: update browse page when installing dependencies

* fix: lint

* fix: vers page

* fix: vers page link query persistence
2026-06-16 18:36:42 +00:00
Calum H.andGitHub 46a7cf490d feat: implement dependency deletion modal for content tab (#6410)
* feat: implement dependency deletion modal for content tab

* fix: handle bulk delete state

* fix: lint
2026-06-16 18:36:30 +00:00
Truman GaoandGitHub b5f7406998 fix: game version filter in search using quotes instead of backticks (#6415) 2026-06-16 18:22:52 +00:00
ThatGravyBoatandGitHub 3679d2c786 fix: maven version ranges not matching correctly (#6416) 2026-06-16 17:28:50 +00:00
Calum H.andGitHub f48a2f64db fix: content modals missing nexttick (#6412) 2026-06-16 16:01:49 +00:00
Calum H.andGitHub fe80ac10fc fix: hide content selection floating bar modal issues (#6413) 2026-06-16 16:01:09 +00:00
Prospector cf0d260802 fix blog 2026-06-15 08:41:40 -07:00
Prospector 503d34ee0f add spark blog post 2026-06-15 07:34:31 -07:00
ProspectorandGitHub bfbe66f73b feat: add product metadata to admin billing (#6371)
* feat: add product metadata to admin billing

* prepr
2026-06-11 15:30:39 -07:00
aecsocketandGitHub 64e17c7c1b Include compatible dependencies field in project search (#6366)
* include compatible dependency projects field

* fix
2026-06-11 20:18:44 +00:00
Calum H.andGitHub c082594825 fix: underscored users not searchable (#6362) 2026-06-11 19:27:28 +00:00
Prospector e5831d38eb changelog 2026-06-11 12:08:54 -07:00
Truman GaoandGitHub 1cabfe3e85 fix: allow mojang skins to be draggable (#6365)
* fix: allow mojang skins to be draggable

* pnpm prepr
2026-06-11 19:02:28 +00:00
36423eb5b5 Feat: system theme live update (#6197)
* fix: restore fixed render mode for logs tab

* feat: react to system theme changes in real time

When the 'System' theme is selected, listen for OS-level
prefers-color-scheme changes and update the app theme immediately,
without requiring a restart.

* Apply suggestion from @IMB11

Signed-off-by: Calum H. <hendersoncal117@gmail.com>

---------

Signed-off-by: Calum H. <hendersoncal117@gmail.com>
Co-authored-by: Calum H. <hendersoncal117@gmail.com>
Co-authored-by: Calum H. <calum@modrinth.com>
2026-06-11 13:32:15 +00:00
ArthurandGitHub 7d15fd3ac0 devex: integrate i18n ally extension (#6328)
* Integrate with i18n ally

* Update .gitignore

* Cleanup

* Dont force display language
2026-06-11 12:52:44 +00:00
c1780eef7d feat: drag and drop skins to reorder (#6357)
* feat: drag and drop skins to reorder

* feat: implement drag to reorder skins

* fix: ci

* remove: backend implementation

* regenerate sqlx

* fix: remove v-if selectable

* feat: remove drag handle

* refactor: pnpm prepr

* cargo fmt

* fix: dragging disable hover, wrong evt for edit skin + remove back of skin hover

---------

Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
2026-06-11 12:22:38 +00:00
aecsocketandGitHub d2a66bb2b0 Allow searching by project dependencies (#6350)
* Allow searching by project dependencies

* change field name

* use query macro
2026-06-10 17:30:03 +00:00
Calum H.andGitHub 98b1730e19 fix(skins): translucency issues with outer layer (#6345)
fix(skins): translucency issues with outer layer in main renderer
2026-06-10 13:28:21 +00:00
Calum H.andGitHub 180cef6eaa fix(skins): better offline handling (#6344) 2026-06-10 13:28:10 +00:00
Calum H.andGitHub b828fa17de feat(app-auth): add new error cases to mc auth error modal (#6349) 2026-06-10 13:28:00 +00:00
Truman GaoandGitHub 72a4e86c26 fix: analytics events page not in admin dropdown (#6352)
* fix: analytics events page not in admin dropdown

* pnpm prepr

* fix: add clearing date picker

* fix: date picker positioning not using rendered height
2026-06-09 21:01:42 +00:00
Calum H.andGitHub 93f8da1666 fix(app): notifications appearing with modal open (#6348)
fix: notifications appearing with modal open
2026-06-09 21:01:02 +00:00
Calum H.andGitHub f474940321 fix(skins): hardcoded skins not editable (#6347)
fix: hardcoded skins not editable
2026-06-09 18:23:19 +00:00
Calum H.andGitHub 83b0586fd2 fix(skins): loading state flash on delete (#6346) 2026-06-09 18:22:07 +00:00
Calum H.andGitHub 543d25e2d6 feat: use discord_id from discord sso for role grant (#6326)
* feat: properly use labrinth's linked discord accts sso for discord bot

* Update email for fixture user in SQL insert

Signed-off-by: Calum H. <calum@modrinth.com>

* fix: rev changes

* fix: copy on email

* fix: lint

* fix: rev

* fix: lint

---------

Signed-off-by: Calum H. <calum@modrinth.com>
2026-06-09 17:33:07 +00:00
aecsocketandGitHub bc5a761312 Add analytics meta for downloading dependent projects (#6318)
* Send dependent mod info to backend

* Parse meta from query

* condense dependent_on and modpack

* Analytics dependents response
2026-06-09 15:47:52 +00:00
Prospector 3258d7dbdf changelog 2026-06-08 15:56:14 -07:00
ProspectorandGitHub b5d1aeda85 feat: collapse uncommon plugin loaders behind "Show more" (#6342) 2026-06-08 15:53:42 -07:00
coolbotandGitHub 1cedbe5fda fix: upload a version nag (#6338)
Update core.ts

Use projectV3.versions instead of NagContext.versions to prevent the warning showing up when versions have not been loaded yet.
2026-06-08 22:06:47 +00:00
Truman GaoandGitHub 2c9bf58d1f feat: various analytics updates (#6330)
* feat: add button to view user analytics

* feat: add "Your projects" preset selection

* feat: fix revenue rounding for values under 1 and show full values for all statcards with tooltip

* fix: sum rounded value instead of raw value for tooltip total if it's under 1

* fix: show decimal in playtime statcard if under 1 hrs

* feat: disable playtime statcard for purely plugin projects

* refactor: pnpm prepr
2026-06-08 22:03:28 +00:00
Truman GaoandGitHub a92b5b08df fix: radius on inner button in Tabs (#6340) 2026-06-08 21:53:40 +00:00
Calum H.andGitHub 01d3fb47c4 feat: updater ui change + win restart fix (#6339)
* feat: updater ui change

* fix: fix width

* fix: impl fork tauri updater plugin

* fix: lint
2026-06-08 21:52:22 +00:00
Calum H.andGitHub 9404d46782 feat: release channels instance setting (#6252)
* feat: rough release channels impl draft

* feat: move to bottom + lint

* fix: invalidate content queries on channel change

* fix: change to chips

* fix: lint

* fix: copy
2026-06-08 17:10:59 +00:00
Calum H.andGitHub 926c72de42 fix: files tab drag and drop (#6325)
* fix: files drag drop

* fix: standardize drag and drop + fix files tab permissions
2026-06-08 17:03:30 +00:00
9729737d7d fix: replace save banner in env migration modal with physical bn (#6028)
* feat: dont use save banner for project env modal

* fix: modal btn gap

* fix: lint

* Update packages/ui/src/components/project/settings/environment/ProjectEnvironmentModal.vue

Signed-off-by: Calum H. <hendersoncal117@gmail.com>

* fix: lint

* fix: import

---------

Signed-off-by: Calum H. <hendersoncal117@gmail.com>
Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
2026-06-08 16:52:35 +00:00
Truman GaoandGitHub 97a8c11b50 fix: incorrectly showing empty state for versions page (#6303)
* fix: incorrectly showing empty state for versions page

* refactor: pnpm prepr
2026-06-08 16:40:00 +00:00
Truman GaoandGitHub 4d654162eb refactor: rename query-filter.ts to query-filter-utils.ts (#6335) 2026-06-08 16:39:12 +00:00
Modrinth BotandGitHub 33b1419bdf New translations from Crowdin (main) (#6332) 2026-06-08 16:20:46 +00:00
Calum H.andGitHub c28ba2e6a4 fix: z index of console tab expanded (#6334)
fix: z index
2026-06-08 15:56:14 +00:00
aecsocketandGitHub c7ba6ba8b2 Bump frontend version upload timeout (#6333)
Bump upload timeout
2026-06-08 15:43:18 +00:00
706 changed files with 57393 additions and 6065 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: taiki-e/install-action@b5fddbb5361bce8a06fb168c9d403a6cc552b084 # v2.75.29
- uses: taiki-e/install-action@fd2f5e3d644b484055ebf4268f474c565f148f25 # v2.81.9
with:
tool: tombi
- run: tombi lint
+15 -5
View File
@@ -20,6 +20,14 @@ on:
required: true
type: string
description: 'The environment to deploy to (staging-preview or production-preview)'
head_sha:
required: false
type: string
description: 'The commit SHA to deploy (defaults to github.sha)'
head_ref:
required: false
type: string
description: 'The branch name to deploy (defaults to github.ref_name)'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.environment || 'push' }}
@@ -40,12 +48,14 @@ jobs:
- name: Configure environment
id: meta
env:
ENV_INPUT: ${{ inputs.environment }}
REF: ${{ github.ref }}
DEPLOY_SHA: ${{ inputs.head_sha || github.sha }}
run: |
echo "cmd=deploy" >> $GITHUB_OUTPUT
ENV_INPUT="${{ inputs.environment }}"
REF="${{ github.ref }}"
SHA_SHORT="${GITHUB_SHA::8}"
SHA_SHORT="${DEPLOY_SHA::8}"
if [ "$ENV_INPUT" = "staging-preview" ]; then
echo "env=staging" >> $GITHUB_OUTPUT
@@ -106,8 +116,8 @@ jobs:
working-directory: ./apps/frontend
run: pnpm build
env:
CF_PAGES_BRANCH: ${{ github.ref_name }}
CF_PAGES_COMMIT_SHA: ${{ github.sha }}
CF_PAGES_BRANCH: ${{ inputs.head_ref || github.ref_name }}
CF_PAGES_COMMIT_SHA: ${{ inputs.head_sha || github.sha }}
CF_PAGES_URL: ${{ steps.meta.outputs.url }}
BUILD_ENV: ${{ steps.meta.outputs.env }}
PREVIEW: ${{ inputs.environment != '' && 'true' || 'false' }}
+9 -3
View File
@@ -24,6 +24,8 @@ jobs:
environment: [staging-preview, production-preview]
with:
environment: ${{ matrix.environment }}
head_sha: ${{ github.event.pull_request.head.sha }}
head_ref: ${{ github.event.pull_request.head.ref }}
deploy-storybook:
if: github.repository_owner == 'modrinth' && github.event.pull_request.head.repo.full_name == github.repository
@@ -68,7 +70,9 @@ jobs:
- name: Configure short SHA
id: meta
run: echo "sha_short=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: echo "sha_short=${HEAD_SHA::8}" >> $GITHUB_OUTPUT
- name: Deploy Storybook preview
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0
@@ -93,6 +97,8 @@ jobs:
- name: Read deployment URLs
id: urls
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
STAGING_PREVIEW_URL=$(cat deployment-url-staging-preview.txt)
PRODUCTION_PREVIEW_URL=$(cat deployment-url-production-preview.txt)
@@ -102,7 +108,7 @@ jobs:
echo "staging-preview-url=$STAGING_PREVIEW_URL" >> $GITHUB_OUTPUT
echo "production-preview-url=$PRODUCTION_PREVIEW_URL" >> $GITHUB_OUTPUT
echo "storybook-preview-url=https://git-${GITHUB_SHA::8}-storybook.modrinth.workers.dev" >> $GITHUB_OUTPUT
echo "storybook-preview-url=https://git-${HEAD_SHA::8}-storybook.modrinth.workers.dev" >> $GITHUB_OUTPUT
- name: Find comment
if: github.event_name == 'pull_request'
@@ -123,7 +129,7 @@ jobs:
body: |
## Frontend previews
Last deployed commit is [${{ github.sha }}](${{ github.event.pull_request.head.repo.html_url }}/commit/${{ github.sha }})
Last deployed commit is [${{ github.event.pull_request.head.sha }}](${{ github.event.pull_request.head.repo.html_url }}/commit/${{ github.event.pull_request.head.sha }})
| Environment | URL |
|-------------|-----|
+6
View File
@@ -99,6 +99,12 @@ jobs:
- name: Setup mold
uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 / Mold 2.41.0
- name: Install build dependencies
uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0
with:
packages: cmake libcurl4-openssl-dev
version: v2 # cache key
- name: Cache Cargo registry and index
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
+11 -4
View File
@@ -30,6 +30,10 @@ on:
- prod-with-staging-archon
default: prod
required: false
app-version-override:
description: Temporary app version override for updater testing
type: string
required: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -126,7 +130,10 @@ jobs:
- name: Set application version and environment
shell: bash
run: |
APP_VERSION="$(git describe --tags --always | sed -E 's/-([0-9]+)-(g[0-9a-fA-F]+)$/-canary+\1.\2/')"
APP_VERSION="${{ inputs.app-version-override }}"
if [ -z "$APP_VERSION" ]; then
APP_VERSION="$(git describe --tags --always | sed -E 's/-([0-9]+)-(g[0-9a-fA-F]+)$/-canary+\1.\2/')"
fi
BUILD_ENVIRONMENT="${{ inputs.environment || 'prod' }}"
echo "Setting application version to $APP_VERSION"
echo "Using environment $BUILD_ENVIRONMENT"
@@ -153,7 +160,7 @@ jobs:
fi
- name: Build macOS app
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' }}
run: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || inputs.app-version-override != '') && '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'
@@ -168,7 +175,7 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Build Linux app
run: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && 'pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json' || 'pnpm --filter=@modrinth/app run tauri build --config tauri-dev.conf.json' }}
run: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || inputs.app-version-override != '') && 'pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json' || 'pnpm --filter=@modrinth/app run tauri build --config tauri-dev.conf.json' }}
if: contains(matrix.platform, 'ubuntu')
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
@@ -179,7 +186,7 @@ jobs:
[System.Convert]::FromBase64String("$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_BASE64") | Set-Content -Path signer-client-cert.p12 -AsByteStream
$env:DIGICERT_ONE_SIGNER_CREDENTIALS = "$env:DIGICERT_ONE_SIGNER_API_KEY|$PWD\signer-client-cert.p12|$env:DIGICERT_ONE_SIGNER_CLIENT_CERTIFICATE_PASSWORD"
$env:JAVA_HOME = "$env:JAVA_HOME_17_X64"
${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && 'pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles "nsis,updater"' || 'pnpm --filter=@modrinth/app run tauri build --config tauri-dev.conf.json --verbose --bundles "nsis,updater"' }}
${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || inputs.app-version-override != '') && 'pnpm --filter=@modrinth/app run tauri build --config tauri-release.conf.json --verbose --bundles "nsis,updater"' || 'pnpm --filter=@modrinth/app run tauri build --config tauri-dev.conf.json --verbose --bundles "nsis,updater"' }}
Remove-Item -Path signer-client-cert.p12 -ErrorAction SilentlyContinue
if: contains(matrix.platform, 'windows')
env:
+3 -3
View File
@@ -99,8 +99,8 @@ jobs:
- name: Install build dependencies
uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0
with:
packages: libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev
version: v1 # cache key
packages: cmake libcurl4-openssl-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev
version: v2 # cache key
- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -153,7 +153,7 @@ jobs:
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
- name: Setup nextest
run: cargo binstall --no-confirm --secure cargo-nextest@0.9.133
run: cargo binstall --no-confirm --secure --locked cargo-nextest@0.9.133
# cargo-binstall does not have pre-built binaries for sqlx-cli, so we fall
# back to a cached cargo install
+1
View File
@@ -22,6 +22,7 @@ node_modules
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/i18n-ally-custom-framework.yml
# IDE - IntelliJ
.idea/*
+1 -1
View File
@@ -1,3 +1,3 @@
{
"recommendations": ["esbenp.prettier-vscode", "Vue.volar", "rust-lang.rust-analyzer"]
"recommendations": ["esbenp.prettier-vscode", "Vue.volar", "rust-lang.rust-analyzer", "lokalise.i18n-ally"]
}
+10
View File
@@ -0,0 +1,10 @@
languageIds:
- vue
- typescript
- javascript
- typescriptreact
usageMatchRegex:
- id:\s*['"]({key})['"]
monopoly: true
+11 -1
View File
@@ -31,5 +31,15 @@
"editor.defaultFormatter": "rust-lang.rust-analyzer"
},
"css.lint.unknownAtRules": "ignore",
"scss.lint.unknownAtRules": "ignore"
"scss.lint.unknownAtRules": "ignore",
"i18n-ally.localesPaths": [
"packages/ui/src/locales",
"apps/frontend/src/locales",
"packages/moderation/src/locales"
],
"i18n-ally.pathMatcher": "{locale}/index.{ext}",
"i18n-ally.keystyle": "flat",
"i18n-ally.sourceLanguage": "en-US",
"i18n-ally.namespace": false,
"i18n-ally.includeSubfolders": true
}
Generated
+102 -18
View File
@@ -1378,7 +1378,7 @@ dependencies = [
"bitflags 2.9.4",
"cexpr",
"clang-sys",
"itertools 0.12.1",
"itertools 0.13.0",
"log",
"prettyplease",
"proc-macro2",
@@ -2927,7 +2927,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
"libloading 0.7.4",
"libloading 0.8.8",
]
[[package]]
@@ -3260,7 +3260,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -4577,7 +4577,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.5.10",
"socket2 0.6.1",
"system-configuration",
"tokio",
"tower-service",
@@ -4974,7 +4974,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
"hermit-abi 0.5.2",
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -5277,6 +5277,7 @@ dependencies = [
"quick-xml 0.38.3",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rdkafka",
"redis",
"regex",
"reqwest 0.12.24",
@@ -5439,7 +5440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
"windows-targets 0.53.5",
]
[[package]]
@@ -5489,6 +5490,18 @@ dependencies = [
"zlib-rs",
]
[[package]]
name = "libz-sys"
version = "1.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
@@ -7701,7 +7714,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls 0.23.32",
"socket2 0.5.10",
"socket2 0.6.1",
"thiserror 2.0.17",
"tokio",
"tracing",
@@ -7738,9 +7751,9 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.5.10",
"socket2 0.6.1",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -7967,6 +7980,37 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rdkafka"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1beea247b9a7600a81d4cc33f659ce1a77e1988323d7d2809c7ed1c21f4c316d"
dependencies = [
"futures-channel",
"futures-util",
"libc",
"log",
"rdkafka-sys",
"serde",
"serde_derive",
"serde_json",
"slab",
"tokio",
]
[[package]]
name = "rdkafka-sys"
version = "4.10.0+2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e234cf318915c1059d4921ef7f75616b5219b10b46e9f3a511a15eb4b56a3f77"
dependencies = [
"cmake",
"libc",
"libz-sys",
"num_enum",
"pkg-config",
]
[[package]]
name = "redis"
version = "0.32.7"
@@ -8159,15 +8203,20 @@ dependencies = [
"http-body 1.0.1",
"http-body-util",
"hyper 1.7.0",
"hyper-rustls 0.27.7",
"hyper-util",
"js-sys",
"log",
"percent-encoding",
"pin-project-lite",
"rustls 0.23.32",
"rustls-pki-types",
"rustls-platform-verifier",
"serde",
"serde_json",
"sync_wrapper",
"tokio",
"tokio-rustls 0.26.4",
"tokio-util",
"tower",
"tower-http",
@@ -8413,7 +8462,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -8426,7 +8475,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.11.0",
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -8509,6 +8558,33 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls-platform-verifier"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [
"core-foundation 0.10.1",
"core-foundation-sys",
"jni",
"log",
"once_cell",
"rustls 0.23.32",
"rustls-native-certs 0.8.1",
"rustls-platform-verifier-android",
"rustls-webpki 0.103.7",
"security-framework 3.5.1",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
@@ -9654,7 +9730,6 @@ dependencies = [
"cfg-if",
"libc",
"psm",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
@@ -10264,9 +10339,8 @@ dependencies = [
[[package]]
name = "tauri-plugin-updater"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27cbc31740f4d507712550694749572ec0e43bdd66992db7599b89fbfd6b167b"
version = "2.10.1"
source = "git+https://github.com/modrinth/plugins-workspace?rev=0d30f2aa28ec668ce187d527da1c475da3c01cbc#0d30f2aa28ec668ce187d527da1c475da3c01cbc"
dependencies = [
"base64 0.22.1",
"dirs",
@@ -10278,7 +10352,8 @@ dependencies = [
"minisign-verify",
"osakit",
"percent-encoding",
"reqwest 0.12.24",
"reqwest 0.13.2",
"rustls 0.23.32",
"semver",
"serde",
"serde_json",
@@ -10419,7 +10494,7 @@ dependencies = [
"getrandom 0.3.3",
"once_cell",
"rustix 1.1.2",
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -11957,6 +12032,15 @@ dependencies = [
"libwebp-sys",
]
[[package]]
name = "webpki-root-certs"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"
@@ -12078,7 +12162,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.61.2",
]
[[package]]
+3 -1
View File
@@ -140,6 +140,7 @@ quick-xml = "0.38.3"
quote = { version = "1.0" }
rand = "=0.8.5" # Locked on 0.8 until argon2 and p256 update to 0.9
rand_chacha = "=0.3.1" # Locked on 0.3 until we can update rand to 0.9
rdkafka = { version = "0.36.2", features = ["cmake-build"] }
redis = "0.32.7"
regex = "1.12.2"
reqwest = { version = "0.12.24", default-features = false }
@@ -192,7 +193,7 @@ tauri-plugin-http = "2.5.7"
tauri-plugin-opener = "2.5.0"
tauri-plugin-os = "2.3.1"
tauri-plugin-single-instance = "2.3.4"
tauri-plugin-updater = { version = "2.9.0", default-features = false, features = [
tauri-plugin-updater = { git = "https://github.com/modrinth/plugins-workspace", rev = "0d30f2aa28ec668ce187d527da1c475da3c01cbc", default-features = false, features = [
"rustls-tls",
"zip",
] }
@@ -269,6 +270,7 @@ redundant_type_annotations = "warn"
result_large_err = "allow"
todo = "warn"
too_many_arguments = "allow"
type_complexity = "allow"
uninlined_format_args = "warn"
unnested_or_patterns = "warn"
wildcard_dependencies = "warn"
+2 -1
View File
@@ -44,7 +44,8 @@
"vue": "^3.5.13",
"vue-i18n": "^10.0.0",
"vue-router": "^4.6.0",
"vue-virtual-scroller": "v2.0.0-beta.8"
"vue-virtual-scroller": "v2.0.0-beta.8",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@eslint/compat": "^1.1.1",
+166 -115
View File
@@ -12,7 +12,6 @@ import {
ArrowBigUpDashIcon,
ChangeSkinIcon,
CompassIcon,
DownloadIcon,
ExternalIcon,
HomeIcon,
LeftArrowIcon,
@@ -45,7 +44,6 @@ import {
NotificationPanel,
OverflowMenu,
PopupNotificationPanel,
ProgressSpinner,
provideModalBehavior,
provideModrinthClient,
provideNotificationManager,
@@ -113,6 +111,16 @@ import {
setRestartAfterPendingUpdate,
} from '@/helpers/utils.js'
import i18n from '@/i18n.config'
import {
appUpdateState,
downloadAvailableAppUpdate,
getNextAppUpdatePopupTime,
installAvailableAppUpdate,
markAppUpdateActionable,
markAppUpdatePopupShown,
openAppUpdateChangelog,
setAppUpdateActions,
} from '@/providers/app-update.ts'
import { createContentInstall, provideContentInstall } from '@/providers/content-install'
import {
provideAppUpdateDownloadProgress,
@@ -297,6 +305,7 @@ onUnmounted(async () => {
document.querySelector('body').removeEventListener('click', handleClick)
document.querySelector('body').removeEventListener('auxclick', handleAuxClick)
unsubscribeSidebarToggle()
clearDelayedUpdatePopup()
await unlistenUpdateDownload?.()
})
@@ -313,18 +322,6 @@ const messages = defineMessages({
id: 'app.update.complete-toast.text',
defaultMessage: 'Click here to view the changelog.',
},
reloadToUpdate: {
id: 'app.update.reload-to-update',
defaultMessage: 'Reload to install update',
},
downloadUpdate: {
id: 'app.update.download-update',
defaultMessage: 'Download update',
},
downloadingUpdate: {
id: 'app.update.downloading-update',
defaultMessage: 'Downloading update ({percent}%)',
},
authUnreachableHeader: {
id: 'app.auth-servers.unreachable.header',
defaultMessage: 'Cannot reach authentication servers',
@@ -882,20 +879,21 @@ async function handleCommand(e) {
}
const appUpdateDownload = {
progress: ref(0),
progress: appUpdateState.progress,
version: ref(),
}
let unlistenUpdateDownload
const downloadProgress = computed(() => appUpdateDownload.progress.value)
const downloadPercent = computed(() => Math.trunc(appUpdateDownload.progress.value * 100))
const metered = ref(true)
const finishedDownloading = ref(false)
const restarting = ref(false)
const availableUpdate = ref(null)
const updateSize = ref(null)
const updatesEnabled = ref(true)
const {
metered,
finishedDownloading,
downloading,
restarting,
availableUpdate,
updateSize,
updatesEnabled,
} = appUpdateState
let delayedUpdatePopupTimeout = null
const updatePopupMessages = defineMessages({
updateAvailable: {
@@ -906,11 +904,6 @@ const updatePopupMessages = defineMessages({
id: 'app.update-popup.download-complete',
defaultMessage: 'Download complete',
},
body: {
id: 'app.update-popup.body',
defaultMessage:
'Modrinth App v{version} is ready to install! Reload to update now, or automatically when you close Modrinth App.',
},
meteredBody: {
id: 'app.update-popup.body.metered',
defaultMessage: `Modrinth App v{version} is available now! Since you're on a metered network, we didn't automatically download it.`,
@@ -926,7 +919,7 @@ const updatePopupMessages = defineMessages({
},
reload: {
id: 'app.update-popup.reload',
defaultMessage: 'Reload',
defaultMessage: 'Reload to update',
},
download: {
id: 'app.update-popup.download',
@@ -938,6 +931,106 @@ const updatePopupMessages = defineMessages({
},
})
function clearDelayedUpdatePopup() {
if (delayedUpdatePopupTimeout !== null) {
clearTimeout(delayedUpdatePopupTimeout)
delayedUpdatePopupTimeout = null
}
}
function getCurrentUpdatePromptStage() {
return finishedDownloading.value ? 'downloaded' : 'available'
}
function scheduleDelayedUpdatePopup() {
clearDelayedUpdatePopup()
const version = availableUpdate.value?.version
if (!version) {
return
}
const nextPopupTime = getNextAppUpdatePopupTime(version, getCurrentUpdatePromptStage())
if (nextPopupTime === null) {
return
}
const delay = nextPopupTime - Date.now()
if (delay <= 0) {
showDelayedUpdatePopup()
return
}
delayedUpdatePopupTimeout = setTimeout(showDelayedUpdatePopup, Math.min(delay, 2_147_483_647))
}
function showDelayedUpdatePopup() {
const update = availableUpdate.value
if (!update) {
return
}
const stage = getCurrentUpdatePromptStage()
const nextPopupTime = getNextAppUpdatePopupTime(update.version, stage)
if (nextPopupTime === null) {
return
}
if (Date.now() < nextPopupTime) {
scheduleDelayedUpdatePopup()
return
}
if (metered.value && !finishedDownloading.value) {
addPopupNotification({
title: formatMessage(updatePopupMessages.updateAvailable),
text: formatMessage(updatePopupMessages.meteredBody, { version: update.version }),
type: 'info',
autoCloseMs: null,
buttons: [
{
label: formatMessage(updatePopupMessages.download, {
size: formatBytes(updateSize.value ?? 0),
}),
action: () => downloadAvailableAppUpdate(),
color: 'brand',
},
{
label: formatMessage(updatePopupMessages.changelog),
action: () => openAppUpdateChangelog(),
keepOpen: true,
},
],
})
} else if (finishedDownloading.value) {
addPopupNotification({
title: formatMessage(updatePopupMessages.downloadComplete),
text: formatMessage(updatePopupMessages.downloadedBody, {
version: update.version,
}),
type: 'success',
autoCloseMs: null,
buttons: [
{
label: formatMessage(updatePopupMessages.reload),
action: () => installAvailableAppUpdate(),
color: 'brand',
},
{
label: formatMessage(updatePopupMessages.changelog),
action: () => openAppUpdateChangelog(),
keepOpen: true,
},
],
})
} else {
scheduleDelayedUpdatePopup()
return
}
markAppUpdatePopupShown(update.version, stage)
}
async function checkUpdates() {
if (!(await areUpdatesEnabled())) {
console.log('Skipping update check as updates are disabled in this build or environment')
@@ -961,11 +1054,15 @@ async function checkUpdates() {
if (isExistingUpdate) {
console.log('Update is already known')
scheduleDelayedUpdatePopup()
return
}
appUpdateDownload.progress.value = 0
finishedDownloading.value = false
downloading.value = false
updateSize.value = null
availableUpdate.value = update
console.log(`Update ${update.version} is available.`)
@@ -975,34 +1072,11 @@ async function checkUpdates() {
downloadUpdate(update)
} else {
console.log(`Metered connection detected, not auto-downloading update.`)
getUpdateSize(update.rid).then((size) => {
updateSize.value = size
addPopupNotification({
title: formatMessage(updatePopupMessages.updateAvailable),
text: formatMessage(updatePopupMessages.meteredBody, { version: update.version }),
type: 'info',
autoCloseMs: null,
buttons: [
{
label: formatMessage(updatePopupMessages.download, {
size: formatBytes(updateSize.value ?? 0),
}),
action: () => downloadAvailableUpdate(),
color: 'brand',
},
{
label: formatMessage(updatePopupMessages.changelog),
action: () => openUrl('https://modrinth.com/news/changelog?filter=app'),
keepOpen: true,
},
],
})
})
markAppUpdateActionable(update.version)
scheduleDelayedUpdatePopup()
}
getUpdateSize(update.rid).then((size) => (updateSize.value = size))
availableUpdate.value = update
}
await performCheck()
@@ -1024,12 +1098,17 @@ async function checkLinuxUpdates() {
const latestVersion = updates?.version
if (latestVersion && latestVersion !== currentVersion) {
addPopupNotification({
title: formatMessage(updatePopupMessages.updateAvailable),
text: formatMessage(updatePopupMessages.linuxBody, { version: latestVersion }),
type: 'info',
autoCloseMs: null,
})
markAppUpdateActionable(latestVersion)
const nextPopupTime = getNextAppUpdatePopupTime(latestVersion)
if (nextPopupTime !== null && Date.now() >= nextPopupTime) {
addPopupNotification({
title: formatMessage(updatePopupMessages.updateAvailable),
text: formatMessage(updatePopupMessages.linuxBody, { version: latestVersion }),
type: 'info',
autoCloseMs: null,
})
markAppUpdatePopupShown(latestVersion)
}
}
} catch (e) {
console.error('Failed to check for updates:', e)
@@ -1043,55 +1122,48 @@ async function downloadAvailableUpdate() {
async function downloadUpdate(versionToDownload) {
if (!versionToDownload) {
handleError(`Failed to download update: no version available`)
return
}
if (appUpdateDownload.progress.value !== 0) {
if (downloading.value || appUpdateDownload.progress.value !== 0) {
console.error(`Update ${versionToDownload.version} already downloading`)
return
}
console.log(`Downloading update ${versionToDownload.version}`)
downloading.value = true
try {
enqueueUpdateForInstallation(versionToDownload.rid).then(() => {
finishedDownloading.value = true
unlistenUpdateDownload?.().then(() => {
unlistenUpdateDownload = null
enqueueUpdateForInstallation(versionToDownload.rid)
.then(() => {
downloading.value = false
finishedDownloading.value = true
unlistenUpdateDownload?.().then(() => {
unlistenUpdateDownload = null
})
console.log('Finished downloading!')
markAppUpdateActionable(versionToDownload.version, 'downloaded')
scheduleDelayedUpdatePopup()
})
console.log('Finished downloading!')
addPopupNotification({
title: formatMessage(updatePopupMessages.downloadComplete),
text: formatMessage(updatePopupMessages.downloadedBody, {
version: versionToDownload.version,
}),
type: 'success',
autoCloseMs: null,
buttons: [
{
label: formatMessage(updatePopupMessages.reload),
action: () => installUpdate(),
color: 'brand',
},
{
label: formatMessage(updatePopupMessages.changelog),
action: () => openUrl('https://modrinth.com/news/changelog?filter=app'),
keepOpen: true,
},
],
.catch((e) => {
downloading.value = false
appUpdateDownload.progress.value = 0
handleError(e)
})
})
unlistenUpdateDownload = await subscribeToDownloadProgress(
appUpdateDownload,
versionToDownload.version,
)
} catch (e) {
downloading.value = false
appUpdateDownload.progress.value = 0
handleError(e)
}
}
async function installUpdate() {
restarting.value = true
try {
await setRestartAfterPendingUpdate(true)
} catch (e) {
@@ -1104,6 +1176,12 @@ async function installUpdate() {
}, 250)
}
setAppUpdateActions({
download: downloadAvailableUpdate,
install: installUpdate,
changelog: () => openUrl('https://modrinth.com/news/changelog?filter=app'),
})
async function openModrinthProjectLinkInApp(parsed) {
const { slug, pathSuffix, url } = parsed
const loadToken = loading.begin()
@@ -1373,33 +1451,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
<PlusIcon />
</NavButton>
<div class="flex flex-grow"></div>
<Transition name="nav-button-animated">
<div v-if="availableUpdate && !restarting && (finishedDownloading || metered)">
<NavButton
v-tooltip.right="
formatMessage(
finishedDownloading
? messages.reloadToUpdate
: downloadProgress === 0
? messages.downloadUpdate
: messages.downloadingUpdate,
{
percent: downloadPercent,
},
)
"
:to="finishedDownloading ? installUpdate : downloadAvailableUpdate"
>
<ProgressSpinner
v-if="downloadProgress > 0 && downloadProgress < 1"
class="text-brand"
:progress="downloadProgress"
/>
<RefreshCwIcon v-else-if="finishedDownloading" class="text-brand" />
<DownloadIcon v-else class="text-brand" />
</NavButton>
</div>
</Transition>
<NavButton
v-tooltip.right="formatMessage(commonMessages.settingsLabel)"
:to="() => $refs.settingsModal.show()"
@@ -1,5 +1,5 @@
<template>
<div class="flex gap-4 items-center">
<div class="flex gap-2 items-center">
<ButtonStyled
v-if="hasActiveLoadingBars && !hasVisibleActiveDownloadToasts"
color="brand"
@@ -14,6 +14,31 @@
<UnplugIcon class="text-secondary" />
<span class="text-sm text-contrast"> {{ formatMessage(messages.offline) }} </span>
</div>
<ButtonStyled color="brand" type="outlined" hover-color-fill="background">
<button
v-if="showUpdatePill"
type="button"
class="!h-[34px] overflow-hidden text-sm !transition-[width,opacity,transform,background-color,color,filter] !duration-200 ease-out"
:class="[
updatePillWidthClass,
{
'update-pill-ready-hidden': finishedDownloading && !animateReadyPill,
'update-pill-ready-visible': finishedDownloading && animateReadyPill,
},
]"
:disabled="isUpdateDownloading"
:aria-busy="isUpdateDownloading"
@click="handleUpdateClick"
>
<RefreshCwIcon v-if="finishedDownloading" :class="{ 'animate-spin': restarting }" />
<DownloadIcon v-else />
<span v-if="isUpdateDownloading">
{{ formatMessage(messages.downloadingUpdate) }}
<span class="inline-block w-[3ch] text-right tabular-nums">{{ downloadPercent }}%</span>
</span>
<span v-else>{{ updateLabel }}</span>
</button>
</ButtonStyled>
<div
class="flex border-solid border-surface-5 text-sm items-center gap-2 py-1.5 px-3 rounded-xl border"
>
@@ -119,6 +144,7 @@ import {
DownloadIcon,
DropdownIcon,
OnlineIndicatorIcon,
RefreshCwIcon,
StarIcon,
StopCircleIcon,
TerminalSquareIcon,
@@ -135,7 +161,7 @@ import {
} from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { Dropdown } from 'floating-vue'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { trackEvent } from '@/helpers/analytics'
@@ -145,6 +171,11 @@ import { get_many as getInstances } from '@/helpers/profile.js'
import type { LoadingBar } from '@/helpers/state'
import { progress_bars_list } from '@/helpers/state'
import type { GameInstance } from '@/helpers/types'
import {
appUpdateState,
downloadAvailableAppUpdate,
installAvailableAppUpdate,
} from '@/providers/app-update'
const { handleError } = injectNotificationManager()
const popupNotificationManager = injectPopupNotificationManager()
@@ -209,8 +240,96 @@ const messages = defineMessages({
id: 'app.action-bar.view-active-downloads',
defaultMessage: 'View active downloads',
},
update: {
id: 'app.action-bar.update',
defaultMessage: 'Update',
},
downloadingUpdate: {
id: 'app.action-bar.downloading-update',
defaultMessage: 'Downloading update',
},
reloadToUpdate: {
id: 'app.action-bar.reload-to-update',
defaultMessage: 'Reload to update',
},
})
const {
downloading,
downloadPercent,
downloadProgress,
finishedDownloading,
isVisible: isUpdateVisible,
metered,
restarting,
} = appUpdateState
const isUpdateDownloading = computed(
() =>
downloading.value ||
(downloadProgress.value > 0 && downloadProgress.value < 1 && !finishedDownloading.value),
)
const showUpdatePill = computed(
() => isUpdateVisible.value && (finishedDownloading.value || metered.value),
)
const animateReadyPill = ref(false)
const updateLabel = computed(() => {
if (isUpdateDownloading.value) {
return formatMessage(messages.downloadingUpdate)
}
if (finishedDownloading.value) {
return formatMessage(messages.reloadToUpdate)
}
return formatMessage(messages.update)
})
const updatePillWidthClass = computed(() => {
if (isUpdateDownloading.value) {
return 'w-[219px]'
}
if (finishedDownloading.value) {
return 'w-[166px]'
}
return '!w-[96px]'
})
let readyPillAnimationFrame: number | null = null
watch([showUpdatePill, finishedDownloading], async ([show, ready], [wasShown, wasReady]) => {
if (readyPillAnimationFrame !== null) {
cancelAnimationFrame(readyPillAnimationFrame)
readyPillAnimationFrame = null
}
if (!show || !ready) {
animateReadyPill.value = false
return
}
if (wasShown && wasReady) {
return
}
animateReadyPill.value = false
await nextTick()
readyPillAnimationFrame = requestAnimationFrame(() => {
animateReadyPill.value = true
readyPillAnimationFrame = null
})
})
async function handleUpdateClick() {
if (isUpdateDownloading.value) {
return
}
if (finishedDownloading.value) {
await installAvailableAppUpdate()
} else {
await downloadAvailableAppUpdate()
}
}
const currentProcesses = ref<RunningProcess[]>([])
const selectedProcess = ref<RunningProcess | undefined>()
@@ -469,5 +588,20 @@ onBeforeUnmount(() => {
window.removeEventListener('online', handleOnline)
unlistenProcess()
unlistenLoading()
if (readyPillAnimationFrame !== null) {
cancelAnimationFrame(readyPillAnimationFrame)
}
})
</script>
<style scoped>
.update-pill-ready-hidden {
opacity: 0;
transform: scale(0.96);
}
.update-pill-ready-visible {
opacity: 1;
transform: scale(1);
}
</style>
@@ -89,14 +89,9 @@ const initFiles = async () => {
disabled:
folder === 'profile.json' ||
folder.startsWith('modrinth_logs') ||
folder.startsWith('.fabric'),
folder.startsWith('.fabric') ||
folder.startsWith('__MACOSX'),
}))
.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 !== '') {
@@ -4,12 +4,14 @@ import {
Avatar,
ButtonStyled,
Checkbox,
Chips,
defineMessages,
injectNotificationManager,
OverflowMenu,
StyledInput,
useVIntl,
} from '@modrinth/ui'
import { useQueryClient } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import { open } from '@tauri-apps/plugin-dialog'
import { computed, type Ref, ref, watch } from 'vue'
@@ -25,14 +27,22 @@ import type { GameInstance } from '../../../helpers/types'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const router = useRouter()
const queryClient = useQueryClient()
const deleteConfirmModal = ref()
const { instance } = injectInstanceSettings()
type ReleaseChannel = GameInstance['preferred_update_channel']
const releaseChannelOptions: ReleaseChannel[] = ['release', 'beta', 'alpha']
const title = ref(instance.value.name)
const icon: Ref<string | undefined> = ref(instance.value.icon_path)
const groups = ref([...instance.value.groups])
const savingReleaseChannel = ref(false)
const selectedReleaseChannel = ref<ReleaseChannel>(instance.value.preferred_update_channel)
const releaseChannelDisabledItems = computed<ReleaseChannel[]>(() =>
savingReleaseChannel.value ? [...releaseChannelOptions] : [],
)
const newCategoryInput = ref('')
@@ -51,6 +61,52 @@ const availableGroups = computed(() => [
...new Set([...allInstances.value.flatMap((instance) => instance.groups), ...groups.value]),
])
function formatReleaseChannelLabel(channel: ReleaseChannel) {
switch (channel) {
case 'release':
return formatMessage(messages.updateChannelRelease)
case 'beta':
return formatMessage(messages.updateChannelBeta)
case 'alpha':
return formatMessage(messages.updateChannelAlpha)
}
}
function formatReleaseChannelDescription(channel: ReleaseChannel) {
switch (channel) {
case 'release':
return formatMessage(messages.updateChannelReleaseDescription)
case 'beta':
return formatMessage(messages.updateChannelBetaDescription)
case 'alpha':
return formatMessage(messages.updateChannelAlphaDescription)
}
}
watch(
() => [instance.value.path, instance.value.preferred_update_channel] as const,
() => {
if (!savingReleaseChannel.value) {
selectedReleaseChannel.value = instance.value.preferred_update_channel
}
},
)
watch(selectedReleaseChannel, async (channel, previousChannel) => {
const previousReleaseChannel = previousChannel ?? instance.value.preferred_update_channel
if (channel === instance.value.preferred_update_channel) return
savingReleaseChannel.value = true
const profilePath = instance.value.path
await edit(profilePath, { preferred_update_channel: channel })
.then(() => queryClient.invalidateQueries({ queryKey: ['linkedModpackInfo', profilePath] }))
.catch((error) => {
selectedReleaseChannel.value = previousReleaseChannel
handleError(error)
})
savingReleaseChannel.value = false
})
async function resetIcon() {
icon.value = undefined
await edit_icon(instance.value.path, null).catch(handleError)
@@ -175,6 +231,38 @@ const messages = defineMessages({
id: 'instance.settings.tabs.general.duplicate-button',
defaultMessage: 'Duplicate',
},
updateChannel: {
id: 'instance.settings.tabs.general.update-channel',
defaultMessage: 'Update channel',
},
updateChannelReleaseDescription: {
id: 'instance.settings.tabs.general.update-channel.release.description',
defaultMessage: 'Only release versions will be shown as available updates.',
},
updateChannelBetaDescription: {
id: 'instance.settings.tabs.general.update-channel.beta.description',
defaultMessage: 'Release and beta versions will be shown as available updates.',
},
updateChannelAlphaDescription: {
id: 'instance.settings.tabs.general.update-channel.alpha.description',
defaultMessage: 'Release, beta, and alpha versions will be shown as available updates.',
},
updateChannelRelease: {
id: 'instance.settings.tabs.general.update-channel.release',
defaultMessage: 'Release',
},
updateChannelBeta: {
id: 'instance.settings.tabs.general.update-channel.beta',
defaultMessage: 'Beta',
},
updateChannelAlpha: {
id: 'instance.settings.tabs.general.update-channel.alpha',
defaultMessage: 'Alpha',
},
selectUpdateChannelAriaLabel: {
id: 'instance.settings.tabs.general.update-channel.select',
defaultMessage: 'Select update channel',
},
deleteInstance: {
id: 'instance.settings.tabs.general.delete',
defaultMessage: 'Delete instance',
@@ -304,6 +392,23 @@ const messages = defineMessages({
</p>
</div>
<div class="flex flex-col gap-2.5 mt-6">
<h2 class="m-0 text-lg font-semibold text-contrast block">
{{ formatMessage(messages.updateChannel) }}
</h2>
<Chips
v-model="selectedReleaseChannel"
:items="releaseChannelOptions"
:format-label="formatReleaseChannelLabel"
:capitalize="false"
:disabled-items="releaseChannelDisabledItems"
:aria-label="formatMessage(messages.selectUpdateChannelAriaLabel)"
/>
<p class="m-0">
{{ formatReleaseChannelDescription(selectedReleaseChannel) }}
</p>
</div>
<div class="flex flex-col gap-2.5 mt-6">
<h2 id="delete-instance-label" class="m-0 text-lg font-semibold text-contrast block">
{{ formatMessage(messages.deleteInstance) }}
@@ -13,7 +13,7 @@ import {
} from '@modrinth/ui'
import type { GameVersionTag, PlatformTag } from '@modrinth/utils'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed, ref, shallowRef } from 'vue'
import { computed, ref } from 'vue'
import { trackEvent } from '@/helpers/analytics'
import { get_project_versions, get_version } from '@/helpers/cache'
@@ -46,55 +46,62 @@ debug('metadata load: start', {
installStage: instance.value.install_stage,
})
const [
fabric_versions,
forge_versions,
quilt_versions,
neoforge_versions,
all_game_versions,
loaders,
] = await Promise.all([
get_loader_versions('fabric')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('forge')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('quilt')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('neo')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_game_versions()
.then((gameVersions: GameVersionTag[]) => shallowRef(gameVersions))
.catch(handleError),
get_loaders()
.then((value: PlatformTag[]) =>
value
.filter(
(item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla',
)
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
.then((loader: PlatformTag[]) => ref(loader))
.catch(handleError),
])
function getSupportedModpackLoaders() {
return get_loaders().then((value: PlatformTag[]) =>
value
.filter((item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla')
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
}
debug('metadata load: done', {
hasFabricManifest: !!fabric_versions?.value,
hasForgeManifest: !!forge_versions?.value,
hasQuiltManifest: !!quilt_versions?.value,
hasNeoforgeManifest: !!neoforge_versions?.value,
gameVersions: all_game_versions?.value?.length ?? 0,
availablePlatforms: loaders?.value?.map((loader) => loader.name) ?? [],
const fabricVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'fabric'],
queryFn: () => get_loader_versions('fabric') as Promise<Manifest>,
})
const forgeVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'forge'],
queryFn: () => get_loader_versions('forge') as Promise<Manifest>,
})
const quiltVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'quilt'],
queryFn: () => get_loader_versions('quilt') as Promise<Manifest>,
})
const neoforgeVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'neo'],
queryFn: () => get_loader_versions('neo') as Promise<Manifest>,
})
const gameVersionsQuery = useQuery({
queryKey: ['instance-settings', 'game-versions'],
queryFn: () => get_game_versions() as Promise<GameVersionTag[]>,
})
const loadersQuery = useQuery({
queryKey: ['instance-settings', 'loaders', 'modpack'],
queryFn: getSupportedModpackLoaders,
})
const { data: modpackInfo } = useQuery({
const metadataLoading = computed(() =>
[
fabricVersionsQuery,
forgeVersionsQuery,
quiltVersionsQuery,
neoforgeVersionsQuery,
gameVersionsQuery,
loadersQuery,
].some((query) => query.isLoading.value),
)
debug('metadata queries configured', {
instancePath: instance.value.path,
loader: instance.value.loader,
gameVersion: instance.value.game_version,
})
const modpackInfoQuery = useQuery({
queryKey: computed(() => ['linkedModpackInfo', instance.value.path]),
queryFn: () => get_linked_modpack_info(instance.value.path, 'must_revalidate'),
enabled: computed(() => !!instance.value.linked_data?.project_id && !offline),
})
const modpackInfo = modpackInfoQuery.data
const repairing = ref(false)
const reinstalling = ref(false)
@@ -107,17 +114,17 @@ const messages = defineMessages({
})
function getManifest(loader: string) {
const map: Record<string, typeof fabric_versions> = {
fabric: fabric_versions,
forge: forge_versions,
quilt: quilt_versions,
neoforge: neoforge_versions,
const map: Record<string, Manifest | undefined> = {
fabric: fabricVersionsQuery.data.value,
forge: forgeVersionsQuery.data.value,
quilt: quiltVersionsQuery.data.value,
neoforge: neoforgeVersionsQuery.data.value,
}
const manifest = map[loader]
debug('getManifest:', {
loader,
hasManifest: !!manifest?.value,
gameVersions: manifest?.value?.gameVersions?.length ?? 0,
hasManifest: !!manifest,
gameVersions: manifest?.gameVersions?.length ?? 0,
})
return manifest
}
@@ -143,7 +150,7 @@ provideAppBackup({
provideInstallationSettings({
closeSettings: closeModal,
loading: ref(false),
loading: computed(() => metadataLoading.value || modpackInfoQuery.isLoading.value),
installationInfo: computed(() => {
const rows = [
{
@@ -185,14 +192,14 @@ provideInstallationSettings({
currentPlatform: computed(() => instance.value.loader),
currentGameVersion: computed(() => instance.value.game_version),
currentLoaderVersion: computed(() => instance.value.loader_version ?? ''),
availablePlatforms: loaders?.value?.map((x) => x.name) ?? [],
availablePlatforms: computed(() => loadersQuery.data.value?.map((x) => x.name) ?? []),
resolveGameVersions(loader, showSnapshots) {
const versions = all_game_versions?.value ?? []
const versions = gameVersionsQuery.data.value ?? []
const filtered = versions.filter((item) => {
if (loader === 'vanilla') return true
const manifest = getManifest(loader)
return !!manifest?.value?.gameVersions?.some((x) => item.version === x.id)
return !!manifest?.gameVersions?.some((x) => item.version === x.id)
})
const result = (
showSnapshots ? filtered : filtered.filter((x) => x.version_type === 'release')
@@ -213,12 +220,12 @@ provideInstallationSettings({
return []
}
const manifest = getManifest(loader)
if (!manifest?.value) {
if (!manifest) {
debug('resolveLoaderVersions: no manifest', { loader, gameVersion })
return []
}
if (loader === 'fabric' || loader === 'quilt') {
const result = manifest.value.gameVersions[0]?.loaders ?? []
const result = manifest.gameVersions[0]?.loaders ?? []
debug('resolveLoaderVersions: fabric/quilt result', {
loader,
gameVersion,
@@ -226,14 +233,13 @@ provideInstallationSettings({
})
return result
}
const result =
manifest.value.gameVersions?.find((item) => item.id === gameVersion)?.loaders ?? []
const result = manifest.gameVersions?.find((item) => item.id === gameVersion)?.loaders ?? []
debug('resolveLoaderVersions: result', { loader, gameVersion, count: result.length })
return result
},
resolveHasSnapshots(loader) {
const versions = all_game_versions?.value ?? []
const versions = gameVersionsQuery.data.value ?? []
if (loader === 'vanilla') {
const result = versions.some((x) => x.version_type !== 'release')
debug('resolveHasSnapshots: vanilla', { loader, result })
@@ -241,7 +247,7 @@ provideInstallationSettings({
}
const manifest = getManifest(loader)
const supported = versions.filter(
(item) => !!manifest?.value?.gameVersions?.some((x) => item.version === x.id),
(item) => !!manifest?.gameVersions?.some((x) => item.version === x.id),
)
const result = supported.some((x) => x.version_type !== 'release')
debug('resolveHasSnapshots:', {
@@ -14,7 +14,7 @@ import { hide_ads_window, show_ads_window } from '@/helpers/ads.js'
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
import { handleSevereError } from '@/store/error.js'
import { type MinecraftAuthError, minecraftAuthErrors } from './minecraft-auth-errors'
import { findMinecraftAuthError, type MinecraftAuthError } from './minecraft-auth-errors'
const modal = ref<InstanceType<typeof NewModal>>()
const rawError = ref<string>('')
@@ -26,7 +26,7 @@ const loadingSignIn = ref(false)
function show(errorVal: { message?: string }) {
rawError.value = errorVal?.message ?? String(errorVal)
matchedError.value = minecraftAuthErrors.find((e) => rawError.value.includes(e.errorCode)) ?? null
matchedError.value = findMinecraftAuthError(rawError.value)
debugCollapsed.value = true
hide_ads_window()
@@ -1,10 +1,93 @@
export interface MinecraftAuthError {
errorCode: string
errorCode?: string
errorMatchers?: string[]
matches?: (message: string) => boolean
whatHappened: string
stepsToFix: string[]
}
export const minecraftAuthErrors: MinecraftAuthError[] = [
{
errorMatchers: ['Failed to deserialize response to JSON during step RefreshOAuthToken:'],
whatHappened:
'Your saved Microsoft sign-in token has expired or was revoked, so Modrinth App cannot refresh your Minecraft session.',
stepsToFix: [
'Sign out of the affected Minecraft account in Modrinth App',
'Sign in to the account again',
'Once the new sign-in finishes, try launching Minecraft again',
],
},
{
errorMatchers: ['Failed to deserialize response to JSON during step SisuAuthenticate:'],
whatHappened:
'Xbox services rejected the first sign-in response. This is most often caused by your system clock or time zone being out of sync.',
stepsToFix: [
'Open your system date and time settings',
'Turn on automatic time zone and automatic time, if available',
'Use the sync option in your system settings to synchronize the clock',
'Restart Modrinth App',
'Try signing in again',
],
},
{
matches: (message) =>
message.includes('Failed to deserialize response to JSON during step MinecraftToken:') &&
message.includes('429 Too Many Requests'),
whatHappened:
'Microsoft or Minecraft temporarily blocked the sign-in request because there were too many recent attempts.',
stepsToFix: [
'Wait about an hour before trying again',
'Restart Modrinth App after waiting',
'Try signing in once more',
'If the same message appears, wait longer before retrying so the temporary limit can clear',
],
},
{
matches: (message) =>
message.includes('Failed to deserialize response to JSON during step MinecraftToken:') &&
/Status Code: 5\d\d/.test(message),
whatHappened:
"Minecraft's authentication service is returning a server error, so Modrinth App cannot finish signing you in right now.",
stepsToFix: [
'Wait a few minutes and try signing in again',
'Check <a href="https://support.xbox.com/xbox-live-status">Xbox Status</a> for current service issues',
'Try signing in with the <a href="https://www.minecraft.net/en-us/download">official Minecraft Launcher</a> to confirm whether Minecraft sign-in is also affected there',
'If the service is healthy and this keeps happening, contact support with the debug information below',
],
},
{
errorMatchers: ['Failed to fetch player profile'],
whatHappened:
'Minecraft services could not return a Java Edition profile for this account. This most often happens when the game was purchased recently, the Java profile has not finished being created, or the wrong Microsoft account is being used.',
stepsToFix: [
'Sign in with the <a href="https://www.minecraft.net/en-us/download">official Minecraft Launcher</a>',
'Launch Minecraft: Java Edition once from the official launcher',
'Wait up to an hour if the purchase or profile setup was recent',
'Make sure you are using the Microsoft account that owns Minecraft. See <a href="https://support.modrinth.com/en/articles/9409136-finding-the-right-xbox-account">Finding the right Xbox account</a> for help',
'Try signing in to Modrinth App again',
],
},
{
matches: (message) =>
message.includes('error sending request for url (') &&
[
'minecraft.net',
'minecraftservices.com',
'mojang.com',
'xbox.com',
'xboxlive.com',
'live.com',
].some((domain) => message.includes(domain)),
whatHappened:
'Modrinth App could not connect to a Microsoft, Xbox, or Minecraft service needed for sign-in. This is usually caused by a local network, DNS, proxy, firewall, hosts file, VPN, or antivirus issue.',
stepsToFix: [
'Restart Modrinth App and try signing in again',
'Check that your internet connection is working',
'Allow Modrinth App through your firewall, antivirus, proxy, VPN, and hosts file rules',
'Try a different network or temporarily disable VPN/proxy software if you use one',
'If routing or DNS is the issue, a service like Cloudflare WARP can sometimes help',
],
},
{
errorCode: '2148916222',
whatHappened:
@@ -87,4 +170,31 @@ export const minecraftAuthErrors: MinecraftAuthError[] = [
'Once finished, try signing in again',
],
},
{
errorMatchers: ['Failed to deserialize response to JSON during step XstsAuthorize:'],
whatHappened:
'Xbox services rejected the request to authorize this account for Minecraft services, but did not return a specific account restriction that Modrinth App recognizes.',
stepsToFix: [
'Sign in with the <a href="https://www.minecraft.net/en-us/download">official Minecraft Launcher</a>',
'Complete any prompts shown by Microsoft, Xbox, or Minecraft',
'Try signing in to Modrinth App again',
'If the official launcher also fails, follow the error shown there or contact Xbox Support',
],
},
]
export function findMinecraftAuthError(message: string): MinecraftAuthError | null {
return (
minecraftAuthErrors.find((error) => {
if (error.errorCode && message.includes(error.errorCode)) {
return true
}
if (error.errorMatchers?.some((matcher) => message.includes(matcher))) {
return true
}
return error.matches?.(message) ?? false
}) ?? null
)
}
@@ -16,7 +16,8 @@ import {
type TabbedModalTab,
useVIntl,
} from '@modrinth/ui'
import { useQueryClient } from '@tanstack/vue-query'
import type { PlatformTag } from '@modrinth/utils'
import { useQuery } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import { computed, nextTick, ref, watch } from 'vue'
@@ -26,7 +27,9 @@ import InstallationSettings from '@/components/ui/instance_settings/Installation
import JavaSettings from '@/components/ui/instance_settings/JavaSettings.vue'
import WindowSettings from '@/components/ui/instance_settings/WindowSettings.vue'
import { get_project_v3 } from '@/helpers/cache'
import { get_loader_versions } from '@/helpers/metadata'
import { get_linked_modpack_info } from '@/helpers/profile'
import { get_game_versions, get_loaders } from '@/helpers/tags'
import { provideInstanceSettings } from '@/providers/instance-settings'
import type { GameInstance } from '../../../helpers/types'
@@ -45,7 +48,6 @@ const isMinecraftServer = ref(false)
const handleUnlinked = () => emit('unlinked')
const instanceRef = computed(() => props.instance)
const queryClient = useQueryClient()
const tabbedModal = ref<InstanceType<typeof TabbedModal> | null>(null)
function hide() {
@@ -120,13 +122,46 @@ const tabs = computed<TabbedModalTab[]>(() => [
},
])
function getSupportedModpackLoaders() {
return get_loaders().then((value: PlatformTag[]) =>
value
.filter((item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla')
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
}
// Preload
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'fabric'],
queryFn: () => get_loader_versions('fabric'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'forge'],
queryFn: () => get_loader_versions('forge'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'quilt'],
queryFn: () => get_loader_versions('quilt'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'neo'],
queryFn: () => get_loader_versions('neo'),
})
useQuery({
queryKey: ['instance-settings', 'game-versions'],
queryFn: get_game_versions,
})
useQuery({
queryKey: ['instance-settings', 'loaders', 'modpack'],
queryFn: getSupportedModpackLoaders,
})
useQuery({
queryKey: computed(() => ['linkedModpackInfo', props.instance.path]),
queryFn: () => get_linked_modpack_info(props.instance.path, 'stale_while_revalidate'),
enabled: computed(() => !!props.instance.linked_data?.project_id && !props.offline),
})
function show(tabIndex?: number) {
if (props.instance.linked_data?.project_id) {
queryClient.prefetchQuery({
queryKey: ['linkedModpackInfo', props.instance.path],
queryFn: () => get_linked_modpack_info(props.instance.path, 'stale_while_revalidate'),
})
}
tabbedModal.value?.show()
if (tabIndex !== undefined) {
nextTick(() => tabbedModal.value?.setTab(tabIndex))
@@ -148,7 +148,6 @@ import { arrayBufferToBase64 } from '@modrinth/utils'
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
import {
add_and_equip_custom_skin,
type Cape,
determineModelType,
equip_skin,
@@ -440,9 +439,22 @@ async function save() {
const bytes: Uint8Array = new Uint8Array(await (await fetch(textureUrl)).arrayBuffer())
if (mode.value === 'new') {
const addedSkin = await add_and_equip_custom_skin(bytes, variant.value, selectedCape.value)
const addedSkin = await save_custom_skin(
{
texture_key: '',
variant: variant.value,
cape_id: selectedCape.value?.id,
texture: textureUrl,
source: 'custom',
is_equipped: false,
},
bytes,
variant.value,
selectedCape.value,
true,
)
emit('saved', {
applied: true,
applied: false,
skin: addedSkin,
})
} else {
@@ -13,6 +13,7 @@ import {
import { useElementSize, useWindowSize } from '@vueuse/core'
import { Tooltip } from 'floating-vue'
import { computed, nextTick, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import Draggable from 'vuedraggable'
import type { RenderResult } from '@/helpers/rendering/batch-skin-renderer.ts'
import type { Skin } from '@/helpers/skins.ts'
@@ -82,12 +83,14 @@ const props = defineProps<{
isSkinSelected: (skin: Skin) => boolean
isSkinActive: (skin: Skin) => boolean
isAddSkinButtonDragActive: boolean
readOnly?: boolean
}>()
const emit = defineEmits<{
select: [skin: Skin]
edit: [skin: Skin, event: MouseEvent]
delete: [skin: Skin]
'reorder-saved-skins': [skins: Skin[]]
'add-skin': []
'add-skin-dragenter': [event: DragEvent]
'add-skin-dragover': [event: DragEvent]
@@ -153,6 +156,11 @@ const sections = computed<SkinSection[]>(() => [
})),
])
const draggableSavedSkins = ref<Skin[]>([])
const isDraggingSavedSkin = ref(false)
const canReorderSavedSkins = computed(() => draggableSavedSkins.value.length > 1)
const fixedSavedSkins = computed(() => props.savedSkins.filter((skin) => !canDragSavedSkin(skin)))
const sectionLayouts = computed(() => {
const layouts: Array<{ section: SkinSection; top: number; height: number; index: number }> = []
let top = 0
@@ -209,6 +217,18 @@ watch(
{ immediate: true },
)
watch(
() => props.savedSkins,
(nextSkins) => {
if (isDraggingSavedSkin.value) {
return
}
draggableSavedSkins.value = nextSkins.filter(canDragSavedSkin)
},
{ immediate: true },
)
watch(
listWidth,
(width) => {
@@ -257,6 +277,40 @@ function skinKey(skin: Skin, prefix: string) {
return `${prefix}-${skin.source}-${skin.texture_key}-${skin.variant}-${skin.cape_id ?? 'no-cape'}`
}
function savedSkinKey(skin: Skin) {
return skinKey(skin, 'saved-skin')
}
function canDragSavedSkin(skin: Skin) {
return skin.source === 'custom' || skin.source === 'custom_external'
}
function doSkinOrdersMatch(firstSkins: Skin[], secondSkins: Skin[]) {
const draggableSecondSkins = secondSkins.filter(canDragSavedSkin)
return (
firstSkins.length === draggableSecondSkins.length &&
firstSkins.every(
(skin, index) => savedSkinKey(skin) === savedSkinKey(draggableSecondSkins[index]),
)
)
}
function onSavedSkinDragStart() {
isDraggingSavedSkin.value = true
}
function onSavedSkinDragEnd() {
isDraggingSavedSkin.value = false
if (doSkinOrdersMatch(draggableSavedSkins.value, props.savedSkins)) {
draggableSavedSkins.value = props.savedSkins.filter(canDragSavedSkin)
return
}
emit('reorder-saved-skins', [...draggableSavedSkins.value])
}
function isSectionOpen(key: string) {
return openSectionKeys.value.has(key)
}
@@ -354,36 +408,140 @@ defineExpose({ getAddSkinButtonElement })
</Tooltip>
</template>
<div
<Draggable
v-if="section.kind === 'saved'"
:list="draggableSavedSkins"
class="grid w-full grid-cols-3 gap-3 min-[1300px]:grid-cols-4 min-[1750px]:grid-cols-5 min-[2050px]:grid-cols-6"
:item-key="savedSkinKey"
:disabled="readOnly || !canReorderSavedSkins"
:animation="250"
:swap-threshold="1"
:invert-swap="false"
:force-fallback="true"
:fallback-on-body="true"
:fallback-tolerance="4"
ghost-class="skin-reorder-ghost"
chosen-class="skin-reorder-chosen"
drag-class="skin-reorder-drag"
fallback-class="skin-reorder-fallback"
@start="onSavedSkinDragStart"
@end="onSavedSkinDragEnd"
>
<template #header>
<SkinLikeTextButton
ref="addSkinButton"
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
dropzone
:disabled="readOnly"
:drag-active="!readOnly && isAddSkinButtonDragActive"
@click="emit('add-skin')"
@dragenter="emit('add-skin-dragenter', $event)"
@dragover="emit('add-skin-dragover', $event)"
@dragleave="emit('add-skin-dragleave', $event)"
@drop="emit('add-skin-drop', $event)"
>
<template #icon>
<PlusIcon class="size-8" />
</template>
{{ formatMessage(messages.addSkinButton) }}
<template #subtitle>{{ formatMessage(messages.dragAndDropSubtitle) }}</template>
</SkinLikeTextButton>
</template>
<template #item="{ element: skin }">
<div
:key="savedSkinKey(skin)"
class="relative aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
>
<SkinButton
class="h-full w-full min-w-0 box-border rounded-[20px]"
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
:selected="isSkinSelected(skin)"
:active="isSkinActive(skin)"
:disabled="readOnly"
:is-dragging="isDraggingSavedSkin"
@select="emit('select', skin)"
>
<template v-if="!readOnly" #overlay-buttons>
<ButtonStyled color="brand">
<button
:aria-label="formatMessage(messages.editSkinButton)"
class="pointer-events-auto"
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
>
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
<button
v-tooltip="formatMessage(messages.deleteSkinButton)"
:aria-label="formatMessage(messages.deleteSkinButton)"
class="!rounded-[100%] pointer-events-auto"
@click.stop="emit('delete', skin)"
>
<TrashIcon />
</button>
</ButtonStyled>
</template>
</SkinButton>
</div>
</template>
<template #footer>
<div
v-for="skin in fixedSavedSkins"
:key="savedSkinKey(skin)"
class="relative aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
>
<SkinButton
class="h-full w-full min-w-0 box-border rounded-[20px]"
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
:selected="isSkinSelected(skin)"
:active="isSkinActive(skin)"
:disabled="readOnly"
:is-dragging="isDraggingSavedSkin"
@select="emit('select', skin)"
>
<template v-if="!readOnly" #overlay-buttons>
<ButtonStyled color="brand">
<button
:aria-label="formatMessage(messages.editSkinButton)"
class="pointer-events-auto"
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
>
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
<button
v-tooltip="formatMessage(messages.deleteSkinButton)"
:aria-label="formatMessage(messages.deleteSkinButton)"
class="!rounded-[100%] pointer-events-auto"
@click.stop="emit('delete', skin)"
>
<TrashIcon />
</button>
</ButtonStyled>
</template>
</SkinButton>
</div>
</template>
</Draggable>
<div
v-else
class="grid w-full grid-cols-3 gap-3 min-[1300px]:grid-cols-4 min-[1750px]:grid-cols-5 min-[2050px]:grid-cols-6"
>
<SkinLikeTextButton
ref="addSkinButton"
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
dropzone
:drag-active="isAddSkinButtonDragActive"
@click="emit('add-skin')"
@dragenter="emit('add-skin-dragenter', $event)"
@dragover="emit('add-skin-dragover', $event)"
@dragleave="emit('add-skin-dragleave', $event)"
@drop="emit('add-skin-drop', $event)"
>
<template #icon>
<PlusIcon class="size-8" />
</template>
{{ formatMessage(messages.addSkinButton) }}
<template #subtitle>{{ formatMessage(messages.dragAndDropSubtitle) }}</template>
</SkinLikeTextButton>
<SkinButton
v-for="skin in section.skins"
:key="skinKey(skin, 'saved-skin')"
:key="skinKey(skin, section.key)"
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
:backward-image-src="getBakedSkinTextures(skin)?.backwards"
:selected="isSkinSelected(skin)"
:active="isSkinActive(skin)"
:tooltip="skin.name"
:disabled="readOnly"
:is-dragging="isDraggingSavedSkin"
@select="emit('select', skin)"
>
<template #overlay-buttons>
@@ -396,37 +554,25 @@ defineExpose({ getAddSkinButtonElement })
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
<button
v-tooltip="formatMessage(messages.deleteSkinButton)"
:aria-label="formatMessage(messages.deleteSkinButton)"
class="!rounded-[100%] pointer-events-auto"
@click.stop="emit('delete', skin)"
>
<TrashIcon />
</button>
</ButtonStyled>
</template>
</SkinButton>
</div>
<div
v-else
class="grid w-full grid-cols-3 gap-3 min-[1300px]:grid-cols-4 min-[1750px]:grid-cols-5 min-[2050px]:grid-cols-6"
>
<SkinButton
v-for="skin in section.skins"
:key="skinKey(skin, section.key)"
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
:backward-image-src="getBakedSkinTextures(skin)?.backwards"
:selected="isSkinSelected(skin)"
:active="isSkinActive(skin)"
:tooltip="skin.name"
@select="emit('select', skin)"
/>
</div>
</Accordion>
</div>
</div>
</template>
<style scoped>
:global(.skin-reorder-ghost) {
opacity: 0.35;
}
:global(.skin-reorder-drag) {
cursor: grabbing;
}
:global(.skin-reorder-fallback) {
opacity: 0.9;
pointer-events: none;
}
</style>
+2
View File
@@ -197,11 +197,13 @@ export async function add_project_from_version(
path: string,
versionId: string,
reason: DownloadReason,
dependentOnVersionId?: string,
): Promise<string> {
return await invoke('plugin:profile|profile_add_project_from_version', {
path,
versionId,
reason,
dependentOnVersionId,
})
}
@@ -15,12 +15,10 @@ import { skinPreviewStorage } from '../storage/skin-preview-storage'
export interface RenderResult {
forwards: string
backwards: string
}
export interface RawRenderResult {
forwards: Blob
backwards: Blob
}
class BatchSkinRenderer {
@@ -92,12 +90,9 @@ class BatchSkinRenderer {
}
const frontCameraPos: [number, number, number] = [-1.3, 1, 6.3]
const backCameraPos: [number, number, number] = [-1.3, 1, -2.5]
const forwards = await this.renderView(frontCameraPos, lookAtTarget)
const backwards = await this.renderView(backCameraPos, lookAtTarget)
return { forwards, backwards }
return { forwards }
}
private async renderView(
@@ -404,16 +399,15 @@ async function generateSkinPreviewsForGeneration(
const headKey = headKeys[i]
const rawCached = cachedSkinPreviews[skinKey]
if (rawCached) {
if (rawCached && !skinBlobUrlMap.has(skinKey)) {
const cached: RenderResult = {
forwards: URL.createObjectURL(rawCached.forwards),
backwards: URL.createObjectURL(rawCached.backwards),
}
skinBlobUrlMap.set(skinKey, cached)
}
const cachedHead = cachedHeadPreviews[headKey]
if (cachedHead) {
if (cachedHead && !headBlobUrlMap.has(headKey)) {
headBlobUrlMap.set(headKey, URL.createObjectURL(cachedHead))
}
}
@@ -427,7 +421,6 @@ async function generateSkinPreviewsForGeneration(
if (DEBUG_MODE) {
const result = skinBlobUrlMap.get(key)!
URL.revokeObjectURL(result.forwards)
URL.revokeObjectURL(result.backwards)
skinBlobUrlMap.delete(key)
} else continue
}
@@ -456,7 +449,6 @@ async function generateSkinPreviewsForGeneration(
const renderResult: RenderResult = {
forwards: URL.createObjectURL(rawRenderResult.forwards),
backwards: URL.createObjectURL(rawRenderResult.backwards),
}
skinBlobUrlMap.set(key, renderResult)
+6
View File
@@ -142,6 +142,12 @@ export async function remove_custom_skin(skin: Skin): Promise<void> {
})
}
export async function set_custom_skin_order(textureKeys: string[]): Promise<void> {
await invoke('plugin:minecraft-skins|set_custom_skin_order', {
textureKeys,
})
}
export async function save_custom_skin(
skin: Skin,
textureBlob: Uint8Array,
@@ -2,7 +2,6 @@ import type { RawRenderResult } from '../rendering/batch-skin-renderer'
interface StoredPreview {
forwards: Blob
backwards: Blob
timestamp: number
}
@@ -38,7 +37,6 @@ export class SkinPreviewStorage {
const storedPreview: StoredPreview = {
forwards: result.forwards,
backwards: result.backwards,
timestamp: Date.now(),
}
@@ -67,7 +65,7 @@ export class SkinPreviewStorage {
return
}
resolve({ forwards: result.forwards, backwards: result.backwards })
resolve({ forwards: result.forwards })
}
request.onerror = () => reject(request.error)
})
@@ -95,7 +93,7 @@ export class SkinPreviewStorage {
const result = request.result as StoredPreview | undefined
if (result) {
results[key] = { forwards: result.forwards, backwards: result.backwards }
results[key] = { forwards: result.forwards }
} else {
results[key] = null
}
@@ -173,7 +171,7 @@ export class SkinPreviewStorage {
const key = cursor.primaryKey as string
const value = cursor.value as StoredPreview
const entrySize = value.forwards.size + value.backwards.size
const entrySize = value.forwards.size
totalSize += entrySize
count++
+3
View File
@@ -14,6 +14,7 @@ export type GameInstance = {
groups: string[]
linked_data?: LinkedData
preferred_update_channel: ReleaseChannel
created: Date
modified: Date
@@ -46,6 +47,8 @@ type LinkedData = {
locked: boolean
}
type ReleaseChannel = 'release' | 'beta' | 'alpha'
export type InstanceLoader = 'vanilla' | 'forge' | 'fabric' | 'quilt' | 'neoforge'
type ContentFile = {
+134 -20
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "جاز تنزيل إصدار جافا {version}"
},
"app.action-bar.downloading-update": {
"message": "تحميل التحديث"
},
"app.action-bar.downloads": {
"message": "التنزيلات"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "النسخة ألرئيسية"
},
"app.action-bar.reload-to-update": {
"message": "أعد التشغيل للتحديث"
},
"app.action-bar.show-more-running-instances": {
"message": "عرض المزيد من النسخ قيد التشغيل"
},
"app.action-bar.stop-instance": {
"message": "إيقاف النسخة"
},
"app.action-bar.update": {
"message": "تحديث"
},
"app.action-bar.view-active-downloads": {
"message": "عرض التحميلات الفعالة"
},
@@ -35,12 +44,36 @@
"app.action-bar.view-logs": {
"message": "عرض التسجيل"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "يتيح العرض المتقدم لتأثيرات مثل التشويش الذي قد تسبب مشكلات في الأداء دون استخدام العرض المسرع بالأجهزة."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "العرض المتقدم"
},
"app.appearance-settings.color-theme.description": {
"message": "اختر لون سمتك المفضل من أجل تطبيق Modrinth App."
},
"app.appearance-settings.color-theme.title": {
"message": "لون السمة"
},
"app.appearance-settings.default-landing-page.description": {
"message": "غير الصفحة التي يفتح عليها المشغل."
},
"app.appearance-settings.default-landing-page.home": {
"message": "صفحة الرئيسية"
},
"app.appearance-settings.default-landing-page.library": {
"message": "مكتبة"
},
"app.appearance-settings.default-landing-page.title": {
"message": "الصفحة الرئيسة الافتراضية"
},
"app.appearance-settings.hide-nametag.description": {
"message": "ألغ تفعيل عرض الاسم فوق لاعبك في صفحة المظاهر."
},
"app.appearance-settings.hide-nametag.title": {
"message": "إخفاء عرض الاسم"
},
"app.auth-servers.unreachable.body": {
"message": "قد تكون خوادم مصادقة ماينكرافت معطلة حاليًا. تحقق من اتصالك بالإنترنت وحاول مرة أخرى لاحقًا."
},
@@ -59,15 +92,27 @@
"app.browse.already-added": {
"message": "مضاف فعلا"
},
"app.browse.back-to-instance": {
"message": "العودة للنموذج"
},
"app.browse.discover-content": {
"message": "استكشف محتوى"
},
"app.browse.discover-servers": {
"message": "استكشف خوادم"
},
"app.browse.hide-added-servers": {
"message": "إخفاء الخوادم المضافة مسبقًا"
},
"app.browse.server.installing": {
"message": "جاري التثبيت"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
"app.creation-modal.installing-modpack.title": {
"message": "تنزيل الحزم"
},
"app.export-modal.description-placeholder": {
"message": "أدخل وصف التعديل..."
},
@@ -83,6 +128,9 @@
"app.export-modal.modpack-name-placeholder": {
"message": "إسم حزمة التعديل"
},
"app.export-modal.select-files-label": {
"message": "أختيار الملفات التي سيتم تصديرها"
},
"app.export-modal.version-number-label": {
"message": "رقم الإصدار"
},
@@ -102,13 +150,13 @@
"message": "حذف النموذج"
},
"app.instance.modpack-already-installed.body": {
"message": "حُزْمَة التعديل هذه مثبته فعلًا في نموذج <bold>{instanceName}</bold>. هل انت متأكد بإرادة نسخه؟"
"message": "حُزْمَة التعديل مثبته مسبقًا فعلًا في نموذج <bold>{instanceName}</bold>. هل أنت متأكد بإرادة نسخه؟"
},
"app.instance.modpack-already-installed.create": {
"message": "إنشاء"
},
"app.instance.modpack-already-installed.header": {
"message": "حُزْمَة التعديل مثبتة بالفعل"
"message": "حُزْمَة التعديل مثبتة فعلًا"
},
"app.instance.modpack-already-installed.instance": {
"message": "النموذج"
@@ -126,7 +174,7 @@
"message": "تحقق من المشاريع التي أستخدمها في حزمة التعديل الخاص بي!"
},
"app.instance.mods.share-title": {
"message": "مشاركة محتوى حزمة التعديل"
"message": "مشاركة محتوى حُزْمَة التعديل"
},
"app.instance.mods.successfully-uploaded": {
"message": "تم الرفع بنجاح"
@@ -189,7 +237,7 @@
"message": "{count, plural, one {# تعديل} other {# تعديلات}}"
},
"app.modal.install-to-play.required-modpack": {
"message": زمة التعديل مطلوبة"
"message": ُزْمَة التعديل مطلوبة"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "يتطلب هذا الخادم تعديلات للعب. انقر فوق \"تثبيت\" لإعداد الملفات المطلوبة من Modrinth، ثم قم بتشغيله مباشرة إلى الخادم."
@@ -210,7 +258,13 @@
"message": "يلزم التحديث"
},
"app.modal.update-to-play.update-required-description": {
"message": "هناك تحديث مطلوب للعب بـ {name}. الرجاء التحديث إلى آخر اصدار لتشغيل اللعبة."
"message": "هناك تحديث مطلوب للعب بـ {name}. الرجاء التحديث إلى أحدث اصدار لتشغيل اللعبة."
},
"app.project.install-context.back-to-browse": {
"message": "العودة إلى الاستكشاف"
},
"app.project.install-context.install-content-to-instance": {
"message": "تثبيت المحتويات الى النموذج"
},
"app.settings.developer-mode-enabled": {
"message": "تم تفعيل وضع المطوّر."
@@ -236,8 +290,74 @@
"app.settings.tabs.resource-management": {
"message": "إدارة الموارد"
},
"app.update-popup.body": {
"message": "تطبيق Modrinth v{version} جاهز للتثبيت! أعد التحميل للتحديث الآن، أو تلقائيًا عند إغلاق تطبيق Modrinth."
"app.skins.add-button": {
"message": "إضافة مظهر"
},
"app.skins.add-button.drag-and-drop": {
"message": "أسحب ثم أفلت"
},
"app.skins.apply-button": {
"message": "تطبيق"
},
"app.skins.delete-button": {
"message": "حذف المظهر"
},
"app.skins.delete-modal.description": {
"message": "سيتم حذف المظهر بشكل نهائي، ولايمكن إرجاعه."
},
"app.skins.delete-modal.title": {
"message": "هل أنت متأكد من حذف المظهر هذا؟"
},
"app.skins.edit-button": {
"message": "تعديل المظهر"
},
"app.skins.modal.add-skin-button": {
"message": "إضافة المظهر"
},
"app.skins.modal.add-title": {
"message": "إضافة المظهر"
},
"app.skins.modal.cape-fallback-name": {
"message": "رداء"
},
"app.skins.modal.cape-section": {
"message": "رداء"
},
"app.skins.modal.edit-title": {
"message": "تعديل المظهر"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "عدل المظهر أولا للحفظ !"
},
"app.skins.modal.no-cape-tooltip": {
"message": "بدون رداء"
},
"app.skins.modal.none-cape-option": {
"message": "لا شيء"
},
"app.skins.modal.save-skin-button": {
"message": "حفظ المظهر"
},
"app.skins.modal.saving-tooltip": {
"message": "حفظ..."
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "أرفع المظهر أولا !"
},
"app.skins.preview.edit-button": {
"message": "تعديل المظهر"
},
"app.skins.previewing-badge": {
"message": "أستعراض"
},
"app.skins.rate-limit.title": {
"message": "تمهل !"
},
"app.skins.sign-in.title": {
"message": "الرجاء تسجيل الدخول"
},
"app.skins.title": {
"message": "اختيار المظهر"
},
"app.update-popup.body.download-complete": {
"message": "انتهى تنزيل تطبيق Modrinth v{version}. أعد التحميل للتحديث الآن، أو تلقائيًا عند إغلاق تطبيق Modrinth."
@@ -257,9 +377,6 @@
"app.update-popup.download-complete": {
"message": "اكتمل التنزيل"
},
"app.update-popup.reload": {
"message": "إعادة تحميل"
},
"app.update-popup.title": {
"message": "تحديث متاح"
},
@@ -269,17 +386,8 @@
"app.update.complete-toast.title": {
"message": "تم تثبيت الإصدار {version} بنجاح!"
},
"app.update.download-update": {
"message": "تنزيل التحديث"
},
"app.update.downloading-update": {
"message": "جار تنزيل التحديث ({percent}٪)"
},
"app.update.reload-to-update": {
"message": "أعد التحميل لتثبيت التحديث"
},
"app.world.server-modal.placeholder-address": {
"message": "مثال.مودرنث.جج"
"message": "مثال (example.modrinth.gg)"
},
"app.world.server-modal.select-an-option": {
"message": "حدد خيارا"
@@ -530,6 +638,9 @@
"instance.settings.tabs.java.java-memory": {
"message": "الذاكرة المخصَّصة"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/path/to/java"
},
"instance.settings.tabs.window": {
"message": "النافذة"
},
@@ -605,6 +716,9 @@
"minecraft-account.add-account": {
"message": "إضافة حساب"
},
"minecraft-account.label": {
"message": "حساب ماينكرافت"
},
"minecraft-account.select-account": {
"message": "اختيار الحساب"
},
+325 -118
View File
@@ -2,14 +2,17 @@
"app.action-bar.downloading-java": {
"message": "Stahování Javy {version}"
},
"app.action-bar.downloading-update": {
"message": "Stahování aktualizace"
},
"app.action-bar.downloads": {
"message": "Stažené Soubory"
"message": "Stahování"
},
"app.action-bar.hide-more-running-instances": {
"message": "Skrýt více spuštěných instancí"
},
"app.action-bar.make-primary-instance": {
"message": "Použít jako primární instanci"
"message": "Nastavit jako primární instanci"
},
"app.action-bar.no-instances-running": {
"message": "Žádné spuštěné instance"
@@ -20,11 +23,17 @@
"app.action-bar.primary-instance": {
"message": "Primární instance"
},
"app.action-bar.reload-to-update": {
"message": "Znovu načíst pro aktualizaci"
},
"app.action-bar.show-more-running-instances": {
"message": "Zobrazit další spuštěné instance"
},
"app.action-bar.stop-instance": {
"message": "Ukončit instanci"
"message": "Zastavit instanci"
},
"app.action-bar.update": {
"message": "Aktualizace"
},
"app.action-bar.view-active-downloads": {
"message": "Zobrazit probíhající stahování"
@@ -33,10 +42,10 @@
"message": "Zobrazit instanci"
},
"app.action-bar.view-logs": {
"message": "Zobrazit záznamy"
"message": "Zobrazit logy"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Umožňuje pokročilé vykreslování, jako jsou efekty rozostření, které mohou způsobit problémy s výkonem bez hardwarového vykreslení."
"message": "Povolí pokročilé vykreslování, například efekty rozmazání, které mohou bez hardwarového zrychlení způsobovat problémy s výkonem."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "Pokročilé vykreslování"
@@ -48,16 +57,16 @@
"message": "Barevný motiv"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Změňte stránku, na které se spouštěč otevře."
"message": "Změňte stránku, která se otevře po spuštění launcheru."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Domov"
"message": "Domů"
},
"app.appearance-settings.default-landing-page.library": {
"message": "Knihovna"
},
"app.appearance-settings.default-landing-page.title": {
"message": "Výchozí úvodní stránka"
"message": "Výchozí stránka po spuštění"
},
"app.appearance-settings.hide-nametag.description": {
"message": "Zakáže zobrazení jmenovky nad vaší postavou na stránce skinů."
@@ -66,28 +75,34 @@
"message": "Skrýt jmenovku"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "V sekci „Vrátit se do hry“ na úvodní stránce jsou uvedeny nejnovější světy."
"message": "Zahrne nedávné světy do sekce „Vrátit se do hry“ na domovské stránce."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "Skočit zpět do světů"
"message": "Rychlý návrat do světů"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimalizovat spouštěč při spuštění procesu Minecraftu."
"message": "Minimalizovat launcher při spuštění Minecraftu."
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimalizovat spouštěč"
"message": "Minimalizovat launcher"
},
"app.appearance-settings.native-decorations.description": {
"message": "Použít systémový rámeček okna (vyžaduje restart aplikace)."
},
"app.appearance-settings.native-decorations.title": {
"message": "Nativní dekorace"
"message": "Systémové dekorace"
},
"app.appearance-settings.select-option": {
"message": "Vyberte možnost"
},
"app.appearance-settings.show-play-time.description": {
"message": "Zobrazuje, kolik času jsi strávil hraním instance."
},
"app.appearance-settings.show-play-time.title": {
"message": "Zobrazit odehraný čas"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Umožňí přepínat zobrazení postranního panelu."
"message": "Umožní přepínat zobrazení postranního panelu."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Přepnout postranní panel"
@@ -99,19 +114,19 @@
"message": "Upozorni mě před instalací neznámých modpacků"
},
"app.auth-servers.unreachable.body": {
"message": "Autořizační servery Minecraftu aktuálně nejsou k dispozici. Zkontrolujte si své připojení k internetu a zkuste to znovu později."
"message": "Ověřovací Minecraft servery mohou být právě teď nedostupné. Zkontroluj si své připojení k internetu a zkus to znovu později."
},
"app.auth-servers.unreachable.header": {
"message": "Připojení k autorizačním serverům se nezdařilo"
"message": "Nelze se připojit k ověřovacím serverům"
},
"app.browse.add-servers-to-instance": {
"message": "Přidat server do instance"
"message": "Přidání serveru do instance"
},
"app.browse.add-to-an-instance": {
"message": "Přidat do instalace"
"message": "Přidat do instance"
},
"app.browse.add-to-instance": {
"message": "Přidat do instalace"
"message": "Přidat do instance"
},
"app.browse.add-to-instance-name": {
"message": "Přidat do {instanceName}"
@@ -120,22 +135,22 @@
"message": "Přidáno"
},
"app.browse.already-added": {
"message": "Už přidáno"
"message": "Již přidáno"
},
"app.browse.back-to-instance": {
"message": "Zpět k instanci"
},
"app.browse.discover-content": {
"message": "Prozkoumat obsah"
"message": "Objevovat obsah"
},
"app.browse.discover-servers": {
"message": "Prozkoumat servery"
"message": "Objevovat servery"
},
"app.browse.hide-added-servers": {
"message": "Skrýt již přidané servery"
},
"app.browse.project-type.modpacks": {
"message": "Balíčky Modifikací"
"message": "Modpacky"
},
"app.browse.server-instance-content-warning": {
"message": "Přidání obsahu může narušit kompatibilitu při připojení k serveru. Veškerý přidaný obsah se navíc ztratí při aktualizaci obsahu instance serveru."
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Instalování"
},
"app.content-install.no-compatible-versions": {
"message": "Žádná dostupná verze se neshoduje s verzí {compatibilityLabel}. Vyber verzi, abys ji i přesto nainstaloval/a. Závislosti nebudou automaticky nainstalovány."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -150,7 +168,7 @@
"message": "Probíhá instalace modpacku..."
},
"app.export-modal.description-placeholder": {
"message": "Přidej popis modpacku..."
"message": "Zadej popis modpacku..."
},
"app.export-modal.export-button": {
"message": "Exportovat"
@@ -162,10 +180,10 @@
"message": "Zahrnout \"{file}\"?"
},
"app.export-modal.modpack-name-label": {
"message": "Jméno Modpacku"
"message": "Název modpacku"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Jméno modpacku"
"message": "Název modpacku"
},
"app.export-modal.select-files-label": {
"message": "Nastavte, které soubory mají být do tohoto exportu zahrnuty"
@@ -177,7 +195,7 @@
"message": "1.0.0"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Všechna data z instance budou navždy smazána, včetně světů, nastavení a všeho ostatního."
"message": "Všechna data tvé instance budou trvale smazána, včetně světů, konfigurací a veškerého nainstalovaného obsahu."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Tato akce nemůže být vrácena"
@@ -186,19 +204,19 @@
"message": "Odstranit instanci"
},
"app.instance.confirm-delete.header": {
"message": "Odstanit instanci"
"message": "Odstranění instance"
},
"app.instance.modpack-already-installed.body": {
"message": "Tento modpack je už nainstalovaný v instanci <bold>{instanceName}</bold>. Opravdu ho chceš duplikovat?"
"message": "Tento modpack je již nainstalovaný v instanci <bold>{instanceName}</bold>. Opravdu ho chceš duplikovat?"
},
"app.instance.modpack-already-installed.create": {
"message": "Vytvořit"
},
"app.instance.modpack-already-installed.header": {
"message": "Modpack už je nainstalovaný"
"message": "Modpack je již nainstalován"
},
"app.instance.modpack-already-installed.instance": {
"message": "Instalace"
"message": "Instance"
},
"app.instance.mods.content-type-project": {
"message": "projekt"
@@ -210,7 +228,7 @@
"message": "{count} projektů bylo přidáno"
},
"app.instance.mods.share-text": {
"message": "Podívejte se na projekty co používám ve svém modpacku!"
"message": "Podívejte se na projekty, které používám ve svém modpacku!"
},
"app.instance.mods.share-title": {
"message": "Sdílení obsahu modpacku"
@@ -224,11 +242,14 @@
"app.instance.worlds.browse-servers": {
"message": "Procházet servery"
},
"app.instance.worlds.delete-world-description": {
"message": "'{name}' bude **navždy smazán** a nebude možné jej obnovit."
},
"app.instance.worlds.delete-world-title": {
"message": "Opravdu si jsi jistý, že chceš navždy smazat tenhle svět?"
"message": "Jsi si jistý, že chceš trvale smazat tento svět?"
},
"app.instance.worlds.filter-modded": {
"message": "Módováno"
"message": "Módovaný"
},
"app.instance.worlds.filter-offline": {
"message": "Offline"
@@ -240,19 +261,22 @@
"message": "Vanilla"
},
"app.instance.worlds.no-worlds-description": {
"message": "Přidej nebo procházej servery"
"message": "Přidej server nebo začni procházet"
},
"app.instance.worlds.no-worlds-heading": {
"message": "Žádné servery nebo světy nebyly přidány"
},
"app.instance.worlds.remove-server-description": {
"message": "'{name}' bude odstraněn z tvého seznamu i ze hry a nebude možné jej obnovit."
},
"app.instance.worlds.remove-server-description-with-address": {
"message": "'{name}' ({address}) bude odstraněn z vašeho seznamu, a to i ve hře, a nebude možné jej obnovit."
"message": "'{name}' ({address}) bude odstraněn z tvého seznamu i ze hry a nebude možné jej obnovit."
},
"app.instance.worlds.remove-server-title": {
"message": "Opravdu chceš odstranit {name}?"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Hledat ve světech {count}..."
"message": "Hledat v {count} světech..."
},
"app.instance.worlds.this-server": {
"message": "tento server"
@@ -261,19 +285,19 @@
"message": "Požadovaný obsah"
},
"app.modal.install-to-play.header": {
"message": "Nainstaluj ke hraní"
"message": "Nainstaluj pro hraní"
},
"app.modal.install-to-play.install-button": {
"message": "Instalovat"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural,one {#mód} other {#módy}}"
"message": "{count, plural, one {# mód} few {# módy} other {# módů}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Povinný modpack"
"message": "Požadovaný modpack"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Tento server k hraní vyžaduje módy. Klikni na instalovat pro získání potřebných módů z Modrinth."
"message": "Tento server k hraní vyžaduje módy. Klikni na instalovat pro získání potřebných módů z Modrinth a poté se rovnou připoj na server."
},
"app.modal.install-to-play.shared-instance": {
"message": "Sdílená instance"
@@ -285,22 +309,22 @@
"message": "Zobrazit obsah"
},
"app.modal.update-to-play.header": {
"message": "Updatuj ke hraní"
"message": "Aktualizuj pro hraní"
},
"app.modal.update-to-play.update-required": {
"message": "Je vyžadována aktualizace"
},
"app.modal.update-to-play.update-required-description": {
"message": "Je nutná aktualizace ke hraní {name}. Prosím updatuj na nejnovější verzi k spuštění hry."
"message": "Pro hraní {name} je vyžadována aktualizace. Prosím aktualizuj na nejnovější verzi, abys mohl hru spustit."
},
"app.project.install-button.already-installed": {
"message": "Tento projekt je již nainstalován"
},
"app.project.install-context.back-to-browse": {
"message": "Zpět na Prozkoumat"
"message": "Zpět k Objevování"
},
"app.project.install-context.install-content-to-instance": {
"message": "Nainstalovat obsah do instnce"
"message": "Nainstalovat obsah do instance"
},
"app.settings.developer-mode-enabled": {
"message": "Vývojářský režim povolen."
@@ -326,14 +350,158 @@
"app.settings.tabs.resource-management": {
"message": "Správa zdrojů"
},
"app.update-popup.body": {
"message": "Aplikace Modrinth v{version} je připravena k instalaci! Nainstalujte aktualizaci nyní nebo automaticky po zavření aplikace Modrinth."
"app.skins.add-button": {
"message": "Přidat skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Přetáhni a pusť"
},
"app.skins.apply-button": {
"message": "Použít"
},
"app.skins.delete-button": {
"message": "Smazat skin"
},
"app.skins.delete-modal.description": {
"message": "Tahle akce navždy smaže vybraný skin a nejde vrátit zpět."
},
"app.skins.delete-modal.title": {
"message": "Opravdu chceš smazat tenhle skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Nepodařilo se načíst přetažený soubor."
},
"app.skins.dropped-file-error.title": {
"message": "Chyba při načítání souboru"
},
"app.skins.edit-button": {
"message": "Upravit skin"
},
"app.skins.modal.add-skin-button": {
"message": "Přidat skin"
},
"app.skins.modal.add-title": {
"message": "Přidání skinu"
},
"app.skins.modal.arm-style-section": {
"message": "Typ paží"
},
"app.skins.modal.arm-style-slim": {
"message": "Štíhlý"
},
"app.skins.modal.arm-style-wide": {
"message": "Široký"
},
"app.skins.modal.cape-fallback-name": {
"message": "Plášť"
},
"app.skins.modal.cape-section": {
"message": "Plášť"
},
"app.skins.modal.edit-title": {
"message": "Úprava skinu"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Nejdříve uprav skin!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Bez pláště"
},
"app.skins.modal.none-cape-option": {
"message": "Žádný"
},
"app.skins.modal.replace-texture-button": {
"message": "Nahradit texturu"
},
"app.skins.modal.save-skin-button": {
"message": "Uložit skin"
},
"app.skins.modal.saving-tooltip": {
"message": "Ukládání..."
},
"app.skins.modal.texture-section": {
"message": "Textura"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Nejdříve nahraj skin!"
},
"app.skins.preview.edit-button": {
"message": "Upravit skin"
},
"app.skins.previewing-badge": {
"message": "Náhled"
},
"app.skins.rate-limit.text": {
"message": "Měníš svůj skin příliš často. Servery Mojangu dočasně zablokovaly další požadavky. Počkej chvíli, než to zkusíš znovu."
},
"app.skins.rate-limit.title": {
"message": "Zpomal!"
},
"app.skins.reorder-error.text": {
"message": "Tvé pořadí skinů se nepodařilo uložit."
},
"app.skins.reorder-error.title": {
"message": "Nepodařilo se změnit pořadí skinů"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Výchozí skiny"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Tyto skiny jsi obdržel za přispění do sbírky Modrinth Pride behěm Pride měsíce."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Uložené skiny"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Přihlásit se"
},
"app.skins.sign-in.description": {
"message": "Přihlas se prosím ke svému Minecraft účtu, abys mohl/a využívat funkce pro správu skinů v aplikaci Modrinth."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Nadšený Modrinth Bot"
},
"app.skins.sign-in.title": {
"message": "Přihlas se prosím"
},
"app.skins.title": {
"message": "Výběr skinu"
},
"app.update-popup.body.download-complete": {
"message": "Stahování aplikace Modrinth v{version} bylo dokončeno. Nainstalujte aktualizaci nyní nebo automaticky po zavření aplikace Modrinth."
"message": "Stahování aplikace Modrinth v{version} bylo dokončeno. Aktualizujte ji znovunačtem nebo se automaticky aktualizuje po zavření."
},
"app.update-popup.body.linux": {
"message": "Aplikace Modrinth v{version} je k dispozici. Aktualizujte pomocí svého správce balíčků, abyste získali nejnovější funkce a opravy!"
"message": "Aplikace Modrinth v{version} je k dispozici. Aktualizujte ji pomocí svého správce balíčků, abyste získali nejnovější funkce a opravy!"
},
"app.update-popup.body.metered": {
"message": "Aplikace Modrinth v{version} je nyní k dispozici! Protože jste v měřené síti, nebyla stažena automaticky."
@@ -348,10 +516,10 @@
"message": "Stahování bylo dokončeno"
},
"app.update-popup.reload": {
"message": "Načíst znovu"
"message": "Znovu načíst pro aktualizaci"
},
"app.update-popup.title": {
"message": "Je k dispozici aktualizace"
"message": "Aktualizace k dispozici"
},
"app.update.complete-toast.text": {
"message": "Kliknutím sem zobrazíte seznam změn."
@@ -359,26 +527,17 @@
"app.update.complete-toast.title": {
"message": "Verze {version} byla úspěšně nainstalována!"
},
"app.update.download-update": {
"message": "Stáhnout aktualizaci"
},
"app.update.downloading-update": {
"message": "Stahování aktualizace ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Restartovat aplikaci pro nainstalování aktualizace"
},
"app.world.server-modal.placeholder-address": {
"message": "priklad.modrinth.gg"
},
"app.world.server-modal.select-an-option": {
"message": "Vyberte možnost"
"message": "Vybrat možnost"
},
"app.world.world-item.incompatible-version": {
"message": "Nekompatibilní verze {version}"
},
"app.world.world-item.not-played-yet": {
"message": "Ještě nehráno"
"message": "Zatím nehráno"
},
"app.world.world-item.offline": {
"message": "Offline"
@@ -390,25 +549,25 @@
"message": "Přidat přítele"
},
"friends.action.view-friend-requests": {
"message": "{count} přátelé {count, plural, one {request} other {requests}}"
"message": "{count} {count, plural, one {žádost} few {žádosti} other {žádostí}} o přátelství"
},
"friends.add-friend.submit": {
"message": "Poslat žádost o přátelství"
},
"friends.add-friend.title": {
"message": "Přidákamaráda"
"message": "Přidání přítele"
},
"friends.add-friend.username.description": {
"message": "Může být jiné než jejich Minecraft jméno!"
"message": "Může se lišit od jejich Minecraft přezdívky!"
},
"friends.add-friend.username.placeholder": {
"message": "Zadejte uživatelské jméno Modrinth..."
"message": "Zadej přezdívku na Modrinthu..."
},
"friends.add-friend.username.title": {
"message": "Jak se váš kamarád jmenuje na Modrinthu?"
"message": "Jaká je přezdívka tvého přítele na Modrinthu?"
},
"friends.add-friends-to-share": {
"message": "<link>Přidejte si přátelé</link> a podívejte se, co hrají!"
"message": "<link>Přidej si přátele</link>, abys viděl, co hrají!"
},
"friends.friend.cancel-request": {
"message": "Zrušit žádost"
@@ -423,7 +582,7 @@
"message": "Zobrazit profil"
},
"friends.heading": {
"message": "Kamarádi"
"message": "Přátelé"
},
"friends.heading.active": {
"message": "Aktivní"
@@ -437,6 +596,9 @@
"friends.heading.pending": {
"message": "Čeká na vyřízení"
},
"friends.no-friends-match": {
"message": "Žádní přátelé neodpovídají ''{query}''"
},
"friends.search-friends-placeholder": {
"message": "Hledat přátele..."
},
@@ -444,7 +606,7 @@
"message": "{title} - {count}"
},
"friends.sign-in-to-add-friends": {
"message": "<link>Přihlašte se ke svému Modrinth účtu</link>, abyste jsi přidali přátele a uviděli co hrají!"
"message": "<link>Přihlas se k Modrinth účtu</link>, abys mohl přidávat přátele a vidět, co hrají!"
},
"instance.add-server.add-and-play": {
"message": "Přidat a hrát"
@@ -456,10 +618,10 @@
"message": "Vypnuto"
},
"instance.add-server.resource-pack.enabled": {
"message": "Zapnuto"
"message": "Povolený"
},
"instance.add-server.resource-pack.prompt": {
"message": "Ptát se"
"message": "Zeptat se"
},
"instance.add-server.title": {
"message": "Přidat server"
@@ -468,7 +630,7 @@
"message": "Upravit server"
},
"instance.edit-world.hide-from-home": {
"message": "Skrýt z domovské obrazovky"
"message": "Skrýt z domovské stránky"
},
"instance.edit-world.name": {
"message": "Název"
@@ -477,7 +639,7 @@
"message": "Minecraft svět"
},
"instance.edit-world.reset-icon": {
"message": "Resetovat ikonu"
"message": "Obnovit ikonu"
},
"instance.edit-world.title": {
"message": "Upravit svět"
@@ -495,22 +657,22 @@
"message": "Název"
},
"instance.server-modal.placeholder-name": {
"message": "Minecraft Server"
"message": "Minecraft server"
},
"instance.server-modal.resource-pack": {
"message": "Balíčky textur"
"message": "Balíček textur"
},
"instance.settings.tabs.general": {
"message": "Obecné"
},
"instance.settings.tabs.general.delete": {
"message": "Odstranit instalaci"
"message": "Odstranění instance"
},
"instance.settings.tabs.general.delete.button": {
"message": "Odstranit instalaci"
"message": "Odstranit instanci"
},
"instance.settings.tabs.general.delete.description": {
"message": "Trvale smaže instalaci z vašeho zařízení, včetně vašich světů, konfigurací a veškerého nainstalovaného obsahu. Buďte opatrní, protože jakmile instalaci smažete, nebude možné ji obnovit."
"message": "Trvale smaže instanci z tvého zařízení, včetně světů, konfigurací a veškerého nainstalovaného obsahu. Buď opatrný, jakmile instanci smažeš, nepůjde ji obnovit."
},
"instance.settings.tabs.general.deleting.button": {
"message": "Mazání..."
@@ -519,40 +681,64 @@
"message": "Duplikovat"
},
"instance.settings.tabs.general.duplicate-button.tooltip.installing": {
"message": "Nelze duplikovat při instalaci."
"message": "Nelze duplikovat během instalace."
},
"instance.settings.tabs.general.duplicate-instance": {
"message": "Duplikovat instalaci"
"message": "Duplikace instance"
},
"instance.settings.tabs.general.duplicate-instance.description": {
"message": "Vytvoří kopii této instalace, včetně světů, konfigurací, modů atd."
"message": "Vytvoří kopii této instance, včetně světů, konfigurací, modů, atd."
},
"instance.settings.tabs.general.edit-icon": {
"message": "Upravit ikonu"
},
"instance.settings.tabs.general.edit-icon.remove": {
"message": "Odstranit ikonu"
"message": "Odebrat ikonu"
},
"instance.settings.tabs.general.edit-icon.replace": {
"message": "Změnit ikonu"
"message": "Nahradit ikonu"
},
"instance.settings.tabs.general.edit-icon.select": {
"message": "Vybrat ikonu"
},
"instance.settings.tabs.general.library-groups": {
"message": "Skupina knihoven"
"message": "Skupiny knihovny"
},
"instance.settings.tabs.general.library-groups.create": {
"message": "Vytvořit novou skupinu"
},
"instance.settings.tabs.general.library-groups.description": {
"message": "Skupiny knihoven vám umožňují uspořádat instalace do různých sekcí."
"message": "Skupiny knihovny ti umožňují uspořádat instance do různých sekcí v knihovně."
},
"instance.settings.tabs.general.library-groups.enter-name": {
"message": "Zadejte název skupiny"
"message": "Zadej název skupiny"
},
"instance.settings.tabs.general.name": {
"message": "Název"
"message": "Jméno"
},
"instance.settings.tabs.general.update-channel": {
"message": "Aktualizační kanál"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Vydané, beta a alfa verze se zobrazí jako dostupné aktualizace."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Vydané a beta verze se zobrazí jako dostupné aktualizace."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Vydání"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Pouze vydané verze se zobrazí jako dostupné aktualizace."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Vyber aktualizační kanál"
},
"instance.settings.tabs.hooks": {
"message": "Spouštěcí hooky"
@@ -561,28 +747,28 @@
"message": "Vlastní spouštěcí hooky"
},
"instance.settings.tabs.hooks.description": {
"message": "Hooky umožňují pokročilým uživatelům spouštět určité systémové příkazy před a po spuštění hry."
"message": "Hooky umožňují pokročilým uživatelům spustit určité systémové příkazy před a po spuštění hry."
},
"instance.settings.tabs.hooks.post-exit": {
"message": "Po ukončení"
},
"instance.settings.tabs.hooks.post-exit.description": {
"message": "Spušteno po vypnutí hry."
"message": "Provede se po ukončení hry."
},
"instance.settings.tabs.hooks.post-exit.enter": {
"message": "Zadejte příkaz k provedení po ukončení..."
"message": "Zadej příkaz k provedení po ukončení..."
},
"instance.settings.tabs.hooks.pre-launch": {
"message": "Před spuštěním"
},
"instance.settings.tabs.hooks.pre-launch.description": {
"message": "Spouštějí se před spuštěním instalace."
"message": "Provede se před spuštěním instance."
},
"instance.settings.tabs.hooks.pre-launch.enter": {
"message": "Zadejte příkaz k provedení před spuštěním..."
"message": "Zadej příkaz k provedení před spuštěním..."
},
"instance.settings.tabs.hooks.title": {
"message": "Spouštěcí hooky hry"
"message": "Spouštěcí příkazy hry"
},
"instance.settings.tabs.hooks.wrapper": {
"message": "Wrapper"
@@ -591,7 +777,7 @@
"message": "Příkaz wrapperu pro spuštění Minecraftu."
},
"instance.settings.tabs.hooks.wrapper.enter": {
"message": "Zadejte příkaz wrapperu..."
"message": "Zadej příkaz wrapperu..."
},
"instance.settings.tabs.installation": {
"message": "Instalace"
@@ -602,6 +788,24 @@
"instance.settings.tabs.java": {
"message": "Java a pamět"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Vlastní proměnná prostředí"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Vlastní argumenty Javy"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Vlastní instalace Javy"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Vlastní nastavení paměti"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Zadej proměnná prostředí..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Zadej argumenty Javy..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "Proměnné prostředí"
},
@@ -609,13 +813,16 @@
"message": "Hooky"
},
"instance.settings.tabs.java.java-arguments": {
"message": "Java argumenty"
"message": "Argumenty Javy"
},
"instance.settings.tabs.java.java-installation": {
"message": "Instalace Javy"
},
"instance.settings.tabs.java.java-memory": {
"message": "Paměti přiřazeno"
"message": "Přiřazeno paměti"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/path/to/java"
},
"instance.settings.tabs.window": {
"message": "Okno"
@@ -624,10 +831,10 @@
"message": "Vlastní nastavení okna"
},
"instance.settings.tabs.window.fullscreen": {
"message": "Celá obrazovka"
"message": "Režim celé obrazovky"
},
"instance.settings.tabs.window.fullscreen.description": {
"message": "Spustit hru v plné obrazovce pokud spuštěno (použitím options.txt)."
"message": "Při spuštění otevřít hru v režimu celé obrazovky (použitím options.txt)."
},
"instance.settings.tabs.window.height": {
"message": "Výška"
@@ -636,7 +843,7 @@
"message": "Výška herního okna při spuštění."
},
"instance.settings.tabs.window.height.enter": {
"message": "Zadejte výšku..."
"message": "Zadej výšku..."
},
"instance.settings.tabs.window.width": {
"message": "Šířka"
@@ -645,19 +852,19 @@
"message": "Šířka herního okna při spuštění."
},
"instance.settings.tabs.window.width.enter": {
"message": "Zadejte šířku..."
"message": "Zadej šířku..."
},
"instance.worlds.a_minecraft_server": {
"message": "Minecraft Server"
"message": "Minecraft server"
},
"instance.worlds.cant_connect": {
"message": "Nelze připojit k serveru"
"message": "Nelze se připojit k serveru"
},
"instance.worlds.copy_address": {
"message": "Kopírovat adresu"
},
"instance.worlds.dont_show_on_home": {
"message": "Nezobrazuje na domovské stránce"
"message": "Nezobrazovat na domovské stránce"
},
"instance.worlds.game_already_open": {
"message": "Instance je již otevřená"
@@ -672,13 +879,13 @@
"message": "Spravováno projektem serveru"
},
"instance.worlds.no_contact": {
"message": "Server se nepodařilo spojit"
"message": "Se serverem se nepodařilo spojit"
},
"instance.worlds.no_server_quick_play": {
"message": "Můžeš rovnou skočit na server pouze v Minecraftu Alpha 1.0.5+"
"message": "Připojit se rovnou k serverům můžeš pouze s Minecraft verzí Alpha 1.0.5+"
},
"instance.worlds.no_singleplayer_quick_play": {
"message": "V Minecraftu 1.20+ se dá rovnou přeskočit pouze do singleplayerových světů"
"message": "Můžeš se rovnou připojit do světů pro jednoho hráče pouze s Minecraft verzí 1.20+"
},
"instance.worlds.play_instance": {
"message": "Hrát instanci"
@@ -687,22 +894,22 @@
"message": "Zobrazit instanci"
},
"instance.worlds.world_in_use": {
"message": "Svět je použit"
"message": "Svět je používán"
},
"minecraft-account.add-account": {
"message": "Přidat účet"
},
"minecraft-account.label": {
"message": "Účet Minecraft"
"message": "Minecraft účet"
},
"minecraft-account.not-signed-in": {
"message": "Nejste přihlášeni"
},
"minecraft-account.remove-account": {
"message": "Odstranit účet"
"message": "Odebrat účet"
},
"minecraft-account.select-account": {
"message": "Vyberte účet"
"message": "Zvolte účet"
},
"minecraft-account.sign-in": {
"message": "Přihlásit se do Minecraftu"
@@ -711,31 +918,31 @@
"message": "Poskytováno instancí"
},
"search.filter.locked.instance-game-version.title": {
"message": "Verze hry je poskytnut instanci"
"message": "Verze hry je poskytována instancí"
},
"search.filter.locked.instance-loader.title": {
"message": "Spouštěč je poskytnut instancí"
"message": "Načítací modul je poskytován instancí"
},
"search.filter.locked.instance.sync": {
"message": "Synchronizováno z instancí"
"message": "Synchronizováno s instancí"
},
"search.filter.locked.server": {
"message": "Poskytováno serverem"
},
"search.filter.locked.server-environment.title": {
"message": "Jen klientské mody mohou být přidány na server"
"message": "Pouze módy ze strany klientu mohou být přidány na server"
},
"search.filter.locked.server-game-version.title": {
"message": "Verzi hry poskytuje server"
"message": "Verze hry je poskytována serverem"
},
"search.filter.locked.server-loader.title": {
"message": "Loader zprostředkovává server"
},
"unknown-pack-warning-modal.body": {
"message": "Soubor je zkontrolován pouze v případě, že je nahrán na Modrinth, a to bez ohledu na jeho formát (včetně formátu .mrpack)."
"message": "Soubor je zkontrolován pouze v případě, že je nahrán na Modrinth, bez ohledu na jeho formát (včetně formátu .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Toto varování již nezobrazovat"
"message": "Toto varování znovu nezobrazovat"
},
"unknown-pack-warning-modal.header": {
"message": "Potvrdit instalaci"
+164 -11
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Downloader Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Downloader opdatering"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primære instance"
},
"app.action-bar.reload-to-update": {
"message": "Genindlæs for at opdater"
},
"app.action-bar.show-more-running-instances": {
"message": "Vis flere kørende instances"
},
"app.action-bar.stop-instance": {
"message": "Stop instance"
},
"app.action-bar.update": {
"message": "Opdater"
},
"app.action-bar.view-active-downloads": {
"message": "Vis aktive downloads"
},
@@ -80,6 +89,12 @@
"app.appearance-settings.select-option": {
"message": "Vælg en mulighed"
},
"app.appearance-settings.show-play-time.description": {
"message": "Vis hvor meget tid du har brugt på at spille en instance."
},
"app.appearance-settings.show-play-time.title": {
"message": "Vis tid spillet"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Aktivere muligheden for at slå sidepanelet til og fra."
},
@@ -137,6 +152,9 @@
"app.browse.server.installing": {
"message": "Installere"
},
"app.content-install.no-compatible-versions": {
"message": "Ingen tilgængelige versioner passer til {compatibilityLabel}. Vælg en version for at installere. Afhængigheder bliver ikke installeret automatisk."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -359,8 +377,95 @@
"app.skins.modal.cape-fallback-name": {
"message": "Kappe"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} er klar til at blive installeret! Genindlæs for at opdatere nu, eller automatisk når du lukker Modrinth App."
"app.skins.modal.cape-section": {
"message": "Kappe"
},
"app.skins.modal.edit-title": {
"message": "Rediger skin"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Rediger skinnet først!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Ingen kappe"
},
"app.skins.modal.none-cape-option": {
"message": "Ingen"
},
"app.skins.modal.save-skin-button": {
"message": "Gem skin"
},
"app.skins.modal.saving-tooltip": {
"message": "Gemmer..."
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Upload et skin først!"
},
"app.skins.preview.edit-button": {
"message": "Rediger skin"
},
"app.skins.previewing-badge": {
"message": "Forhåndsvisning"
},
"app.skins.rate-limit.text": {
"message": "Du skifter dit skin for ofte. Mojang's servere har midlertidigt blokeret fremtidige anmodninger. Vent venligst et øjeblik for du prøver igen."
},
"app.skins.rate-limit.title": {
"message": "Ro på!"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Standard skins"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Du modtog disse skins for at donere til en Modrinth Pride indsamling imens der er Pride Month."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Gemte skins"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Log Ind"
},
"app.skins.sign-in.description": {
"message": "Venligst log ind til din Minecraft konto for at kunne funktionerne til at administrere dit skin i Modrinth appen."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Overrasket Modrinth Bot"
},
"app.skins.sign-in.title": {
"message": "Venligst log ind"
},
"app.skins.title": {
"message": "Skin vælger"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} er færdig med at download. Genindlæs for at opdatere nu, eller automatisk når du lukker Modrinth App."
@@ -380,9 +485,6 @@
"app.update-popup.download-complete": {
"message": "Download færdiggjort"
},
"app.update-popup.reload": {
"message": "Geninlæs"
},
"app.update-popup.title": {
"message": "Opdatering tilgængelig"
},
@@ -392,14 +494,23 @@
"app.update.complete-toast.title": {
"message": "Version {version} var installeret med succes!"
},
"app.update.download-update": {
"message": "Download opdatering"
"app.world.server-modal.placeholder-address": {
"message": "eksemple.modrinth.gg"
},
"app.update.downloading-update": {
"message": "Downloader opdatering ({percent}%)"
"app.world.server-modal.select-an-option": {
"message": "Vælg en mulighed"
},
"app.update.reload-to-update": {
"message": "Genindlæs for at installere opdatering"
"app.world.world-item.incompatible-version": {
"message": "Ikke kompatibel version {version}"
},
"app.world.world-item.not-played-yet": {
"message": "Ikke spillet endnu"
},
"app.world.world-item.offline": {
"message": "Offline"
},
"app.world.world-item.players-online": {
"message": "{count} online"
},
"friends.action.add-friend": {
"message": "Tilføj en ven"
@@ -500,6 +611,12 @@
"instance.edit-world.title": {
"message": "Rediger verden"
},
"instance.files.adding-files": {
"message": "Tilføjer filer ({completed}/{total})"
},
"instance.files.save-as": {
"message": "Gem som..."
},
"instance.server-modal.address": {
"message": "Adresse"
},
@@ -614,6 +731,9 @@
"instance.settings.tabs.java": {
"message": "Java og hukommelse"
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Indskriv Java argumenter..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "Omgivelses variabler"
},
@@ -629,6 +749,9 @@
"instance.settings.tabs.java.java-memory": {
"message": "Allokeret hukommelse"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/vej/til/java"
},
"instance.settings.tabs.window": {
"message": "Vindue"
},
@@ -701,9 +824,24 @@
"instance.worlds.world_in_use": {
"message": "Verden er i brug"
},
"minecraft-account.add-account": {
"message": "Tilføj konto"
},
"minecraft-account.label": {
"message": "Minecraft konto"
},
"minecraft-account.not-signed-in": {
"message": "Ikke logged ind"
},
"minecraft-account.remove-account": {
"message": "Fjern konto"
},
"minecraft-account.select-account": {
"message": "Vælg konto"
},
"minecraft-account.sign-in": {
"message": "Log ind på Minecraft"
},
"search.filter.locked.instance": {
"message": "Givet af instanceen"
},
@@ -727,5 +865,20 @@
},
"search.filter.locked.server-loader.title": {
"message": "Loader er givet af serveren"
},
"unknown-pack-warning-modal.body": {
"message": "En fil behandles kun, hvis den uploades til Modrinth, uanset filformat (herunder .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Vis ikke denne advarsel igen"
},
"unknown-pack-warning-modal.header": {
"message": "Bekræft installation"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Installer alligevel"
},
"unknown-pack-warning-modal.warning.title": {
"message": "Ukendt fil advarsel"
}
}
+59 -23
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} wird heruntergeladen"
},
"app.action-bar.downloading-update": {
"message": "Update wird heruntergeladen"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primäre Instanz"
},
"app.action-bar.reload-to-update": {
"message": "Zum Aktualisieren neu laden"
},
"app.action-bar.show-more-running-instances": {
"message": "Weitere laufende Instanzen anzeigen"
},
"app.action-bar.stop-instance": {
"message": "Instanz stoppen"
},
"app.action-bar.update": {
"message": "Aktualisieren"
},
"app.action-bar.view-active-downloads": {
"message": "Aktive Downloads anzeigen"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Option wählen"
},
"app.appearance-settings.show-play-time.description": {
"message": "Zeigt an, wie viel Zeit du mit dem Spielen einer Instanz verbracht hast."
},
"app.appearance-settings.show-play-time.title": {
"message": "Spielzeit anzeigen"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Ermöglicht das Umschalten der Seitenleiste."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Wird installiert"
},
"app.content-install.no-compatible-versions": {
"message": "Keine verfügbaren Versionen stimmen mit {compatibilityLabel} überein. Wähle eine Version aus, um sie trotzdem zu installieren. Abhängigkeiten werden nicht automatisch installiert."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -342,10 +360,10 @@
"message": "Bist du dir sicher, dass du diesen Skin löschen willst?"
},
"app.skins.dropped-file-error.text": {
"message": "Die abgelegte Datei konnte nicht gelesen werden."
"message": "Fehler beim Lesen der abgelegten Datei."
},
"app.skins.dropped-file-error.title": {
"message": "Fehler bei der Verarbeitung der Datei"
"message": "Fehler bei der Dateiverarbeitung"
},
"app.skins.edit-button": {
"message": "Skin bearbeiten"
@@ -381,7 +399,7 @@
"message": "Kein Umhang"
},
"app.skins.modal.none-cape-option": {
"message": "Keinen"
"message": "Keines"
},
"app.skins.modal.replace-texture-button": {
"message": "Textur ersetzen"
@@ -390,13 +408,13 @@
"message": "Skin speichern"
},
"app.skins.modal.saving-tooltip": {
"message": "Wird gespeichert..."
"message": "Speichern..."
},
"app.skins.modal.texture-section": {
"message": "Textur"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Lade zuerst ein Skin hoch!"
"message": "Lade zuerst einen Skin hoch!"
},
"app.skins.preview.edit-button": {
"message": "Skin bearbeiten"
@@ -405,10 +423,16 @@
"message": "Vorschau"
},
"app.skins.rate-limit.text": {
"message": "Du wechselst dein Skin zu oft. Die Server von Mojang haben weitere Anfragen vorübergehend blockiert. Bitte warte einen Moment, bevor du es erneut versuchst."
"message": "Du änderst deinen Skin zu oft. Die Server von Mojang haben weitere Anfragen vorübergehend blockiert. Bitte warte einen Moment, bevor du es erneut versuchst."
},
"app.skins.rate-limit.title": {
"message": "Langsamer!"
"message": "Nicht so schnell!"
},
"app.skins.reorder-error.text": {
"message": "Deine Skin-Reihenfolge konnte nicht gespeichert werden."
},
"app.skins.reorder-error.title": {
"message": "Skins konnten nicht neu angeordnet werden"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
@@ -429,7 +453,7 @@
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Du hast diese Skins erhalten, da du während des Pride-Monats für eine Modrinth-Pride-Spendenaktion gespendet hast."
"message": "Du hast diese Skins als Dankeschön für eine Spende bei der Modrinth Pride Spendenaktion während des Pride Month erhalten."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
@@ -453,20 +477,17 @@
"message": "Anmelden"
},
"app.skins.sign-in.description": {
"message": "Bitte melde dich bei deinem Minecraft-Konto an, um die Funktionen zur Skin-Verwaltung der Modrinth-App nutzen zu können."
"message": "Bitte melde dich in deinem Minecraft-Konto an, um die Skin-Verwaltungsfunktionen der Modrinth App zu nutzen."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Begeisterter Modrinth-Bot"
},
"app.skins.sign-in.title": {
"message": "Bitte melde dich an"
"message": "Bitte anmelden"
},
"app.skins.title": {
"message": "Skin-Auswahl"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} ist bereit zur Installation! Lade die App neu um jetzt zu aktualisieren, oder automatisch nach dem schliessen der Modrinth App."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} wurde heruntergeladen. Lade die App neu um jetzt zu aktualisieren, oder automatisch nach dem schliessen der Modrinth App."
},
@@ -486,7 +507,7 @@
"message": "Download abgeschlossen"
},
"app.update-popup.reload": {
"message": "Neu Laden"
"message": "Zum Aktualisieren neu laden"
},
"app.update-popup.title": {
"message": "Aktualisierung verfügbar"
@@ -497,15 +518,6 @@
"app.update.complete-toast.title": {
"message": "Version {version} wurde erfolgreich installiert!"
},
"app.update.download-update": {
"message": "Aktualisierung herunterladen"
},
"app.update.downloading-update": {
"message": "Lade Aktualisierung herunter ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Lade neu um Aktualisierung zu installieren"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -695,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "Name"
},
"instance.settings.tabs.general.update-channel": {
"message": "Update-Kanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release-, Beta- und Alpha-Versionen werden als verfügbare Updates angezeigt."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release- und Beta-Versionen werden als verfügbare Updates angezeigt."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Nur Release-Versionen werden als verfügbare Updates angezeigt."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Update-Kanal auswählen"
},
"instance.settings.tabs.hooks": {
"message": "Start Hooks"
},
+53 -17
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} wird heruntergeladen"
},
"app.action-bar.downloading-update": {
"message": "Update wird heruntergeladen"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -12,7 +15,7 @@
"message": "Zur primären Instanz machen"
},
"app.action-bar.no-instances-running": {
"message": "Keine aktiven Instanzen"
"message": "Keine laufenden Instanzen"
},
"app.action-bar.offline": {
"message": "Offline"
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primäre Instanz"
},
"app.action-bar.reload-to-update": {
"message": "Zum Aktualisieren neu laden"
},
"app.action-bar.show-more-running-instances": {
"message": "Weitere laufende Instanzen anzeigen"
},
"app.action-bar.stop-instance": {
"message": "Instanz stoppen"
},
"app.action-bar.update": {
"message": "Aktualisieren"
},
"app.action-bar.view-active-downloads": {
"message": "Aktive Downloads anzeigen"
},
@@ -33,7 +42,7 @@
"message": "Instanz anzeigen"
},
"app.action-bar.view-logs": {
"message": "Protokolle anzeigen"
"message": "Logs 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."
@@ -48,7 +57,7 @@
"message": "Farbschema"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Ändere die Kategorie, die beim Öffnen des Launchers angezeigt wird."
"message": "Ändere die Seite, die beim Öffnen des Launchers angezeigt wird."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Start"
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Wähle eine Option"
},
"app.appearance-settings.show-play-time.description": {
"message": "Zeigt an, wie viel Zeit du damit verbracht hast, eine Instanz zu spielen."
},
"app.appearance-settings.show-play-time.title": {
"message": "Spielzeit anzeigen"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Ermöglicht das Umschalten der Seitenleiste."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Wird installiert"
},
"app.content-install.no-compatible-versions": {
"message": "Keine verfügbaren Versionen stimmen mit {compatibilityLabel} überein. Wähle eine Version aus, um sie trotzdem zu installieren. Abhängigkeiten werden nicht automatisch installiert."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -405,11 +423,17 @@
"message": "Vorschau"
},
"app.skins.rate-limit.text": {
"message": "Du wechselst dein Skin zu oft. Die Server von Mojang haben weitere Anfragen vorübergehend blockiert. Bitte warte einen Moment, bevor du es erneut versuchst."
"message": "Du wechselst deinen Skin zu oft. Die Server von Mojang haben weitere Anfragen vorübergehend blockiert. Bitte warte einen Moment, bevor du es erneut versuchst."
},
"app.skins.rate-limit.title": {
"message": "Langsamer!"
},
"app.skins.reorder-error.text": {
"message": "Die Anordnung deiner Skins konnte nicht gespeichert werden."
},
"app.skins.reorder-error.title": {
"message": "Skins konnten nicht neu angeordnet werden"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -464,9 +488,6 @@
"app.skins.title": {
"message": "Skin-Auswahl"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} ist bereit zur Installation! Neu laden, um jetzt zu aktualisieren, oder automatisch, wenn du die Modrinth App schließt."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} wurde heruntergeladen. Neu laden, um jetzt zu aktualisieren, oder automatisch aktualisieren, wenn du die Modrinth App schließt."
},
@@ -486,7 +507,7 @@
"message": "Download abgeschlossen"
},
"app.update-popup.reload": {
"message": "Neu laden"
"message": "Zum Aktualisieren neu laden"
},
"app.update-popup.title": {
"message": "Aktualisierung verfügbar"
@@ -497,15 +518,6 @@
"app.update.complete-toast.title": {
"message": "Version {version} wurde erfolgreich installiert!"
},
"app.update.download-update": {
"message": "Update herunterladen"
},
"app.update.downloading-update": {
"message": "Update wird heruntergeladen ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Neu laden, um das Update zu installieren"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -695,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "Name"
},
"instance.settings.tabs.general.update-channel": {
"message": "Aktualisierungskanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Voll, Beta- und Alpha-Versionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Voll- und Beta-Versionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Vollversion"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Nur Vollversionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Aktualisierungskanal auswählen"
},
"instance.settings.tabs.hooks": {
"message": "Startargumente"
},
+40 -13
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Downloading Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Downloading update"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primary instance"
},
"app.action-bar.reload-to-update": {
"message": "Reload to update"
},
"app.action-bar.show-more-running-instances": {
"message": "Show more running instances"
},
"app.action-bar.stop-instance": {
"message": "Stop instance"
},
"app.action-bar.update": {
"message": "Update"
},
"app.action-bar.view-active-downloads": {
"message": "View active downloads"
},
@@ -428,6 +437,12 @@
"app.skins.rate-limit.title": {
"message": "Slow down!"
},
"app.skins.reorder-error.text": {
"message": "Your skin order could not be saved."
},
"app.skins.reorder-error.title": {
"message": "Failed to reorder skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -482,9 +497,6 @@
"app.skins.title": {
"message": "Skin selector"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} is ready to install! Reload to update now, or automatically when you close Modrinth App."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} has finished downloading. Reload to update now, or automatically when you close Modrinth App."
},
@@ -504,7 +516,7 @@
"message": "Download complete"
},
"app.update-popup.reload": {
"message": "Reload"
"message": "Reload to update"
},
"app.update-popup.title": {
"message": "Update available"
@@ -515,15 +527,6 @@
"app.update.complete-toast.title": {
"message": "Version {version} was successfully installed!"
},
"app.update.download-update": {
"message": "Download update"
},
"app.update.downloading-update": {
"message": "Downloading update ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Reload to install update"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -713,6 +716,30 @@
"instance.settings.tabs.general.name": {
"message": "Name"
},
"instance.settings.tabs.general.update-channel": {
"message": "Update channel"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release, beta, and alpha versions will be shown as available updates."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release and beta versions will be shown as available updates."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Only release versions will be shown as available updates."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Select update channel"
},
"instance.settings.tabs.hooks": {
"message": "Launch hooks"
},
+66 -21
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Descargando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Descargando actualización"
},
"app.action-bar.downloads": {
"message": "Descargas"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instancia principal"
},
"app.action-bar.reload-to-update": {
"message": "Reinicia para actualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar más instancias en ejecución"
},
"app.action-bar.stop-instance": {
"message": "Detener instancia"
},
"app.action-bar.update": {
"message": "Actualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver descargas en curso"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Selecciona una opción"
},
"app.appearance-settings.show-play-time.description": {
"message": "Muestra cuánto tiempo has estado jugando en una instancia."
},
"app.appearance-settings.show-play-time.title": {
"message": "Mostrar tiempo de juego"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Permite mostrar u ocultar la barra lateral."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Instalando"
},
"app.content-install.no-compatible-versions": {
"message": "No hay versiones disponibles que coincidan con {compatibilityLabel}. Selecciona una versión para instalarla de todas formas. Las dependencias no se instalarán automáticamente."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -294,7 +312,7 @@
"message": "Este proyecto ya está instalado"
},
"app.project.install-context.back-to-browse": {
"message": "Volver a descubrir contenido"
"message": "Volver al explorador"
},
"app.project.install-context.install-content-to-instance": {
"message": "Instalar contenido a la instancia"
@@ -327,7 +345,7 @@
"message": "Añadir skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Arrastrar y soltar"
"message": "Arrastra y suelta"
},
"app.skins.apply-button": {
"message": "Aplicar"
@@ -336,10 +354,10 @@
"message": "Eliminar skin"
},
"app.skins.delete-modal.description": {
"message": "Esto eliminará permanentemente el skin seleccionado. Esta acción no se puede deshacer."
"message": "Esto eliminará permanentemente la skin seleccionada. Esta acción no se puede deshacer."
},
"app.skins.delete-modal.title": {
"message": "¿Estás seguro de que quieres eliminar este skin?"
"message": "¿Estás seguro de que quieres eliminar esta skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Error al leer el archivo arrastrado."
@@ -354,7 +372,7 @@
"message": "Añadir skin"
},
"app.skins.modal.add-title": {
"message": "Añadiendo un skin"
"message": "Añadiendo una skin"
},
"app.skins.modal.arm-style-section": {
"message": "Estilo de brazo"
@@ -396,7 +414,7 @@
"message": "Textura"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "¡Primero carga un skin!"
"message": "¡Primero carga una skin!"
},
"app.skins.preview.edit-button": {
"message": "Editar skin"
@@ -410,6 +428,12 @@
"app.skins.rate-limit.title": {
"message": "¡Más despacio!"
},
"app.skins.reorder-error.text": {
"message": "No se ha podido guardar el orden de las skins."
},
"app.skins.reorder-error.title": {
"message": "Error al reordenar las skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Constructores y Biomas"
},
@@ -422,11 +446,20 @@
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Orgullo Modrinth"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Has recibido estas skins por donar a la fundación Orgullo Modrinth durante el Mes del Orgullo."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Monturas del Caos"
},
"app.skins.section.saved-skins": {
"message": "Skins guardados"
"message": "Skins guardadas"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
@@ -455,9 +488,6 @@
"app.skins.title": {
"message": "Selector de skin"
},
"app.update-popup.body": {
"message": "¡Modrinth App v{version} está lista para instalarse! Actualiza ahora o automáticamente al cerrar la Modrinth App."
},
"app.update-popup.body.download-complete": {
"message": "La descarga de la Modrinth App v{version} ha finalizado. Actualiza ahora o automáticamente al cerrar la Modrinth App."
},
@@ -477,7 +507,7 @@
"message": "Descarga completada"
},
"app.update-popup.reload": {
"message": "Recargar"
"message": "Reinicia para actualizar"
},
"app.update-popup.title": {
"message": "Actualización disponible"
@@ -488,15 +518,6 @@
"app.update.complete-toast.title": {
"message": "¡La versión {version} se ha instalado correctamente!"
},
"app.update.download-update": {
"message": "Descargar actualización"
},
"app.update.downloading-update": {
"message": "Descargando actualización ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Recarga para instalar la actualización"
},
"app.world.server-modal.placeholder-address": {
"message": "ejemplo.modrinth.gg"
},
@@ -686,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "Nombre"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de actualizaciones"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Las versiones estables, beta y alpha se mostrarán como actualizaciones disponibles. "
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Las versiones estables y beta serán mostradas como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Estable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Sólo las versiones estables serán mostradas como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Selecciona el canal de actualizaciones"
},
"instance.settings.tabs.hooks": {
"message": "Hooks de inicio"
},
@@ -876,7 +921,7 @@
"message": "Proporcionado por el servidor"
},
"search.filter.locked.server-environment.title": {
"message": "Solo se puede añadir mods que sean del lado del cliente a la instancia del servidor"
"message": "Solo se pueden añadir mods que sean del lado del cliente a la instancia del servidor"
},
"search.filter.locked.server-game-version.title": {
"message": "La versión del juego es proporcionada por el servidor"
+200 -32
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Descargando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Descargando actualización"
},
"app.action-bar.downloads": {
"message": "Descargas"
},
@@ -9,7 +12,7 @@
"message": "Ocultar más instancias en ejecución"
},
"app.action-bar.make-primary-instance": {
"message": "Hacer instancia principal"
"message": "Establecer como instancia principal"
},
"app.action-bar.no-instances-running": {
"message": "Ninguna instancia en ejecución"
@@ -20,20 +23,26 @@
"app.action-bar.primary-instance": {
"message": "Instancia principal"
},
"app.action-bar.reload-to-update": {
"message": "Recargar para actualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar más instancias en ejecución"
},
"app.action-bar.stop-instance": {
"message": "Finalizar instancia"
"message": "Detener instancia"
},
"app.action-bar.update": {
"message": "Actualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Mostrar descargas activas"
},
"app.action-bar.view-instance": {
"message": "Mostrar instancia"
"message": "Ver instancia"
},
"app.action-bar.view-logs": {
"message": "Mostrar registros"
"message": "Ver 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."
@@ -42,43 +51,43 @@
"message": "Renderizado avanzado"
},
"app.appearance-settings.color-theme.description": {
"message": "Seleccione su tema de color preferido para Modrinth en este dispositivo."
"message": "Selecciona tu tema de color preferido para la Modrinth App."
},
"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 lanzador inicia."
"message": "Cambia la página en la que el launcher inicia."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Inicio"
},
"app.appearance-settings.default-landing-page.library": {
"message": "Librería"
"message": "Biblioteca"
},
"app.appearance-settings.default-landing-page.title": {
"message": "Página predeterminada"
"message": "Página de inicio 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"
"message": "Ocultar etiqueta de nombre"
},
"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."
"message": "Incluye los mundos recientes en la sección \"Volver a jugar\" de la página de inicio."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "Vuelve a jugar tus mundos"
"message": "Volver a jugar en tus mundos"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimiza el lanzador cuando un proceso de Minecraft empieza."
"message": "Minimiza el lanzador cuando se inicia un proceso de Minecraft."
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimiza el lanzador"
"message": "Minimizar lanzador"
},
"app.appearance-settings.native-decorations.description": {
"message": "Usa los sistemas de marco de Windows (se requiere resetear la aplicación)."
"message": "Usa el marco de ventana del sistema (requiere reiniciar la aplicación)."
},
"app.appearance-settings.native-decorations.title": {
"message": "Decoraciones nativas"
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Selecciona una opción"
},
"app.appearance-settings.show-play-time.description": {
"message": "Muestra cuánto tiempo has estado jugando en una instancia."
},
"app.appearance-settings.show-play-time.title": {
"message": "Mostrar tiempo de juego"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Activa la posibilidad de alternar la barra lateral."
},
@@ -96,7 +111,7 @@
"message": "Si intentas instalar un archivo de paquete de Modrinth (.mrpack) que no esté alojado en Modrinth, debes comprender los riesgos que esto conlleva antes de instalarlo."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Advertir antes de instalarme paquetes de mods desconocidos"
"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."
@@ -105,7 +120,7 @@
"message": "No se puede conectar con los servidores de autenticación"
},
"app.browse.add-servers-to-instance": {
"message": "Añadir servidor a instancia"
"message": "Añadiendo servidor a la instancia"
},
"app.browse.add-to-an-instance": {
"message": "Añadir a una instancia"
@@ -135,7 +150,7 @@
"message": "Ocultar servidores ya añadidos"
},
"app.browse.project-type.modpacks": {
"message": "Paquetes de Mods"
"message": "Modpacks"
},
"app.browse.server-instance-content-warning": {
"message": "Añadir contenido puede comprometer la compatibilidad a la hora de unirse a un servidor. Cualquier contenido añadido se perderá cuando actualices la instancia del servidor."
@@ -143,11 +158,14 @@
"app.browse.server.installing": {
"message": "Instalando"
},
"app.content-install.no-compatible-versions": {
"message": "No hay versiones disponibles que coincidan con {compatibilityLabel}. Selecciona una versión para instalarla de todas formas. Las dependencias no se instalarán automáticamente."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
"app.creation-modal.installing-modpack.title": {
"message": "Instalando el modpack..."
"message": "Instalando modpack..."
},
"app.export-modal.description-placeholder": {
"message": "Escribe la descripción del modpack..."
@@ -341,8 +359,143 @@
"app.skins.apply-button": {
"message": "Aplicar"
},
"app.update-popup.body": {
"message": "¡La versión v{version} de Modrinth está lista para instalarse! Actualiza ahora o automáticamente al cerrar la aplicación."
"app.skins.delete-button": {
"message": "Borrar skin"
},
"app.skins.delete-modal.description": {
"message": "Esto borrará permanentemente la skin seleccionada. Esta acción no se puede deshacer."
},
"app.skins.delete-modal.title": {
"message": "¿Estás seguro/a de que quieres eliminar está skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Error al leer el archivo arrastrado."
},
"app.skins.dropped-file-error.title": {
"message": "Error al procesar el archivo"
},
"app.skins.edit-button": {
"message": "Editar skin"
},
"app.skins.modal.add-skin-button": {
"message": "Añadir skin"
},
"app.skins.modal.add-title": {
"message": "Añadiendo una skin"
},
"app.skins.modal.arm-style-section": {
"message": "Estilo de brazo"
},
"app.skins.modal.arm-style-slim": {
"message": "Delgado"
},
"app.skins.modal.arm-style-wide": {
"message": "Ancho"
},
"app.skins.modal.cape-fallback-name": {
"message": "Capa"
},
"app.skins.modal.cape-section": {
"message": "Capa"
},
"app.skins.modal.edit-title": {
"message": "Editando skin"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "¡Modifica la skin primero!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Sin capa"
},
"app.skins.modal.none-cape-option": {
"message": "Nada"
},
"app.skins.modal.replace-texture-button": {
"message": "Reemplazar textura"
},
"app.skins.modal.save-skin-button": {
"message": "Guardar skin"
},
"app.skins.modal.saving-tooltip": {
"message": "Guardando..."
},
"app.skins.modal.texture-section": {
"message": "Textura"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "¡Primero sube una skin!"
},
"app.skins.preview.edit-button": {
"message": "Editar skin"
},
"app.skins.previewing-badge": {
"message": "Previsualizando"
},
"app.skins.rate-limit.text": {
"message": "Estuviste cambiando tu skin demasiado rápido. Los servidores de Mojang han bloqueado temporalmente tus futuras solicitaciones. Por favor, espere un momento antes de intentar de nuevo."
},
"app.skins.rate-limit.title": {
"message": "¡Cálmate!"
},
"app.skins.reorder-error.text": {
"message": "No se ha podido guardar el orden de las skins."
},
"app.skins.reorder-error.title": {
"message": "Error al reordenar las skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders y Biomas"
},
"app.skins.section.chase-the-skies": {
"message": "Persigue los cielos"
},
"app.skins.section.default-skins": {
"message": "Skins por defecto"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Orgullo Modrinth"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Has recibido estas skins por donar a la fundación Orgullo Modrinth durante el Mes del Orgullo."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Monturas del Caos"
},
"app.skins.section.saved-skins": {
"message": "Skins guardadas"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "La Edad de Cobre"
},
"app.skins.section.the-garden-awakens": {
"message": "El Jardín Despierta"
},
"app.skins.section.tiny-takeover": {
"message": "Pequeña Invasión"
},
"app.skins.sign-in.button": {
"message": "Iniciar Sesión"
},
"app.skins.sign-in.description": {
"message": "Por favor, inicia sesión con tu cuenta de Minecraft para usar las funciones del administrador de Skins de la app de Modrinth."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Modrinth Bot emocionado"
},
"app.skins.sign-in.title": {
"message": "Por favor, inicia sesión"
},
"app.skins.title": {
"message": "Selector de Skin"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} se ha descargado. Actualiza la página ahora o espera a que se actualice automáticamente al cerrar Modrinth App."
@@ -363,7 +516,7 @@
"message": "Descarga completada"
},
"app.update-popup.reload": {
"message": "Recargar"
"message": "Recargar para actualizar"
},
"app.update-popup.title": {
"message": "Actualización disponible"
@@ -374,15 +527,6 @@
"app.update.complete-toast.title": {
"message": "¡La versión {version} se ha instalado correctamente!"
},
"app.update.download-update": {
"message": "Descarga actualización"
},
"app.update.downloading-update": {
"message": "Descargando actualización ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Recarga para instalar la actualización"
},
"app.world.server-modal.placeholder-address": {
"message": "ejemplo.modrinth.gg"
},
@@ -572,6 +716,30 @@
"instance.settings.tabs.general.name": {
"message": "Nombre"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de actualizaciones"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Se mostrarán las versiones estables, beta y alfa como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Se mostrarán las versiones estables y beta como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Estable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Solo se mostrarán las versiones estables como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Seleccionar canal de actualizaciones"
},
"instance.settings.tabs.hooks": {
"message": "Hooks de lanzamiento"
},
@@ -753,7 +921,7 @@
"message": "Versión del juego proporciona por la instancia"
},
"search.filter.locked.instance-loader.title": {
"message": "Cargador proporcionado por la instancia"
"message": "Loader proporcionado por la instancia"
},
"search.filter.locked.instance.sync": {
"message": "Sincronizar con la instancia"
@@ -768,7 +936,7 @@
"message": "La versión del juego es proporcionada por el servidor"
},
"search.filter.locked.server-loader.title": {
"message": "El cargador lo proporciona el servidor"
"message": "Loader proporcionado por el servidor"
},
"unknown-pack-warning-modal.body": {
"message": "Un archivo solo es revisado si es subido a Modrinth, independientemente de su formato de archivo (incluyendo .mrpack)."
+27 -12
View File
@@ -143,6 +143,9 @@
"app.browse.server.installing": {
"message": "Asennetaan"
},
"app.content-install.no-compatible-versions": {
"message": "Yhtään versiota ei ole saatavilla yhteensopivuudella {compatibilityLabel}. Valitse asennettava versio siitä huolimatta. Riippuvuuksia ei asenneta automaattisesti."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -332,8 +335,20 @@
"app.settings.tabs.resource-management": {
"message": "Resurssien hallinta"
},
"app.update-popup.body": {
"message": "Modrinth App versio {version} on valmis asennettavaksi. Voit käynnistää sovelluksen uudelleen päivittääksesi heti tai antaa päivityksen asentua automaattisesti, kun suljet Modrinth Appin."
"app.skins.reorder-error.title": {
"message": "Skinien järjestyksen muuttaminen epäonnistui"
},
"app.skins.section.mounts-of-mayhem": {
"message": "Sekasorron ratsut"
},
"app.skins.section.tiny-takeover": {
"message": "Pienten vallankumous"
},
"app.skins.sign-in.title": {
"message": "Kirjaudu sisään"
},
"app.skins.title": {
"message": "Skinivalinta"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App versio {version} on ladattu. Voit käynnistää sovelluksen uudelleen päivittääksesi heti tai antaa päivityksen asentua automaattisesti, kun suljet Modrinth Appin."
@@ -354,7 +369,7 @@
"message": "Lataus valmis"
},
"app.update-popup.reload": {
"message": "Lataa uudelleen"
"message": "Lataa uudelleen päivittääksesi"
},
"app.update-popup.title": {
"message": "Päivitys saatavilla"
@@ -365,15 +380,6 @@
"app.update.complete-toast.title": {
"message": "Versio {version} asennettiin onnistuneesti!"
},
"app.update.download-update": {
"message": "Lataa päivitys"
},
"app.update.downloading-update": {
"message": "Ladataan päivitystä ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Lataa uudelleen asentaaksesi päivityksen"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -563,6 +569,15 @@
"instance.settings.tabs.general.name": {
"message": "Nimi"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Julkaisu"
},
"instance.settings.tabs.hooks": {
"message": "Aloitustoiminnot"
},
+18 -15
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Dina-download ang Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Dina-download ang update"
},
"app.action-bar.downloads": {
"message": "Mga downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Pangunahing instansiya"
},
"app.action-bar.reload-to-update": {
"message": "Mag-reload upang ma-update"
},
"app.action-bar.show-more-running-instances": {
"message": "Ipakita ang mas marami pang tumatakbong instansiya"
},
"app.action-bar.stop-instance": {
"message": "Itigil ang instansiya"
},
"app.action-bar.update": {
"message": "Mag-update"
},
"app.action-bar.view-active-downloads": {
"message": "Tignan ang mga active downloads"
},
@@ -86,6 +95,9 @@
"app.appearance-settings.select-option": {
"message": "Pumili ng opsiyon"
},
"app.appearance-settings.show-play-time.description": {
"message": "Magpapakita ang katagal ng iyong paglalaro sa isang instansiya."
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Pagpapagana na mata-toggle ang sidebar."
},
@@ -359,9 +371,6 @@
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.update-popup.body": {
"message": "Ang Modrinth App v{version} ay handa nang ma-install. Mag-reload upang ma-update ngayon, o awtomatiko sa pagsara ng Modrinth App."
},
"app.update-popup.body.download-complete": {
"message": "Tapos nang ma-download ang Modrinth App v{version}. Mag-reload upang ma-update ngayon, o awtomatiko sa pagsara ng Modrinth App."
},
@@ -380,9 +389,6 @@
"app.update-popup.download-complete": {
"message": "Nakumpleto ang pagdownload"
},
"app.update-popup.reload": {
"message": "Mag-reload"
},
"app.update-popup.title": {
"message": "May bagong update"
},
@@ -392,15 +398,6 @@
"app.update.complete-toast.title": {
"message": "Tagumpay na na-install ang bersiyong {version}!"
},
"app.update.download-update": {
"message": "I-download ang update"
},
"app.update.downloading-update": {
"message": "Nagdadownload ng update ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Handang ma-install ang update"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -590,6 +587,12 @@
"instance.settings.tabs.general.name": {
"message": "Pangalan"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.hooks": {
"message": "Mga launch hook"
},
+61 -25
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Téléchargement de Java {version} en cours"
},
"app.action-bar.downloading-update": {
"message": "Téléchargement de la mise à jour"
},
"app.action-bar.downloads": {
"message": "Téléchargements"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instance première"
},
"app.action-bar.reload-to-update": {
"message": "Recharger pour mettre à jour"
},
"app.action-bar.show-more-running-instances": {
"message": "Montrer plus d'instances en exécution"
},
"app.action-bar.stop-instance": {
"message": "Arrêter l'instance"
},
"app.action-bar.update": {
"message": "Mettre à jour"
},
"app.action-bar.view-active-downloads": {
"message": "Voir les téléchargements actifs"
},
@@ -45,13 +54,13 @@
"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"
"message": "Couleur du thème"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Modifier la page sur laquelle le lancheur s'ouvre."
"message": "Modifier la page sur laquelle le lanceur s'ouvre."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Acceuil"
"message": "Accueil"
},
"app.appearance-settings.default-landing-page.library": {
"message": "Bibliothèque"
@@ -72,10 +81,10 @@
"message": "Mondes sur partie rapide"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimiser le launcher quand Minecraft démarre."
"message": "Minimiser le lanceur quand Minecraft démarre."
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimiser le launcher"
"message": "Minimiser le lanceur"
},
"app.appearance-settings.native-decorations.description": {
"message": "Utiliser le cadre fenêtre du système (redémarrage de l'application requis)."
@@ -86,8 +95,14 @@
"app.appearance-settings.select-option": {
"message": "Sélectionnez une option"
},
"app.appearance-settings.show-play-time.description": {
"message": "Affiche combien de temps vous avez passé sur une instance."
},
"app.appearance-settings.show-play-time.title": {
"message": "Afficher le temps de jeu"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Permet d'ouvrir de de fermer la barre latérale."
"message": "Permet d'ouvrir ou de fermer la barre latérale."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Tiroir latéral"
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Installation en cours"
},
"app.content-install.no-compatible-versions": {
"message": "Aucune version disponible ne correspond à {compatibilityLabel}. Sélectionnez une version pour l'installer malgré tout. Les dépendances ne seront pas installées automatiquement."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -333,10 +351,10 @@
"message": "Appliquer"
},
"app.skins.delete-button": {
"message": "Supprimer la skin"
"message": "Supprimer le skin"
},
"app.skins.delete-modal.description": {
"message": "Cela va supprimer de manière permanente cette skin. Cette action est irréversible."
"message": "Cela va supprimer de manière permanente ce skin. Cette action est irréversible."
},
"app.skins.delete-modal.title": {
"message": "Êtes-vous sûr de vouloir supprimer cette skin ?"
@@ -348,7 +366,7 @@
"message": "Erreur lors du traitement du fichier"
},
"app.skins.edit-button": {
"message": "Modifier la skin"
"message": "Modifier le skin"
},
"app.skins.modal.add-skin-button": {
"message": "Ajouter une skin"
@@ -372,7 +390,7 @@
"message": "Cape"
},
"app.skins.modal.edit-title": {
"message": "Modification de la skin"
"message": "Modification du skin"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Faites d'abord une modification à la skin !"
@@ -387,7 +405,7 @@
"message": "Remplacer la texture"
},
"app.skins.modal.save-skin-button": {
"message": "Sauvegarder la skin"
"message": "Sauvegarder le skin"
},
"app.skins.modal.saving-tooltip": {
"message": "Sauvegarde..."
@@ -399,7 +417,7 @@
"message": "Importez d'abord une skin !"
},
"app.skins.preview.edit-button": {
"message": "Modifier la skin"
"message": "Modifier le skin"
},
"app.skins.previewing-badge": {
"message": "Aperçu"
@@ -410,6 +428,12 @@
"app.skins.rate-limit.title": {
"message": "Du calme !"
},
"app.skins.reorder-error.text": {
"message": "L'ordre des skins n'a pas pu être sauvegardé."
},
"app.skins.reorder-error.title": {
"message": "Impossible de réorganiser les skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -464,9 +488,6 @@
"app.skins.title": {
"message": "Sélecteur de skin"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} est prête à être installée ! Relancez l'application pour faire la mise à jour maintenant, ou automatiquement à la fermeture de Modrinth App."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} a finie d'être téléchargée. Rechargez pour mettre à jour maintenant, ou automatiquement quand vous fermez Modrinth App."
},
@@ -486,7 +507,7 @@
"message": "Téléchargement terminé"
},
"app.update-popup.reload": {
"message": "Recharger"
"message": "Recharger pour mettre à jour"
},
"app.update-popup.title": {
"message": "Mise à jour disponible"
@@ -497,15 +518,6 @@
"app.update.complete-toast.title": {
"message": "La version {version} a été téléchargée avec succès !"
},
"app.update.download-update": {
"message": "Télécharger la mise à jour"
},
"app.update.downloading-update": {
"message": "Téléchargement de la mise à jour ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Relancez l'application pour installer la mise à jour"
},
"app.world.server-modal.placeholder-address": {
"message": "exemple.modrinth.gg"
},
@@ -692,6 +704,30 @@
"instance.settings.tabs.general.name": {
"message": "Nom"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de mise à jour"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Les versions stables, bêta et alpha seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Bêta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Les versions stables et bêta seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Seules les versions stables seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Sélectionner le canal de mise à jour"
},
"instance.settings.tabs.hooks": {
"message": "Crochets de lancement"
},
+18 -15
View File
@@ -1,7 +1,19 @@
{
"app.action-bar.downloading-java": {
"message": "מוריד את Java {version}"
},
"app.action-bar.downloading-update": {
"message": "מוריד עדכון"
},
"app.action-bar.downloads": {
"message": "הורדות"
},
"app.action-bar.hide-more-running-instances": {
"message": "החבא יותר התקנים רצים"
},
"app.action-bar.make-primary-instance": {
"message": "הפוך למקרה ראשי"
},
"app.action-bar.offline": {
"message": "לא מקוון"
},
@@ -11,6 +23,9 @@
"app.appearance-settings.advanced-rendering.description": {
"message": "מאפשר עיבוד מתקדם כגון אפקטים טשטוש העלולים לגרום לבעיות ביצועים ללא עיבוד מואץ לחומרה."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "חזרה לעולמות"
},
"app.auth-servers.unreachable.body": {
"message": "ייתכן ששרתי האימות של Minecraft מושבתים כרגע. יש לבדוק את חיבור האינטרנט שלך ולנסות שוב מאוחר יותר."
},
@@ -203,9 +218,6 @@
"app.settings.tabs.resource-management": {
"message": "ניהול משאבים"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} מוכנה להורדה!\nיש לרענן כדי לעדכן עכשיו, או באופן אוטומטי בעת סגירת האפליקציה."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} סיימה את תהליך ההורדה. יש לרענן כדי לעדכן עכשיו, או באופן אוטומטי בעת סגירת האפליקציה."
},
@@ -224,9 +236,6 @@
"app.update-popup.download-complete": {
"message": "הורדה הושלמה"
},
"app.update-popup.reload": {
"message": "רענן"
},
"app.update-popup.title": {
"message": "עדכון זמין"
},
@@ -236,15 +245,6 @@
"app.update.complete-toast.title": {
"message": "גרסה {version} הותקנה בהצלחה!"
},
"app.update.download-update": {
"message": "הורדת עדכון"
},
"app.update.downloading-update": {
"message": "מוריד עדכון ({percent}%)"
},
"app.update.reload-to-update": {
"message": "צריך לרענן כדי להתקין את העדכון"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -266,6 +266,9 @@
"friends.action.add-friend": {
"message": "הוספת חבר"
},
"friends.action.view-friend-requests": {
"message": "{count, plural, one {בקשת חברות אחת} other {# בקשות חברות}}"
},
"friends.add-friend.submit": {
"message": "שליחת בקשת חברות"
},
+65 -20
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "A Java {version} letöltése"
},
"app.action-bar.downloading-update": {
"message": "Frissítés letöltése"
},
"app.action-bar.downloads": {
"message": "Letöltések"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Elsődleges példány"
},
"app.action-bar.reload-to-update": {
"message": "Töltsd újra a frissítéshez"
},
"app.action-bar.show-more-running-instances": {
"message": "További futó pédányok megjelenítése"
},
"app.action-bar.stop-instance": {
"message": "Példány leállítása"
},
"app.action-bar.update": {
"message": "Frissítés"
},
"app.action-bar.view-active-downloads": {
"message": "Aktív letöltések megtekintése"
},
@@ -69,7 +78,7 @@
"message": "A kezdőlap „Folytasd itt” részében a legújabb világok is megtalálhatók."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "„Folytasd itt” a világokat is"
"message": "Visszaugrás a világokba"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimalizáld az indítót a Minecraft indításakor."
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Válassz egy lehetőséget"
},
"app.appearance-settings.show-play-time.description": {
"message": "Megmutatja, mennyi időt töltöttél játszva egy profillal."
},
"app.appearance-settings.show-play-time.title": {
"message": "Játékidő megjelenítése"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Lehetővé teszi az oldalsáv be- és kikapcsolását."
},
@@ -105,7 +120,7 @@
"message": "Nem lehet elérni a hitelesítési kiszolgálókat"
},
"app.browse.add-servers-to-instance": {
"message": "Szerver hozzáadása az példányhoz"
"message": "Szerver hozzáadása a példányhoz"
},
"app.browse.add-to-an-instance": {
"message": "Hozzáadás egy példányhoz"
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Letöltés..."
},
"app.content-install.no-compatible-versions": {
"message": "Nincs a(z) {compatibilityLabel} verziónak megfelelő elérhető verzió. Válassz ki egy verziót, ha mégis telepíteni szeretnéd. A függőségek nem települnek automatikusan."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -272,6 +290,9 @@
"app.modal.install-to-play.install-button": {
"message": "Telepítés"
},
"app.modal.install-to-play.mod-count": {
"message": "{count} mod"
},
"app.modal.install-to-play.required-modpack": {
"message": "Szükséges modcsomag"
},
@@ -428,6 +449,15 @@
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Ezeket a kinézeteket azért kaptad, mert a Pride-hónap során adományoztál a Modrinth Pride jótékonysági gyűjtéséhez."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
@@ -461,9 +491,6 @@
"app.skins.title": {
"message": "Kinézetváltó"
},
"app.update-popup.body": {
"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 a 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 válaszd ki a Frissítés opciót, vagy az alkalmazás a bezárásakor automatikusan frissül."
},
@@ -483,7 +510,7 @@
"message": "Sikeres letöltés"
},
"app.update-popup.reload": {
"message": "Frissítés"
"message": "Töltsd újra a frissítéshez"
},
"app.update-popup.title": {
"message": "Frissítés elérhető"
@@ -494,15 +521,6 @@
"app.update.complete-toast.title": {
"message": "Verzió {version} sikeresen telepítve!"
},
"app.update.download-update": {
"message": "Frissítés letöltése"
},
"app.update.downloading-update": {
"message": "Frissítés letöltése ({percent}%)"
},
"app.update.reload-to-update": {
"message": "A telepítéshez újraindítás szükséges"
},
"app.world.server-modal.placeholder-address": {
"message": "pelda.modrinth.gg"
},
@@ -524,6 +542,9 @@
"friends.action.add-friend": {
"message": "Barát hozzáadása"
},
"friends.action.view-friend-requests": {
"message": "{count} barát {count, plural, other {kérelem}}"
},
"friends.add-friend.submit": {
"message": "Barátkérelem elküldése"
},
@@ -689,6 +710,30 @@
"instance.settings.tabs.general.name": {
"message": "Név"
},
"instance.settings.tabs.general.update-channel": {
"message": "Frissítési csatorna"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "A kiadás, béta és az alfa verziók is meg fognak jelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Béta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "A kiadás és a béta verziók is meg fognak jelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Kiadás"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Csak a release verziók fognak megjelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Válaszd ki a frissítési csatornát"
},
"instance.settings.tabs.hooks": {
"message": "Indítási horgok"
},
@@ -816,7 +861,7 @@
"message": "Ne mutasd a kezdőlapon"
},
"instance.worlds.game_already_open": {
"message": "A profil már el van indítva"
"message": "A példány már el van indítva"
},
"instance.worlds.hardcore": {
"message": "Hardcore mód"
@@ -840,7 +885,7 @@
"message": "Játék a profillal"
},
"instance.worlds.view_instance": {
"message": "Profil megtekintése"
"message": "Példány megtekintése"
},
"instance.worlds.world_in_use": {
"message": "A világ használatban van"
@@ -864,13 +909,13 @@
"message": "Bejelentkezés a Minecraftba"
},
"search.filter.locked.instance": {
"message": "A profil által van megadva"
"message": "Pédány által megadva"
},
"search.filter.locked.instance-game-version.title": {
"message": "A játékverzió a profil által van megadva"
"message": "A játékverziót az adott példány biztosítja"
},
"search.filter.locked.instance-loader.title": {
"message": "A betöltő a profil által van megadva"
"message": "A betöltőt az adott példány biztosítja"
},
"search.filter.locked.instance.sync": {
"message": "Profil szinkronizálása"
+55 -13
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Mengunduh Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Mengunduh pembaruan"
},
"app.action-bar.downloads": {
"message": "Unduhan"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instans utama"
},
"app.action-bar.reload-to-update": {
"message": "Muat ulang untuk memperbarui"
},
"app.action-bar.show-more-running-instances": {
"message": "Tampilkan lebih banyak instans yang sedang berjalan"
},
"app.action-bar.stop-instance": {
"message": "Hentikan instans"
},
"app.action-bar.update": {
"message": "Perbarui"
},
"app.action-bar.view-active-downloads": {
"message": "Lihat pengunduhan berlangsung"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Pilih opsi"
},
"app.appearance-settings.show-play-time.description": {
"message": "Menampilkan seberapa lama Anda memainkan sebuah instans."
},
"app.appearance-settings.show-play-time.title": {
"message": "Tampilkan waktu bermain"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Menghidupkan kemampuan untuk menghidupkan dan mematikan bilah sisi."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Memasang"
},
"app.content-install.no-compatible-versions": {
"message": "Tidak ada versi tersedia yang cocok untuk {compatibilityLabel}. Anda tetap dapat memilih versi yang akan dipasang. Dependensi tidak akan dipasang secara otomatis."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -272,6 +290,9 @@
"app.modal.install-to-play.install-button": {
"message": "Pasang"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Paket mod yang diperlukan"
},
@@ -416,6 +437,12 @@
"app.skins.rate-limit.title": {
"message": "Pelan-pelan!"
},
"app.skins.reorder-error.text": {
"message": "Urutan rupa Anda tidak dapat disimpan."
},
"app.skins.reorder-error.title": {
"message": "Gagal mengurut ulang rupa"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -470,9 +497,6 @@
"app.skins.title": {
"message": "Pemilih rupa"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} siap dipasang! Muat ulang untuk memperbarui sekarang, atau secara otomatis saat Anda menutup Modrinth App."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} telah selesai mengunduh. Muat ulang untuk memperbarui sekarang, atau secara otomatis saat Anda menutup Modrinth App."
},
@@ -492,7 +516,7 @@
"message": "Selesai mengunduh"
},
"app.update-popup.reload": {
"message": "Muat ulang"
"message": "Muat ulang untuk memperbarui"
},
"app.update-popup.title": {
"message": "Pembaruan tersedia"
@@ -503,15 +527,6 @@
"app.update.complete-toast.title": {
"message": "Versi {version} berhasil dipasang!"
},
"app.update.download-update": {
"message": "Unduh pembaruan"
},
"app.update.downloading-update": {
"message": "Mengunduh pembaruan ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Muat ulang untuk memasang pembaruan"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -533,6 +548,9 @@
"friends.action.add-friend": {
"message": "Tambah teman"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, other {permintaan}} teman"
},
"friends.add-friend.submit": {
"message": "Kirim permintaan teman"
},
@@ -698,6 +716,30 @@
"instance.settings.tabs.general.name": {
"message": "Nama"
},
"instance.settings.tabs.general.update-channel": {
"message": "Perbarui kanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Versi rilisan, beta, dan alpha akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Versi beta dan alpha akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Rilisan"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Hanya versi rilisan yang akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Pilih kanal pembaruan"
},
"instance.settings.tabs.hooks": {
"message": "Luncurkan kait"
},
+73 -37
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Scaricando l'aggiornamento"
},
"app.action-bar.downloads": {
"message": "Download"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primaria"
},
"app.action-bar.reload-to-update": {
"message": "Ricarica per aggiornare"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostra più istanze in esecuzione"
},
"app.action-bar.stop-instance": {
"message": "Ferma l'istanza"
},
"app.action-bar.update": {
"message": "Aggiorna"
},
"app.action-bar.view-active-downloads": {
"message": "Mostra i download attivi"
},
@@ -86,14 +95,20 @@
"app.appearance-settings.select-option": {
"message": "Seleziona"
},
"app.appearance-settings.show-play-time.description": {
"message": "Mostra quanto tempo hai speso giocando a un'istanza."
},
"app.appearance-settings.show-play-time.title": {
"message": "Mostra le ore di gioco"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Scegli se mostrare o nascondere la barra laterale."
"message": "Mostra o nascondi la barra laterale."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Abilita la barra laterale"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Visti i rischi, apparirà un avviso prima di installare un file Modrinth Pack (.mrpack) non proveniente da Modrinth."
"message": "Mostrerà un avviso prima di installare un file Modrinth Pack (.mrpack) non proveniente da Modrinth, a causa dei possibili rischi."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Avvisami se installo pacchetti sconosciuti"
@@ -138,11 +153,14 @@
"message": "Pacchetti di mod"
},
"app.browse.server-instance-content-warning": {
"message": "Aggiungere dei contenuti potrebbe portare problemi entrando nel server. Qualsiasi contenuto aggiunto sarà anche perso aggiornando i contenuti dell'istanza del server."
"message": "Aggiungere dei contenuti potrebbe causare problemi entrando nel server. Inoltre, qualsiasi contenuto aggiunto sarà perso quando aggiorni i contenuti dell'istanza."
},
"app.browse.server.installing": {
"message": "Installazione"
},
"app.content-install.no-compatible-versions": {
"message": "Nessuna versione disponibile soddisfa {compatibilityLabel}. Puoi selezionare una versione da installare comunque. Le dipendenze non verranno scaricate automaticamente."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -206,9 +224,6 @@
"app.instance.mods.project-was-added": {
"message": "\"{name}\" è stato aggiunto"
},
"app.instance.mods.projects-were-added": {
"message": "{count} progetti aggiunti"
},
"app.instance.mods.share-text": {
"message": "Guarda le mod che uso nel mio pacchetto!"
},
@@ -263,6 +278,9 @@
"app.modal.install-to-play.install-button": {
"message": "Installa"
},
"app.modal.install-to-play.mod-count": {
"message": "{count} mod"
},
"app.modal.install-to-play.required-modpack": {
"message": "Pacchetto di mod richiesto"
},
@@ -324,7 +342,7 @@
"message": "Nuova skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Trascina e rilascia"
"message": "Trascina qui"
},
"app.skins.apply-button": {
"message": "Applica"
@@ -339,7 +357,7 @@
"message": "Vuoi davvero eliminare questa skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Non è stato possibile leggere il file rilasciato."
"message": "Non è stato possibile leggere il file trascinato."
},
"app.skins.dropped-file-error.title": {
"message": "Impossibile processare il file"
@@ -348,13 +366,13 @@
"message": "Modifica"
},
"app.skins.modal.add-skin-button": {
"message": "Salva"
"message": "Aggiungi"
},
"app.skins.modal.add-title": {
"message": "Aggiungi una nuova skin"
"message": "Aggiungi una skin"
},
"app.skins.modal.arm-style-section": {
"message": "Modello del giocatore"
"message": "Modello"
},
"app.skins.modal.arm-style-slim": {
"message": "Snello"
@@ -372,7 +390,7 @@
"message": "Modifica la skin"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Modifica la skin prima!"
"message": "Modifica prima la skin!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Nessun mantello"
@@ -381,19 +399,19 @@
"message": "Nessuno"
},
"app.skins.modal.replace-texture-button": {
"message": "Sfoglia"
"message": "Cambia"
},
"app.skins.modal.save-skin-button": {
"message": "Salva"
},
"app.skins.modal.saving-tooltip": {
"message": "Salvataggio..."
"message": "Salvando..."
},
"app.skins.modal.texture-section": {
"message": "File della skin"
"message": "Aspetto"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Carica una skin prima!"
"message": "Carica prima una skin!"
},
"app.skins.preview.edit-button": {
"message": "Modifica"
@@ -402,11 +420,17 @@
"message": "Anteprima"
},
"app.skins.rate-limit.text": {
"message": "Stai modificando la skin troppo spesso. I server di Mojang hanno limitato le nostre richieste. Aspetta un momento prima di riprovare."
"message": "Stai cambiando skin troppo spesso. I server di Mojang hanno limitato le nostre richieste. Aspetta un momento prima di riprovare."
},
"app.skins.rate-limit.title": {
"message": "Rallenta!"
},
"app.skins.reorder-error.text": {
"message": "L'ordine delle skin non è stato salvato."
},
"app.skins.reorder-error.title": {
"message": "Errore nel riordine delle skin"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -432,7 +456,7 @@
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Raccolta"
"message": "Skin salvate"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
@@ -450,20 +474,17 @@
"message": "Accedi"
},
"app.skins.sign-in.description": {
"message": "Accedi col tuo account Minecraft per usare la gestione delle skin."
"message": "Accedi col tuo account Minecraft per poter gestire le skin qui."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Modrinth Bot emozionato"
},
"app.skins.sign-in.title": {
"message": "Accedi"
"message": "Effettua l'accesso"
},
"app.skins.title": {
"message": "Seleziona una skin"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} è pronta per essere installata! Ricarica per aggiornare ora, o avverrà in automatico alla chiusura dell'app."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} è stata scaricata. Ricarica per aggiornare ora, o avverrà in automatico alla chiusura dell'app."
},
@@ -494,15 +515,6 @@
"app.update.complete-toast.title": {
"message": "La versione {version} è stata installata con successo!"
},
"app.update.download-update": {
"message": "Scarica aggiornamento"
},
"app.update.downloading-update": {
"message": "Scaricando l'aggiornamento ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Ricarica per installare l'aggiornamento"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -525,7 +537,7 @@
"message": "Stringi un'amicizia"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, one {richiesta} other {richieste}} d''amicizia"
"message": "{count, plural, one {# richiesta} other {# richieste}} d''amicizia"
},
"friends.add-friend.submit": {
"message": "Invia richiesta d'amicizia"
@@ -692,6 +704,30 @@
"instance.settings.tabs.general.name": {
"message": "Nome"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canale di aggiornamento"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Le versioni stabili, beta e alpha saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Le versioni stabili e beta saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stabile"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Solo le versioni stabili saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Seleziona il canale di aggiornamento"
},
"instance.settings.tabs.hooks": {
"message": "Hook di avvio"
},
@@ -741,16 +777,16 @@
"message": "Java e memoria"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Variabili d'ambiente"
"message": "Personalizzate"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Argomenti JVM"
"message": "Personalizzati"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Eseguibile di Java"
"message": "Personalizzata"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Memoria allocata"
"message": "Personalizzata"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Inserisci le variabili d'ambiente..."
+86 -23
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} をダウンロード中"
},
"app.action-bar.downloading-update": {
"message": "アップデートをダウンロード中"
},
"app.action-bar.downloads": {
"message": "ダウンロード"
},
@@ -42,7 +45,7 @@
"message": "高度なレンダリング"
},
"app.appearance-settings.color-theme.description": {
"message": "Modrinth Appでのお好みのテーマを選択してください"
"message": "Modrinth Appでのお好みのテーマを選択してください"
},
"app.appearance-settings.color-theme.title": {
"message": "テーマ"
@@ -60,25 +63,25 @@
"message": "デフォルトの起動ページ"
},
"app.appearance-settings.hide-nametag.description": {
"message": "スキンページでプレイヤー名の上にある名前タグを無効にします。"
"message": "スキンページでプレイヤー上部のネームタグを非表示にします。"
},
"app.appearance-settings.hide-nametag.title": {
"message": "名札を非表示にする"
"message": "ネームタグを非表示にする"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "ホームページの「ジャンプバック」セクションには、最近追加されたワールドが含まれています。"
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "再び世界へ飛び込もう"
"message": "最近のワールドを表示"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minecraftが起動した時、ランチャーを最小化します"
"message": "Minecraftが起動した時、ランチャーを最小化します"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "ランチャーを最小化"
},
"app.appearance-settings.native-decorations.description": {
"message": "OSデフォルトのウィンドウUIを使用します(アプリの再起動が必要です)"
"message": "OSデフォルトのウィンドウUIを使用します。(アプリの再起動が必要です"
},
"app.appearance-settings.native-decorations.title": {
"message": "OSのウィンドウUIを使用"
@@ -86,17 +89,23 @@
"app.appearance-settings.select-option": {
"message": "オプションを選択してください"
},
"app.appearance-settings.show-play-time.description": {
"message": "インスタンスのプレイ時間を表示します。"
},
"app.appearance-settings.show-play-time.title": {
"message": "プレイ時間を表示"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "サイドバーの表示/非表示を切り替える機能を有効にします。"
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "サイドバーの表示/非表示を切り替える"
"message": "サイドバーの表示切替を有効化"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Modrinth上にホストされていないModrinth Packファイル.mrpack)をインストールしようとした場合、インストール前にそのリスクを理解していただけるよう確認します。"
"message": "Modrinthで配布されていないModrinth Pack.mrpack)をインストールする場合、事前にリスクについて警告します。"
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "未知のMODパックインストールする前に警告してください"
"message": "不明なModパックインストール前に警告する"
},
"app.auth-servers.unreachable.body": {
"message": "Minecraftの認証サーバーは現在停止している可能性があります。インターネット接続を確認し、しばらくしてからもう一度お試しください。"
@@ -254,6 +263,9 @@
"app.modal.install-to-play.install-button": {
"message": "インストール"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {#個のMod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "必須のModパック"
},
@@ -302,8 +314,65 @@
"app.settings.tabs.resource-management": {
"message": "リソース管理"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} のインストール準備が整いました!今すぐ更新するには再読み込みするか、Modrinth Appを閉じる際に自動更新されます。"
"app.skins.modal.arm-style-slim": {
"message": "スリム"
},
"app.skins.modal.arm-style-wide": {
"message": "ワイド"
},
"app.skins.modal.cape-fallback-name": {
"message": "マント"
},
"app.skins.modal.cape-section": {
"message": "マント"
},
"app.skins.modal.no-cape-tooltip": {
"message": "マントなし"
},
"app.skins.modal.replace-texture-button": {
"message": "置きかえる"
},
"app.skins.modal.texture-section": {
"message": "テクスチャ"
},
"app.skins.preview.edit-button": {
"message": "編集"
},
"app.skins.section.builders-and-biomes": {
"message": "ビルダーズ&バイオーム"
},
"app.skins.section.chase-the-skies": {
"message": "チェイス・ザ・スカイ"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth プライド"
},
"app.skins.section.mounts-of-mayhem": {
"message": "マウント・オブ・メイヘム"
},
"app.skins.section.saved-skins": {
"message": "保存済みのスキン"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "銅の時代"
},
"app.skins.section.the-garden-awakens": {
"message": "ガーデン・アウェイケンズ"
},
"app.skins.section.tiny-takeover": {
"message": "タイニー・テイクオーバー"
},
"app.skins.sign-in.rinthbot-alt": {
"message": "ワクワクしている Modrinth ボット"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} のダウンロードが完了しました。今すぐ更新するには再読み込みするか、Modrinth Appを閉じる際に自動更新されます。"
@@ -323,9 +392,6 @@
"app.update-popup.download-complete": {
"message": "ダウンロード完了"
},
"app.update-popup.reload": {
"message": "再読み込み"
},
"app.update-popup.title": {
"message": "アップデートが利用可能です"
},
@@ -335,15 +401,6 @@
"app.update.complete-toast.title": {
"message": "バージョン {version} が正常にインストールされました!"
},
"app.update.download-update": {
"message": "アップデートをダウンロード"
},
"app.update.downloading-update": {
"message": "アップデートをダウンロード中 ({percent}%)"
},
"app.update.reload-to-update": {
"message": "再起動して今すぐ更新"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -365,6 +422,9 @@
"friends.action.add-friend": {
"message": "フレンドを追加"
},
"friends.action.view-friend-requests": {
"message": "{count}件の友達リクエスト"
},
"friends.add-friend.submit": {
"message": "フレンド申請を送信"
},
@@ -593,6 +653,9 @@
"instance.settings.tabs.java.java-memory": {
"message": "メモリ割り当て"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/path/to/java"
},
"instance.settings.tabs.window": {
"message": "ウィンドウ"
},
+27 -15
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} 다운로드 중"
},
"app.action-bar.downloading-update": {
"message": "업데이트 다운로드중"
},
"app.action-bar.downloads": {
"message": "다운로드"
},
@@ -86,6 +89,12 @@
"app.appearance-settings.select-option": {
"message": "옵션 선택"
},
"app.appearance-settings.show-play-time.description": {
"message": "인스턴스의 플레이 시간을 표시합니다."
},
"app.appearance-settings.show-play-time.title": {
"message": "플레이 시간 표시"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "사이드바를 접거나 펼칠 수 있는 기능을 활성화합니다."
},
@@ -143,6 +152,9 @@
"app.browse.server.installing": {
"message": "설치 중"
},
"app.content-install.no-compatible-versions": {
"message": "{compatibilityLabel} 버전과 일치하는 버전이 없습니다. 설치할 버전을 선택하세요. 종속성은 자동으로 설치되지 않습니다."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -431,6 +443,15 @@
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth 프라이드"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "프라이드 월간 동안 Modrinth 프라이드 모금 행사에 기부하여 이 스킨들을 받았습니다."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
@@ -455,15 +476,15 @@
"app.skins.sign-in.description": {
"message": "Modrinth 앱의 스킨 관리 기능을 사용하려면 Minecraft 계정에 로그인하세요."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Excited Modrinth Bot"
},
"app.skins.sign-in.title": {
"message": "로그인하세요"
},
"app.skins.title": {
"message": "스킨 선택"
},
"app.update-popup.body": {
"message": "Modrinth App v{version}을 설치할 준비가 완료되었습니다! 새로고침하거나 Modrinth App을 종료하면 자동으로 업데이트됩니다."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} 다운로드가 완료되었습니다. 새로고침하거나 Modrinth App을 종료하면 자동으로 업데이트됩니다."
},
@@ -482,9 +503,6 @@
"app.update-popup.download-complete": {
"message": "다운로드 완료"
},
"app.update-popup.reload": {
"message": "새로고침"
},
"app.update-popup.title": {
"message": "업데이트 가능"
},
@@ -494,15 +512,6 @@
"app.update.complete-toast.title": {
"message": "{version} 버전이 성공적으로 설치되었습니다!"
},
"app.update.download-update": {
"message": "업데이트 다운로드"
},
"app.update.downloading-update": {
"message": "업데이트 다운로드 중 ({percent}%)"
},
"app.update.reload-to-update": {
"message": "새로고침하여 업데이트 설치"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -524,6 +533,9 @@
"friends.action.add-friend": {
"message": "친구 추가"
},
"friends.action.view-friend-requests": {
"message": "{count}건의 친구 요청"
},
"friends.add-friend.submit": {
"message": "친구 요청 보내기"
},
+116 -14
View File
@@ -143,6 +143,9 @@
"app.browse.server.installing": {
"message": "Sedang memasang"
},
"app.content-install.no-compatible-versions": {
"message": "Tiada versi tersedia yang sepadan dengan {compatibilityLabel}. Pilih versi untuk dipasang juga. Kebergantungan tidak akan dipasang secara automatik."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -272,6 +275,9 @@
"app.modal.install-to-play.install-button": {
"message": "Pasang"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Pek mod yang diperlukan"
},
@@ -329,8 +335,110 @@
"app.settings.tabs.resource-management": {
"message": "Pengurusan sumber"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} sudah bersedia untuk dipasang! Muat semula untuk kemas kini sekarang, atau kemas kini secara automatik apabila anda menutup Modrinth App."
"app.skins.add-button": {
"message": "Tambah kekulit"
},
"app.skins.apply-button": {
"message": "Terapkan"
},
"app.skins.delete-button": {
"message": "Padam kekulit"
},
"app.skins.delete-modal.description": {
"message": "Ini akan memadamkan kekulit yang dipilih selama-lamanya. Tindakan ini tidak boleh dibuat asal."
},
"app.skins.edit-button": {
"message": "Sunting kekulit"
},
"app.skins.modal.add-skin-button": {
"message": "Tambah kekulit"
},
"app.skins.modal.arm-style-section": {
"message": "Gaya lengan"
},
"app.skins.modal.arm-style-slim": {
"message": "Kurus"
},
"app.skins.modal.arm-style-wide": {
"message": "Lebar"
},
"app.skins.modal.cape-fallback-name": {
"message": "Mantel"
},
"app.skins.modal.cape-section": {
"message": "Mantel"
},
"app.skins.modal.edit-title": {
"message": "Menyunting kekulit"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Tiada mantel"
},
"app.skins.modal.none-cape-option": {
"message": "Tiada"
},
"app.skins.modal.save-skin-button": {
"message": "Simpan kekulit"
},
"app.skins.modal.saving-tooltip": {
"message": "Sedang menyimpan..."
},
"app.skins.modal.texture-section": {
"message": "Tekstur"
},
"app.skins.preview.edit-button": {
"message": "Sunting kekulit"
},
"app.skins.previewing-badge": {
"message": "Sedang pralihat"
},
"app.skins.rate-limit.text": {
"message": "Anda terlalu kerap menukar kekulit anda. Pelayan Mojang telah menyekat permintaan selanjutnya buat sementara waktu. Sila tunggu sebentar sebelum mencuba lagi."
},
"app.skins.rate-limit.title": {
"message": "Perlahanlah sikit!"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Anda menerima kekulit ini kerana menderma kepada acara pengumpulan dana Modrinth Pride semasa Pride Month."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Kekulit yang disimpan"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Daftar Masuk"
},
"app.skins.sign-in.description": {
"message": "Sila daftar masuk ke akaun Minecraft anda untuk menggunakan ciri pengurusan kekulit milik Modrinth App."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Bot Modrinth yang Teruja"
},
"app.skins.sign-in.title": {
"message": "Sila daftar masuk"
},
"app.skins.title": {
"message": "Pemilih kekulit"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} sudah selesai dimuat turun! Muat semula untuk kemas kini sekarang, atau kemas kini secara automatik apabila anda menutup Modrinth App."
@@ -350,9 +458,6 @@
"app.update-popup.download-complete": {
"message": "Muat turun selesai"
},
"app.update-popup.reload": {
"message": "Muat semula"
},
"app.update-popup.title": {
"message": "Kemas kini tersedia"
},
@@ -362,15 +467,6 @@
"app.update.complete-toast.title": {
"message": "Versi {version} telah berjaya dipasang!"
},
"app.update.download-update": {
"message": "Muat turun kemas kini"
},
"app.update.downloading-update": {
"message": "Sedang memuat turun kemas kini ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Muat semula untuk memasang kemas kini"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -392,6 +488,9 @@
"friends.action.add-friend": {
"message": "Tambah rakan"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, other {permintaan}} rakan"
},
"friends.add-friend.submit": {
"message": "Hantar permintaan rakan"
},
@@ -617,6 +716,9 @@
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Peruntukan ingatan tersuai"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Masukkan pembolehubah persekitaran..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Masukkan argumen Java..."
},
+216 -12
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} aan het downloaden"
},
"app.action-bar.downloading-update": {
"message": "Update aan het downloaden"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primaire instantie"
},
"app.action-bar.reload-to-update": {
"message": "Herlaadt om te updaten"
},
"app.action-bar.show-more-running-instances": {
"message": "Toon meer actieve instanties"
},
"app.action-bar.stop-instance": {
"message": "Stop instantie"
},
"app.action-bar.update": {
"message": "Updaten"
},
"app.action-bar.view-active-downloads": {
"message": "Bekijk actieve downloads"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Selecteer een optie"
},
"app.appearance-settings.show-play-time.description": {
"message": "Toont hoeveel tijd je aan een instantie hebt besteed."
},
"app.appearance-settings.show-play-time.title": {
"message": "Speeltijd laten zien"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Schakelt de mogelijkheid om de zijbalk in- of uit te schakelen in."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Installeren"
},
"app.content-install.no-compatible-versions": {
"message": "Er zijn geen beschikbare versies die overeenkomen met {compatibilityLabel}. Selecteer toch een versie om te installeren. Afhankelijkheden worden niet automatisch geïnstalleerd."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -254,6 +272,9 @@
"app.instance.worlds.remove-server-title": {
"message": "Ben je zeker dat je {name} wil verwijderen?"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Zoek werelden"
},
"app.instance.worlds.this-server": {
"message": "deze server"
},
@@ -296,6 +317,9 @@
"app.project.install-button.already-installed": {
"message": "Dit project is al geïnstalleerd"
},
"app.project.install-context.back-to-browse": {
"message": "Terug naar Ontdekken"
},
"app.project.install-context.install-content-to-instance": {
"message": "Installeer content naar instantie"
},
@@ -323,8 +347,152 @@
"app.settings.tabs.resource-management": {
"message": "Bronnenbeheer"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} is klaar om geïnstalleerd te worden! Herlaad om nu te updaten, of automatisch wanneer je de Modrinth App afsluit."
"app.skins.add-button": {
"message": "Skin toevoegen"
},
"app.skins.add-button.drag-and-drop": {
"message": "Slepen en neerzetten"
},
"app.skins.apply-button": {
"message": "Toepassen"
},
"app.skins.delete-button": {
"message": "Skin verwijderen"
},
"app.skins.delete-modal.description": {
"message": "De geselecteerde skin wordt permanent verwijderd. Deze actie kan niet ongedaan worden gemaakt."
},
"app.skins.delete-modal.title": {
"message": "Weet u zeker dat u deze skin wilt verwijderen?"
},
"app.skins.dropped-file-error.text": {
"message": "Kan het neergezette bestand niet lezen."
},
"app.skins.dropped-file-error.title": {
"message": "Fout bij het verwerken van het bestand"
},
"app.skins.edit-button": {
"message": "Skin aanpassen"
},
"app.skins.modal.add-skin-button": {
"message": "Skin toevoegen"
},
"app.skins.modal.add-title": {
"message": "Voeg een skin toe"
},
"app.skins.modal.arm-style-section": {
"message": "Armstijl"
},
"app.skins.modal.arm-style-slim": {
"message": "Slank"
},
"app.skins.modal.arm-style-wide": {
"message": "Breed"
},
"app.skins.modal.cape-fallback-name": {
"message": "Cape"
},
"app.skins.modal.cape-section": {
"message": "Cape"
},
"app.skins.modal.edit-title": {
"message": "Skin aan het aanpassen"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Verander eerst iets aan de skin!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Geen cape"
},
"app.skins.modal.none-cape-option": {
"message": "Geen"
},
"app.skins.modal.replace-texture-button": {
"message": "Texture vervangen"
},
"app.skins.modal.save-skin-button": {
"message": "Skin opslaan"
},
"app.skins.modal.saving-tooltip": {
"message": "Opslaan..."
},
"app.skins.modal.texture-section": {
"message": "Textuur"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Upload eerst een skin!"
},
"app.skins.preview.edit-button": {
"message": "Skin aanpassen"
},
"app.skins.previewing-badge": {
"message": "Voorvertonen"
},
"app.skins.rate-limit.text": {
"message": "U verandert te snel van skin. Mojang's servers hebben nieuwe aanvragen tijdelijk geblokkeerd. Wacht een moment voordat u het opnieuw probeert."
},
"app.skins.rate-limit.title": {
"message": "Rustig aan!"
},
"app.skins.reorder-error.text": {
"message": "Je skinvolgorde kon niet worden opgeslagen."
},
"app.skins.reorder-error.title": {
"message": "Het herschikken van skins is mislukt."
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Standaardskins"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "U heeft deze skins ontvangen omdat u tijdens Pride Month heeft gedoneerd aan een Modrinth Pride-inzamelingsactie."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Opgeslagen skins"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Inloggen"
},
"app.skins.sign-in.description": {
"message": "Log in op je Minecraft-account om de skinbeheerfuncties van de Modrinth-app te gebruiken."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Excited Modrinth Bot"
},
"app.skins.sign-in.title": {
"message": "Log in"
},
"app.skins.title": {
"message": "Skinkiezer"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} is klaar met downloaden. Herlaad om nu te updaten, of automatisch wanneer je de Modrinth App afsluit."
@@ -345,7 +513,7 @@
"message": "Downloaden voltooid"
},
"app.update-popup.reload": {
"message": "Herlaad"
"message": "Herladen om te updaten"
},
"app.update-popup.title": {
"message": "Update beschikbaar"
@@ -356,15 +524,6 @@
"app.update.complete-toast.title": {
"message": "Versie {version} is succesvol geïnstalleerd!"
},
"app.update.download-update": {
"message": "Download update"
},
"app.update.downloading-update": {
"message": "Update downloaden ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Herlaad om de update te installeren"
},
"app.world.server-modal.placeholder-address": {
"message": "voorbeeld.modrinth.gg"
},
@@ -554,6 +713,30 @@
"instance.settings.tabs.general.name": {
"message": "Naam"
},
"instance.settings.tabs.general.update-channel": {
"message": "Updatekanaal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release-, beta- en alfaversies worden als beschikbare updates weergegeven."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release- en beta-versies worden als beschikbare updates weergegeven.\n"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Alleen releaseversies worden als beschikbare updates weergegeven."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Selecteer updatekanaal"
},
"instance.settings.tabs.hooks": {
"message": "Opstart haakjes"
},
@@ -602,6 +785,24 @@
"instance.settings.tabs.java": {
"message": "Java en geheugen"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Aangepaste omgevingsvariabelen"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Aangepaste Java-argumenten"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Aangepaste Java-installatie"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Aangepaste geheugentoewijzing"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Voer omgevingsvariabelen in…"
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Voer Java-argumenten in…"
},
"instance.settings.tabs.java.environment-variables": {
"message": "Environment variabelen"
},
@@ -617,6 +818,9 @@
"instance.settings.tabs.java.java-memory": {
"message": "Voorgeschreven geheugen"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "//pad/naar/java"
},
"instance.settings.tabs.window": {
"message": "Venster"
},
@@ -59,15 +59,6 @@
"app.update.complete-toast.title": {
"message": "Versjon {version} ble installert!"
},
"app.update.download-update": {
"message": "Last ned oppdatering"
},
"app.update.downloading-update": {
"message": "Laster ned oppdatering ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Last inn på nytt for å installere oppdateringen"
},
"friends.action.add-friend": {
"message": "Legg til en venn"
},
+143 -23
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Pobieranie Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Pobieranie aktualizacji"
},
"app.action-bar.downloads": {
"message": "Pobrania"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Główna instancja"
},
"app.action-bar.reload-to-update": {
"message": "Załaduj ponownie, by zaktualizować"
},
"app.action-bar.show-more-running-instances": {
"message": "Pokaż więcej włączonych instancji"
},
"app.action-bar.stop-instance": {
"message": "Zatrzymaj instancję"
},
"app.action-bar.update": {
"message": "Zaktualizuj"
},
"app.action-bar.view-active-downloads": {
"message": "Pokaż aktualnie pobierane"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Wybierz opcję"
},
"app.appearance-settings.show-play-time.description": {
"message": "Pokazuje czas gry spędzony w danej instancji."
},
"app.appearance-settings.show-play-time.title": {
"message": "Pokaż czas gry"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Umożliwia przełączenie paska bocznego."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Instalowanie"
},
"app.content-install.no-compatible-versions": {
"message": "Nie znaleziono żadnych wersji pasujących do {compatibilityLabel}. Wybierz wersję, by zainstalować ją mimo tego. Zależności nie zostaną zainstalowane automatycznie."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -261,7 +279,7 @@
"message": "ten serwer"
},
"app.modal.install-to-play.content-required": {
"message": "Wymagane treści"
"message": "Wymagana zawartość"
},
"app.modal.install-to-play.header": {
"message": "Zainstaluj, aby grać"
@@ -269,6 +287,9 @@
"app.modal.install-to-play.install-button": {
"message": "Zainstaluj"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} few {# mody} other {# modów}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Wymagana paczka modów"
},
@@ -327,13 +348,28 @@
"message": "Zarządzanie zasobami"
},
"app.skins.add-button": {
"message": "Dodaj skina"
"message": "Dodaj skórkę"
},
"app.skins.add-button.drag-and-drop": {
"message": "Przeciągnij i upuść"
},
"app.skins.apply-button": {
"message": "Zastosuj"
},
"app.skins.delete-button": {
"message": "Usuń skina"
"message": "Usuń skórkę"
},
"app.skins.delete-modal.description": {
"message": "To trwale usunie wybraną skórkę. Tej akcji nie można odwrócić."
},
"app.skins.delete-modal.title": {
"message": "Czy na pewno chcesz usunąć tą skórkę?"
},
"app.skins.dropped-file-error.text": {
"message": "Nie udało się odczytać wybranego pliku."
},
"app.skins.dropped-file-error.title": {
"message": "Błąd podczas przetwarzania pliku"
},
"app.skins.edit-button": {
"message": "Edytuj skina"
@@ -344,20 +380,35 @@
"app.skins.modal.add-title": {
"message": "Dodawanie skina"
},
"app.skins.modal.arm-style-section": {
"message": "Styl ramion"
},
"app.skins.modal.arm-style-slim": {
"message": "Smukły"
},
"app.skins.modal.arm-style-wide": {
"message": "Szeroki"
},
"app.skins.modal.cape-fallback-name": {
"message": "Peleryna"
},
"app.skins.modal.cape-section": {
"message": "Pelerynki"
"message": "Peleryny"
},
"app.skins.modal.edit-title": {
"message": "Edytuj skina"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Wpierw zmień jakoś skórkę!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Brak peleryny"
},
"app.skins.modal.none-cape-option": {
"message": "Nic"
"message": "Brak"
},
"app.skins.modal.replace-texture-button": {
"message": "Zamień teksturę"
},
"app.skins.modal.save-skin-button": {
"message": "Zapisz skina"
@@ -368,29 +419,80 @@
"app.skins.modal.texture-section": {
"message": "Tekstura"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Wpierw prześlij skórkę!"
},
"app.skins.preview.edit-button": {
"message": "Edytuj skina"
"message": "Edytuj skórkę"
},
"app.skins.previewing-badge": {
"message": "Podgląd"
},
"app.skins.rate-limit.text": {
"message": "Zbyt często zmieniasz swoją skórkę. Serwery Mojang tymczasowo zablokowały dalsze żądania. Odczekaj chwilę i spróbuj ponownie."
},
"app.skins.rate-limit.title": {
"message": "Zwolnij!"
},
"app.skins.reorder-error.text": {
"message": "Nie udało się zapisać kolejności skórek."
},
"app.skins.reorder-error.title": {
"message": "Nie udało się zmienić kolejności skórek"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Domyślne skiny"
"message": "Domyślne skórki"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Duma Modrinth"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Zdobyłeś te skórki poprzez dotację na zbiórkę Dumy Modrinth w okresie Miesiąca Dumy."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Zapisane skórki"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Zaloguj Się"
"message": "Zaloguj się"
},
"app.skins.sign-in.description": {
"message": "Zaloguj się na konto Minecraft, by móc zarządzać skórkami poprzez Modrinth App."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Podekscytowany Modrinth Bot"
},
"app.skins.sign-in.title": {
"message": "Proszę zaloguj się"
"message": "Zaloguj się"
},
"app.skins.title": {
"message": "Wybierz skina"
},
"app.update-popup.body": {
"message": "Wersja Modrinth App v{version} jest gotowa do zainstalowania! Załaduj ponownie, żeby zaktualizować teraz, albo automatycznie, gdy zamkniesz Modrinth App."
"message": "Wybierz skórkę"
},
"app.update-popup.body.download-complete": {
"message": "Wersja Modrinth App v{version} została pobrana. Załaduj ponownie, żeby zaktualizować teraz, albo automatycznie, gdy zamkniesz Modrinth App."
@@ -411,7 +513,7 @@
"message": "Pobieranie ukończone"
},
"app.update-popup.reload": {
"message": "Załaduj ponownie"
"message": "Załaduj ponownie, by zaktualizować"
},
"app.update-popup.title": {
"message": "Dostępna aktualizacja"
@@ -422,15 +524,6 @@
"app.update.complete-toast.title": {
"message": "Wersja {version} została pomyślnie zainstalowana!"
},
"app.update.download-update": {
"message": "Pobierz aktualizację"
},
"app.update.downloading-update": {
"message": "Pobieranie aktualizacji ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Załaduj ponownie, aby zainstalować aktualizację"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -452,6 +545,9 @@
"friends.action.add-friend": {
"message": "Dodaj znajomego"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, one {zaproszenie} few {zaproszenia} other {zaproszeń}} do znajomych"
},
"friends.add-friend.submit": {
"message": "Wyślij zaproszenie"
},
@@ -617,6 +713,30 @@
"instance.settings.tabs.general.name": {
"message": "Nazwa"
},
"instance.settings.tabs.general.update-channel": {
"message": "Kanał aktualizacji"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Wersje stabilne, beta i alfa będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Tylko wersje stabilne i wersje beta będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stabilny"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Tylko wersje stabilne będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Wybierz kanał aktualizacji"
},
"instance.settings.tabs.hooks": {
"message": "Haczyki startowe"
},
+151 -109
View File
@@ -1,6 +1,9 @@
{
"app.action-bar.downloading-java": {
"message": "Baixando java {version}"
"message": "Baixando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Baixando atualização"
},
"app.action-bar.downloads": {
"message": "Downloads"
@@ -9,23 +12,29 @@
"message": "Ocultar mais instâncias em execução"
},
"app.action-bar.make-primary-instance": {
"message": "Criar instância primária"
"message": "Tornar instância principal"
},
"app.action-bar.no-instances-running": {
"message": "Nenhuma instância em execução"
},
"app.action-bar.offline": {
"message": "Offline"
"message": "Inativo"
},
"app.action-bar.primary-instance": {
"message": "Instância primária"
"message": "Instância principal"
},
"app.action-bar.reload-to-update": {
"message": "Recarregar para atualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar mais instâncias em execução"
"message": "Exibir mais instâncias em execução"
},
"app.action-bar.stop-instance": {
"message": "Parar instância"
},
"app.action-bar.update": {
"message": "Atualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver downloads ativos"
},
@@ -33,22 +42,22 @@
"message": "Ver instância"
},
"app.action-bar.view-logs": {
"message": "Ver logs"
"message": "Ver registros"
},
"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."
"message": "Ativa a renderização avançada, como desfoque que talvez afete o desempenho sem renderização acelerada do 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."
"message": "Escolha seu tema de cores preferido para o Modrinth App."
},
"app.appearance-settings.color-theme.title": {
"message": "Tema de Cores"
"message": "Tema de cores"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Alterar a página em que o launcher é aberto."
"message": "Altera a página que o launcher é aberto."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Início"
@@ -60,25 +69,25 @@
"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."
"message": "Desativa o nametag acima do seu jogador na página de Skins."
},
"app.appearance-settings.hide-nametag.title": {
"message": "Ocultar nome"
"message": "Ocultar nametag"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "Inclui mundos recentes na seção \"Voltar à ação\" na página inicial."
"message": "Inclui mundos recentes na seção \"Retomar\" na página inicial."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "Volte aos mundos"
"message": "Retomar aos mundos"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minimize o launcher quando um processo do Minecraft for iniciado."
"message": "Minimize o launcher assim que algum processo do Minecraft iniciar."
},
"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)."
"message": "Usar molduras de janela do sistema (exige reinicialização do aplicativo)."
},
"app.appearance-settings.native-decorations.title": {
"message": "Decorações nativas"
@@ -86,14 +95,20 @@
"app.appearance-settings.select-option": {
"message": "Selecione uma opção"
},
"app.appearance-settings.show-play-time.description": {
"message": "Exibe a quantidade de tempo jogado de uma instância."
},
"app.appearance-settings.show-play-time.title": {
"message": "Exibir tempo jogado"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Ativa a capacidade de alternar a visibilidade da barra lateral."
"message": "Permite alternar a 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."
"message": "Se você tentar instalar um arquivo Modrinth Pack (.mrpack) não hospedado no Modrinth, garantiremos que você entenda os riscos antes de instalar."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Avise-me antes de instalar pacotes de mods desconhecidos"
@@ -123,13 +138,13 @@
"message": "Já adicionado"
},
"app.browse.back-to-instance": {
"message": "Voltar a instância"
"message": "Voltar à instância"
},
"app.browse.discover-content": {
"message": "Descubra conteúdo"
"message": "Descobrir conteúdo"
},
"app.browse.discover-servers": {
"message": "Descubra servidores"
"message": "Descobrir servidores"
},
"app.browse.hide-added-servers": {
"message": "Ocultar servidores já adicionados"
@@ -138,11 +153,14 @@
"message": "Pacotes de mods"
},
"app.browse.server-instance-content-warning": {
"message": "Adicionar conteúdo pode quebrar a compatibilidade ao entrar no servidor. Qualquer conteúdo adicionado também será perdido ao atualizar o conteúdo da instância do servidor."
"message": "Adicionar conteúdo talvez quebre a compatibilidade ao entrar no servidor. Qualquer conteúdo adicionado será perdido ao atualizar o conteúdo da instância do servidor."
},
"app.browse.server.installing": {
"message": "Instalando"
},
"app.content-install.no-compatible-versions": {
"message": "Nenhuma versão disponível corresponde a {compatibilityLabel}. Selecione uma versão para instalar mesmo assim. As dependências não serão instaladas automaticamente."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -168,7 +186,7 @@
"message": "Nome do pacote de mods"
},
"app.export-modal.select-files-label": {
"message": "Configure quais arquivos serão incluídos nessa exportação"
"message": "Configure quais arquivos serão incluídos na exportação"
},
"app.export-modal.version-number-label": {
"message": "Número da versão"
@@ -177,7 +195,7 @@
"message": "1.0.0"
},
"app.instance.confirm-delete.admonition-body": {
"message": "Todos os dados da sua instância serão permanentemente excluídos, incluindo seus mundos, configurações e todo o conteúdo instalado."
"message": "Todos os dados para sua instância serão excluídos permanentemente, incluindo seus mundos, configs e todo o conteúdo instalado."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Esta ação não pode ser desfeita"
@@ -189,7 +207,7 @@
"message": "Excluir instância"
},
"app.instance.modpack-already-installed.body": {
"message": "Este pacote de mods já está instalado na instância <bold>{instanceName}</bold>. Tem certeza de que deseja duplicá-lo?"
"message": "Este pacote de mods já foi instalado na instância <bold>{instanceName}</bold>. Deseja mesmo duplicá-lo?"
},
"app.instance.modpack-already-installed.create": {
"message": "Criar"
@@ -210,10 +228,10 @@
"message": "{count} projetos foram adicionados"
},
"app.instance.mods.share-text": {
"message": "Confira os projetos que estou usando no meu pacote de mods!"
"message": "Dê uma olhada nos projetos que estou usando no meu pacote de mods!"
},
"app.instance.mods.share-title": {
"message": "Compartilhando conteúdo do pacote de mods\n\n\n \t\t\t\t\t\t\n \t\t\t\t\t\t\t\n \t\t\t\t\t\t\n \t\t\t\t\t\t"
"message": "Compartilhando conteúdo do pacote de mods"
},
"app.instance.mods.successfully-uploaded": {
"message": "Adicionado com sucesso"
@@ -225,19 +243,19 @@
"message": "Explorar servidores"
},
"app.instance.worlds.delete-world-description": {
"message": "'{name}' será excluído permanentemente, e não haverá como recuperá-lo."
"message": "'{name}' será **excluído permanentemente**, e não poderá ser recuperado."
},
"app.instance.worlds.delete-world-title": {
"message": "Tem certeza de que deseja excluir este mundo permanentemente?"
"message": "Deseja mesmo excluir este mundo permanentemente?"
},
"app.instance.worlds.filter-modded": {
"message": "Modificado"
},
"app.instance.worlds.filter-offline": {
"message": "Offline"
"message": "Inativo"
},
"app.instance.worlds.filter-online": {
"message": "Online"
"message": "Ativo"
},
"app.instance.worlds.filter-vanilla": {
"message": "Jogo padrão"
@@ -249,13 +267,13 @@
"message": "Nenhum servidor ou mundo adicionado"
},
"app.instance.worlds.remove-server-description": {
"message": "'{name}' será removido da sua lista, inclusive no jogo, e não haverá como recuperá-lo."
"message": "'{name}' será removido da lista, inclusive no jogo, e não poderá ser recuperado."
},
"app.instance.worlds.remove-server-description-with-address": {
"message": "'{name}' ({address}) será removido da sua lista, inclusive no jogo, e não haverá como recuperá-lo."
"message": "'{name}' ({address}) será removido da lista, inclusive no jogo, e não poderá ser recuperado."
},
"app.instance.worlds.remove-server-title": {
"message": "Tem certeza de que deseja remover {name}?"
"message": "Deseja mesmo remover {name}?"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "Buscar {count} mundos..."
@@ -272,11 +290,14 @@
"app.modal.install-to-play.install-button": {
"message": "Instalar"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, =0 {Nenhum mod} one {# mod} other {# mods}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Pacote de mods necessário"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "Este servidor requer mods para jogar. Clique em Instalar para configurar os arquivos necessários a partir do Modrinth, e iniciar diretamente no servidor."
"message": "Este servidor exige mods para jogar. Clique em instalar para configurar os arquivos necessários através do Modrinth, e então iniciar diretamente no servidor."
},
"app.modal.install-to-play.shared-instance": {
"message": "Instância compartilhada"
@@ -285,7 +306,7 @@
"message": "Instância de servidor compartilhada"
},
"app.modal.install-to-play.view-contents": {
"message": "Ver conteúdos"
"message": "Ver conteúdo"
},
"app.modal.update-to-play.header": {
"message": "Atualize para jogar"
@@ -294,19 +315,19 @@
"message": "Atualização necessária"
},
"app.modal.update-to-play.update-required-description": {
"message": "É necessária uma atualização para jogar {name}. Atualize para a versão mais recente para iniciar o jogo."
"message": "Uma atualização é necessária para jogar {name}. Atualize para a versão mais recente para iniciar o jogo."
},
"app.project.install-button.already-installed": {
"message": "Este projeto já foi instalado"
},
"app.project.install-context.back-to-browse": {
"message": "Voltar ao descobrir"
"message": "Voltar a descobrir"
},
"app.project.install-context.install-content-to-instance": {
"message": "Instalar conteúdo para a instância"
},
"app.settings.developer-mode-enabled": {
"message": "Modo de desenvolvedor ativado."
"message": "Modo desenvolvedor ativado."
},
"app.settings.downloading": {
"message": "Baixando v{version}"
@@ -342,16 +363,16 @@
"message": "Excluir skin"
},
"app.skins.delete-modal.description": {
"message": "Isto permanentemente deletará a skin selecionada. Esta ação não pode ser desfeita."
"message": "Isso excluirá permanentemente a skin. Esta ação não pode ser desfeita."
},
"app.skins.delete-modal.title": {
"message": "Tem certeza de que deseja excluir esta skin?"
"message": "Deseja mesmo excluir esta skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Falha ao ler o arquivo solto."
},
"app.skins.dropped-file-error.title": {
"message": "Erro ao processar o arquivo"
"message": "Erro ao processar arquivo"
},
"app.skins.edit-button": {
"message": "Editar skin"
@@ -360,7 +381,7 @@
"message": "Adicionar skin"
},
"app.skins.modal.add-title": {
"message": "Adicionando skin"
"message": "Adicionando uma skin"
},
"app.skins.modal.arm-style-section": {
"message": "Estilo do braço"
@@ -375,13 +396,13 @@
"message": "Capa"
},
"app.skins.modal.cape-section": {
"message": "Capa"
"message": "Capas"
},
"app.skins.modal.edit-title": {
"message": "Editando skin"
"message": "Editando a skin"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Primeiro, faça uma edição na skin!"
"message": "Primeiramente, faça uma edição na skin!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Sem capa"
@@ -402,19 +423,25 @@
"message": "Textura"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Primeiro, faça o envio de uma skin!"
"message": "Primeiramente, envie uma skin!"
},
"app.skins.preview.edit-button": {
"message": "Editar skin"
},
"app.skins.previewing-badge": {
"message": "Pré-visualizando"
"message": "Prévia"
},
"app.skins.rate-limit.text": {
"message": "Você está trocando de skin com muita frequência. Os servidores da Mojang bloquearam temporariamente novas solicitações. Aguarde um momento antes de tentar novamente."
"message": "Você está mudando de skin muitas vezes. Os servidores da Mojang bloquearam temporariamente novas solicitações. Aguarde um momento antes de tentar novamente."
},
"app.skins.rate-limit.title": {
"message": "Vá mais devagar!"
"message": "Mais lento!"
},
"app.skins.reorder-error.text": {
"message": "Não foi possível salvar a ordem de skins."
},
"app.skins.reorder-error.title": {
"message": "Falhou ao reordenar skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Construtores & Biomas"
@@ -432,10 +459,10 @@
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Orgulho LGBTQIA+ do Modrinth"
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Você recebeu estas skins por doar para uma campanha de arrecadação de fundos do Orgulho LGBTQIA+ do Modrinth durante o Mês do Orgulho."
"message": "Você recebeu estas skins por doar para uma campanha de arrecadação do Modrinth Pride durante o Mês de Orgulho."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
@@ -447,7 +474,7 @@
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "A Era do Cobre"
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
@@ -456,31 +483,28 @@
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Fazer login"
"message": "Iniciar sessão"
},
"app.skins.sign-in.description": {
"message": "Faça login na sua conta do Minecraft para usar os recursos de gerenciamento de skins do Modrinth app."
"message": "Inicie sessão na sua conta do Minecraft para usar os recursos de gerenciamento de skins no Modrinth App."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Modrinth Bot animado"
},
"app.skins.sign-in.title": {
"message": "Por favor, faça login"
"message": "Por favor, inicie sessão"
},
"app.skins.title": {
"message": "Seletor de skin"
},
"app.update-popup.body": {
"message": "O Modrinth App v{version} está pronto para ser instalado! Você pode recarregar para atualizar agora ou a atualização será feita automaticamente ao fechar o Modrinth App."
"message": "Seletor de skins"
},
"app.update-popup.body.download-complete": {
"message": "O Modrinth App v{version} foi baixado. Recarregue para atualizar agora ou a atualização será aplicada automaticamente ao fechar o Modrinth App."
"message": "O Modrinth App v{version} foi instalado. Recarregue para atualizar agora ou ela será feita automaticamente ao fechar o Modrinth App."
},
"app.update-popup.body.linux": {
"message": "O Modrinth App v{version} está disponível. Use seu gerenciador de pacotes para atualizar e obter os recursos e correções mais recentes!"
"message": "O Modrinth App v{version} está disponível. Use o gerenciador de pacotes para atualizar para obter novos recursos e correções!"
},
"app.update-popup.body.metered": {
"message": "O Modrinth App v{version} já está disponível! Como você está em uma rede limitada, não o baixamos automaticamente."
"message": "O Modrinth App v{version} já está disponível! Já que sua rede é limitada, não a instalamos automaticamente."
},
"app.update-popup.changelog": {
"message": "Mudanças"
@@ -489,10 +513,10 @@
"message": "Baixar ({size})"
},
"app.update-popup.download-complete": {
"message": "Download concluído"
"message": "Instalação concluída"
},
"app.update-popup.reload": {
"message": "Recarregar"
"message": "Recarregar para atualizar"
},
"app.update-popup.title": {
"message": "Atualização disponível"
@@ -503,15 +527,6 @@
"app.update.complete-toast.title": {
"message": "Versão {version} instalada!"
},
"app.update.download-update": {
"message": "Baixar atualização"
},
"app.update.downloading-update": {
"message": "Baixando atualização ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Recarregue para instalar a atualização"
},
"app.world.server-modal.placeholder-address": {
"message": "exemplo.modrinth.gg"
},
@@ -525,22 +540,25 @@
"message": "Ainda não jogado"
},
"app.world.world-item.offline": {
"message": "Offline"
"message": "Inativo"
},
"app.world.world-item.players-online": {
"message": "{count} online"
"message": "{count} ativo(s)"
},
"friends.action.add-friend": {
"message": "Adicionar um amigo"
"message": "Adicionar amigo"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, one {pedido} other {pedidos}} de amizade"
},
"friends.add-friend.submit": {
"message": "Enviar amizade"
"message": "Enviar pedido de amizade"
},
"friends.add-friend.title": {
"message": "Adicionando amigo"
"message": "Adicionar um amigo"
},
"friends.add-friend.username.description": {
"message": "Pode ser diferente do nome que ele usa no Minecraft!"
"message": "Pode diferir do nome de usuário do Minecraft!"
},
"friends.add-friend.username.placeholder": {
"message": "Insira o nome de usuário..."
@@ -552,13 +570,13 @@
"message": "<link>Adicione amigos</link> para ver o que eles estão jogando!"
},
"friends.friend.cancel-request": {
"message": "Cancelar solicitação"
"message": "Cancelar pedido"
},
"friends.friend.remove-friend": {
"message": "Remover amigo"
},
"friends.friend.request-sent": {
"message": "Solicitação de amizade enviada"
"message": "Pedido de amizade enviado"
},
"friends.friend.view-profile": {
"message": "Ver perfil"
@@ -612,7 +630,7 @@
"message": "Editar servidor"
},
"instance.edit-world.hide-from-home": {
"message": "Esconder da página inicial"
"message": "Ocultar da página inicial"
},
"instance.edit-world.name": {
"message": "Nome"
@@ -654,7 +672,7 @@
"message": "Excluir instância"
},
"instance.settings.tabs.general.delete.description": {
"message": "Exclui permanentemente uma instância do seu dispositivo, incluindo seus mundos, configurações e todo o conteúdo instalado. Tome cuidado, pois, uma vez excluída, a instância não pode ser recuperada."
"message": "Exclui permanentemente uma instância do seu dispositivo, incluindo seus mundos, configs e todo o conteúdo instalado. Tome cuidado, assim que excluído, a instância não pode ser recuperada."
},
"instance.settings.tabs.general.deleting.button": {
"message": "Excluindo..."
@@ -669,7 +687,7 @@
"message": "Duplicar instância"
},
"instance.settings.tabs.general.duplicate-instance.description": {
"message": "Cria uma cópia desta instância, incluindo mundos, configurações, mods, etc."
"message": "Cria uma cópia desta instância, incluindo mundos, configs, mods, etc."
},
"instance.settings.tabs.general.edit-icon": {
"message": "Editar ícone"
@@ -687,7 +705,7 @@
"message": "Grupos"
},
"instance.settings.tabs.general.library-groups.create": {
"message": "Criar novo grupo"
"message": "Criar grupo novo"
},
"instance.settings.tabs.general.library-groups.description": {
"message": "Os grupos da biblioteca permitem que você organize suas instâncias em diferentes seções."
@@ -698,6 +716,30 @@
"instance.settings.tabs.general.name": {
"message": "Nome"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de atualização"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "As versões release, beta e alpha aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "As versões release e beta aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Apenas versões release aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Selecionar canal de atualização"
},
"instance.settings.tabs.hooks": {
"message": "Ações de inicialização"
},
@@ -705,7 +747,7 @@
"message": "Ações de inicialização personalizadas"
},
"instance.settings.tabs.hooks.description": {
"message": "Essas ações permitem que usuários mais experientes executem comandos do sistema antes e depois de inicializar o jogo."
"message": "As ações permitem usuários experientes que executem certos comandos do sistema antes ou após iniciar o jogo."
},
"instance.settings.tabs.hooks.post-exit": {
"message": "Ao sair"
@@ -714,16 +756,16 @@
"message": "Executado após o jogo fechar."
},
"instance.settings.tabs.hooks.post-exit.enter": {
"message": "Insira o comando a ser executado após o jogo fechar..."
"message": "Insira o comando ao sair..."
},
"instance.settings.tabs.hooks.pre-launch": {
"message": "Pré-inicialização"
"message": "Ao iniciar"
},
"instance.settings.tabs.hooks.pre-launch.description": {
"message": "Executado antes que a instância seja inicializada."
},
"instance.settings.tabs.hooks.pre-launch.enter": {
"message": "Insira o comando a ser executado antes da inicialização..."
"message": "Insira o comando ao iniciar..."
},
"instance.settings.tabs.hooks.title": {
"message": "Ações de inicialização do jogo"
@@ -735,13 +777,13 @@
"message": "Comando auxiliar para iniciar o Minecraft."
},
"instance.settings.tabs.hooks.wrapper.enter": {
"message": "Insira um comando..."
"message": "Insira um comando auxiliar..."
},
"instance.settings.tabs.installation": {
"message": "Instalação"
},
"instance.settings.tabs.installation.loader-version": {
"message": "Versão do {loader}"
"message": "Versão {loader}"
},
"instance.settings.tabs.java": {
"message": "Java e memória"
@@ -786,13 +828,13 @@
"message": "Janela"
},
"instance.settings.tabs.window.custom-window-settings": {
"message": "Configurações de janela personalizada"
"message": "Opções de janela personalizada"
},
"instance.settings.tabs.window.fullscreen": {
"message": "Tela cheia"
},
"instance.settings.tabs.window.fullscreen.description": {
"message": "Faz com que o jogo inicie em tela cheia (usando options.txt)."
"message": "Faz o jogo iniciar em tela cheia (usando options.txt)."
},
"instance.settings.tabs.window.height": {
"message": "Altura"
@@ -816,13 +858,13 @@
"message": "Servidor de Minecraft"
},
"instance.worlds.cant_connect": {
"message": "Não foi possível se conectar ao servidor"
"message": "Não foi possível conectar ao servidor"
},
"instance.worlds.copy_address": {
"message": "Copiar endereço"
},
"instance.worlds.dont_show_on_home": {
"message": "Não mostrar na tela inicial"
"message": "Não exibir na página inicial"
},
"instance.worlds.game_already_open": {
"message": "A instância já está aberta"
@@ -834,19 +876,19 @@
"message": "Servidor incompatível"
},
"instance.worlds.linked_server": {
"message": "Gerenciado pelo projeto de servidor"
"message": "Gerenciado pelo projeto do servidor"
},
"instance.worlds.no_contact": {
"message": "Não foi possível conectar-se ao servidor"
},
"instance.worlds.no_server_quick_play": {
"message": "Você só pode entrar diretamente em servidores a partir do Minecraft 1.0.5"
"message": "Só é possível entrar diretamente em servidores a partir do Minecraft Alpha 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": "Só é possível entrar em mundos um jogador diretamente a partir do Minecraft 1.20"
},
"instance.worlds.play_instance": {
"message": "Jogar na instância"
"message": "Iniciar instância"
},
"instance.worlds.view_instance": {
"message": "Ver instância"
@@ -858,7 +900,7 @@
"message": "Adicionar conta"
},
"minecraft-account.label": {
"message": "Conta do minecraft"
"message": "Conta de Minecraft"
},
"minecraft-account.not-signed-in": {
"message": "Não conectado"
@@ -870,7 +912,7 @@
"message": "Selecionar conta"
},
"minecraft-account.sign-in": {
"message": "Faça login no Minecraft"
"message": "Inicie sessão no Minecraft"
},
"search.filter.locked.instance": {
"message": "Fornecido pela instância"
@@ -879,7 +921,7 @@
"message": "A versão do jogo é fornecida pela instância"
},
"search.filter.locked.instance-loader.title": {
"message": "O carregador é fornecido pela instância"
"message": "O loader é fornecido pela instância"
},
"search.filter.locked.instance.sync": {
"message": "Sincronizar com a instância"
@@ -888,19 +930,19 @@
"message": "Fornecido pelo servidor"
},
"search.filter.locked.server-environment.title": {
"message": "Apenas mods do lado do cliente podem ser adicionados à instância do servidor"
"message": "Somente mods do cliente podem ser adicionados à instância do servidor"
},
"search.filter.locked.server-game-version.title": {
"message": "A versão do jogo é fornecida pelo servidor"
},
"search.filter.locked.server-loader.title": {
"message": "O carregador é fornecido pelo servidor"
"message": "O loader é 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)."
"message": "Um arquivo só é revisado se for enviado no Modrinth, independente do formato do arquivo (incluindo .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Não mostrar este aviso novamente"
"message": "Não exibir aviso novamente"
},
"unknown-pack-warning-modal.header": {
"message": "Confirmar instalação"
@@ -909,10 +951,10 @@
"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."
"message": "O malware é distribuído frequentemente através de arquivos de pacote de mods compartilhados em plataformas como Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Não foi possível encontrar este arquivo no Modrinth. Recomendamos fortemente que você instale apenas arquivos de fontes confiáveis."
"message": "Não encontramos este arquivo no Modrinth. Nós recomendamos fortemente instalar arquivos apenas de fontes confiáveis."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Aviso de arquivo desconhecido"
+29 -14
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "A descarregar a versão do java {version}"
},
"app.action-bar.downloading-update": {
"message": "A transferir atualização"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instância principal"
},
"app.action-bar.reload-to-update": {
"message": "Recarregar para atualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar mais instâncias a correr"
},
"app.action-bar.stop-instance": {
"message": "Para instância"
},
"app.action-bar.update": {
"message": "Atualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver instâncias ativas"
},
@@ -35,6 +44,9 @@
"app.action-bar.view-logs": {
"message": "Ver logs"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Ativa a renderização avançada, como efeitos de desfoque, que podem causar problemas de desempenho sem o uso de renderização acelerada por hardware."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "Renderização avançada"
},
@@ -56,6 +68,12 @@
"app.appearance-settings.default-landing-page.title": {
"message": "Página de carregamento principal"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimizar launcher"
},
"app.appearance-settings.select-option": {
"message": "Seleciona uma opção"
},
"app.auth-servers.unreachable.body": {
"message": "Os servidores de autenticação do Minecraft poderão estar em baixo de momento. Verifica a tua ligação à internet e tenta novamente mais tarde."
},
@@ -80,6 +98,9 @@
"app.browse.discover-servers": {
"message": "Descobrir servidores"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
},
"app.export-modal.description-placeholder": {
"message": "Insere a descrição do modpack..."
},
@@ -239,8 +260,14 @@
"app.settings.tabs.resource-management": {
"message": "Gestão de recursos"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} está pronta para ser instalada! Recarrega para atualizar agora, ou automaticamente quando fechares a Modrinth App."
"app.skins.delete-modal.title": {
"message": "Tens a certeza que queres apagar esta skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Não foi possível ler o ficheiro largado."
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} acabou de ser transferida. Recarrega para atualizar agora, ou automaticamente quando fechares a Modrinth App."
@@ -260,9 +287,6 @@
"app.update-popup.download-complete": {
"message": "Transferência concluída"
},
"app.update-popup.reload": {
"message": "Recarregar"
},
"app.update-popup.title": {
"message": "Atualização disponível"
},
@@ -272,15 +296,6 @@
"app.update.complete-toast.title": {
"message": "Versão {version} foi instalada com sucesso!"
},
"app.update.download-update": {
"message": "Transferir atualização"
},
"app.update.downloading-update": {
"message": "A transferir atualização ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Recarrega para instalar a atualização"
},
"app.world.server-modal.placeholder-address": {
"message": "exemplo.modrinth.gg"
},
@@ -65,9 +65,6 @@
"app.settings.tabs.resource-management": {
"message": "Administrare resurse"
},
"app.update-popup.body": {
"message": "Aplicația Modrinth v{version} este gata de instalat! Reîncărcați pentru a actualiza acum sau automat când închideți aplicația Modrinth."
},
"app.update-popup.body.download-complete": {
"message": "Aplicația Modrinth v{version} a terminat descărcarea. Reîncărcați pentru a actualiza acum sau automat când închideți aplicația Modrinth."
},
@@ -86,9 +83,6 @@
"app.update-popup.download-complete": {
"message": "Descărcare finalizată"
},
"app.update-popup.reload": {
"message": "Reîncărcați"
},
"app.update-popup.title": {
"message": "Actualizare disponibilă"
},
@@ -98,15 +92,6 @@
"app.update.complete-toast.title": {
"message": "Versiunea {version} a fost instalată cu succes!"
},
"app.update.download-update": {
"message": "Descarcă actualizarea"
},
"app.update.downloading-update": {
"message": "Se descarcă actualizarea ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Reîncarcă pentru a instala actualizarea"
},
"app.world.server-modal.select-an-option": {
"message": "Selectează o opțiune"
},
+62 -14
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Скачивание Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Скачивание"
},
"app.action-bar.downloads": {
"message": "Загрузки"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Основная сборка"
},
"app.action-bar.reload-to-update": {
"message": "Перезапустить"
},
"app.action-bar.show-more-running-instances": {
"message": "Показать другие активные сборки"
},
"app.action-bar.stop-instance": {
"message": "Остановить сборку"
},
"app.action-bar.update": {
"message": "Обновить"
},
"app.action-bar.view-active-downloads": {
"message": "Посмотреть текущие загрузки"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Выберите вариант"
},
"app.appearance-settings.show-play-time.description": {
"message": "Показывать время, проведённое в сборке."
},
"app.appearance-settings.show-play-time.title": {
"message": "Время игры"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Показывать кнопку скрытия боковой панели."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Установка"
},
"app.content-install.no-compatible-versions": {
"message": "Не найдено доступных версий, совместимых с {compatibilityLabel}. Выберите версию для установки вручную. Зависимости не будут установлены автоматически."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -257,6 +275,9 @@
"app.modal.install-to-play.install-button": {
"message": "Установить"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# мод} few {# мода} other {# модов}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Необходимая сборка"
},
@@ -396,11 +417,17 @@
"message": "Предпросмотр"
},
"app.skins.rate-limit.text": {
"message": "Слишком частая смена скина. Серверы Mojang временно заблокировали дальнейшие запросы. Подождите немного, прежде чем попробовать снова."
"message": "Вы слишком часто меняете скин. Серверы Mojang временно заблокировали дальнейшие запросы. Подождите немного, прежде чем попробовать снова."
},
"app.skins.rate-limit.title": {
"message": "Не так быстро!"
},
"app.skins.reorder-error.text": {
"message": "Ваш порядок скинов не удалось сохранить."
},
"app.skins.reorder-error.title": {
"message": "Не удалось изменить порядок скинов"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -416,6 +443,12 @@
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Эти скины получены за участие в сборе средств Modrinth Pride в месяц гордости."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
@@ -438,7 +471,7 @@
"message": "Войти"
},
"app.skins.sign-in.description": {
"message": "Чтобы использовать функции управления скинами Modrinth App, войдите в свою учётную запись Minecraft."
"message": "Пожалуйста, войдите в свой аккаунт Minecraft, чтобы воспользоваться функциями управления скинами в приложении Modrinth."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Восторженный Modrinth Bot"
@@ -449,9 +482,6 @@
"app.skins.title": {
"message": "Выбор скина"
},
"app.update-popup.body": {
"message": "Версия Modrinth App {version} готова к установке! Перезапустите приложение, чтобы обновить его, или оно обновится автоматически после закрытия."
},
"app.update-popup.body.download-complete": {
"message": "Скачивание версии Modrinth App {version} завершено. Перезапустите приложение, чтобы обновить его, или оно обновится автоматически после закрытия."
},
@@ -482,15 +512,6 @@
"app.update.complete-toast.title": {
"message": "Версия {version} успешно установлена!"
},
"app.update.download-update": {
"message": "Скачать обновление"
},
"app.update.downloading-update": {
"message": "Скачивание обновления ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Перезапустить и обновить"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -512,6 +533,9 @@
"friends.action.add-friend": {
"message": "Добавить в друзья"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, one {запрос} few {запроса} other {запросов}} дружбы"
},
"friends.add-friend.submit": {
"message": "Отправить запрос дружбы"
},
@@ -674,6 +698,30 @@
"instance.settings.tabs.general.name": {
"message": "Название"
},
"instance.settings.tabs.general.update-channel": {
"message": "Канал обновления"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Альфа"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Релизные, бета и альфа версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Бета"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Релизные и бета версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Релиз"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Только релизные версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Выберите канал обновления"
},
"instance.settings.tabs.hooks": {
"message": "Команды запуска"
},
+216 -18
View File
@@ -2,17 +2,20 @@
"app.action-bar.downloading-java": {
"message": "Laddar ner Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Laddar ner uppdatering"
},
"app.action-bar.downloads": {
"message": "Nedladdningar"
},
"app.action-bar.hide-more-running-instances": {
"message": "Göm fler pågående instanser"
"message": "Göm fler aktiva instanser"
},
"app.action-bar.make-primary-instance": {
"message": "Gör till primärinstans"
},
"app.action-bar.no-instances-running": {
"message": "Ingen pågående instans"
"message": "Ingen aktiva instans"
},
"app.action-bar.offline": {
"message": "Offline"
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primärinstans"
},
"app.action-bar.reload-to-update": {
"message": "Ladda om för att uppdatera"
},
"app.action-bar.show-more-running-instances": {
"message": "Visa fler pågående instanser"
"message": "Visa fler aktiva instanser"
},
"app.action-bar.stop-instance": {
"message": "Avsluta instans"
},
"app.action-bar.update": {
"message": "Uppdatera"
},
"app.action-bar.view-active-downloads": {
"message": "Visa aktiva nedladdningar"
},
@@ -47,15 +56,27 @@
"app.appearance-settings.color-theme.title": {
"message": "Färgtema"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Ändrar sidan som launchern öppnas på."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Hem"
},
"app.appearance-settings.default-landing-page.library": {
"message": "Bibliotek"
},
"app.appearance-settings.default-landing-page.title": {
"message": "Standardlandingssida"
},
"app.appearance-settings.hide-nametag.description": {
"message": "Inaktiverar namnskylten ovanför din spelare på utseende-sidan."
},
"app.appearance-settings.hide-nametag.title": {
"message": "Dölj namnskylt"
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "Hoppa tillbaka i världar"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimera programstartare"
},
@@ -68,12 +89,36 @@
"app.appearance-settings.select-option": {
"message": "Välj ett alternativ"
},
"app.appearance-settings.show-play-time.description": {
"message": "Visar hur mycket tid du spelat på en instans."
},
"app.appearance-settings.show-play-time.title": {
"message": "Visa speltid"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Låter dig växla sidofältet."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Växla sidofält"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Om du försöker installera en Modrinth Pack fil (.mrpack) som inte kommer från Modrinth ser vi till att du känner till riskerna innan du installerar den."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Varna mig innan jag installerar okända modpaket"
},
"app.auth-servers.unreachable.body": {
"message": "Minecrafts autentiseringsservrar kan vara nere just nu. Kontrollera din internetanslutning och försök igen senare."
},
"app.auth-servers.unreachable.header": {
"message": "Kan ej nå autentiseringsservrarna"
},
"app.browse.add-servers-to-instance": {
"message": "Lägger till server till instans"
},
"app.browse.add-to-an-instance": {
"message": "Lägg till i en instans"
},
"app.browse.add-to-instance": {
"message": "Lägg till i instans"
},
@@ -86,21 +131,33 @@
"app.browse.already-added": {
"message": "Redan tillagd"
},
"app.browse.back-to-instance": {
"message": "Tillbaka till instans"
},
"app.browse.discover-content": {
"message": "Upptäck innehåll"
},
"app.browse.discover-servers": {
"message": "Upptäck servrar"
},
"app.browse.hide-added-servers": {
"message": "Dölj servrar som redan är tillagda"
},
"app.browse.project-type.modpacks": {
"message": "Modpaket"
},
"app.browse.server.installing": {
"message": "Installerar"
},
"app.content-install.no-compatible-versions": {
"message": "Ingen tillgänglig version matchar {compatibilityLabel}. Välj en version att installera ändå. Beroenden kommer inte installeras automatiskt."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
"app.creation-modal.installing-modpack.title": {
"message": "Installerar modpaket..."
},
"app.export-modal.description-placeholder": {
"message": "Ange modpaketets beskrivning..."
},
@@ -119,6 +176,9 @@
"app.export-modal.modpack-name-placeholder": {
"message": "Modpaketets namn"
},
"app.export-modal.select-files-label": {
"message": "Konfigurera vilka filer som inkluderas i denna export"
},
"app.export-modal.version-number-label": {
"message": "Versionsnummer"
},
@@ -126,7 +186,7 @@
"message": "1.0.0"
},
"app.instance.confirm-delete.admonition-body": {
"message": "All data från din instans kommer att raderas permanent, inklusive dina världar, konfigurationer och allt installerat innehåll."
"message": "All data från din instans kommer permanent raderas, däribland dina världar, konfigurationer samt allt installerat innehåll."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Detta kan inte ogöras"
@@ -248,6 +308,12 @@
"app.modal.update-to-play.update-required-description": {
"message": "En uppdatering krävs för att spela {name}. Vänligen uppdatera till senaste version för att starta spelet."
},
"app.project.install-button.already-installed": {
"message": "Detta projekt är redan installerat"
},
"app.project.install-context.install-content-to-instance": {
"message": "Installera innehåll till instans"
},
"app.settings.developer-mode-enabled": {
"message": "Utvecklarläge aktiverat."
},
@@ -275,9 +341,27 @@
"app.skins.add-button": {
"message": "Lägg till utseende"
},
"app.skins.add-button.drag-and-drop": {
"message": "Dra och släpp"
},
"app.skins.apply-button": {
"message": "Tillämpa"
},
"app.skins.delete-button": {
"message": "Ta bort utseende"
},
"app.skins.delete-modal.description": {
"message": "Du kommer permanent radera det valda utseendet. Detta kan inte ångras."
},
"app.skins.delete-modal.title": {
"message": "Är du säker på att du vill radera utseendet?"
},
"app.skins.dropped-file-error.text": {
"message": "Misslyckades att läsa den släppta filen."
},
"app.skins.dropped-file-error.title": {
"message": "Fel med behandlingen av filen"
},
"app.skins.edit-button": {
"message": "Redigera utseende"
},
@@ -326,8 +410,68 @@
"app.skins.modal.texture-section": {
"message": "Textur"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} är redo att laddas ner! Ladda om för att uppdatera nu, eller automatiskt när du stänger Modrinth App."
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Ladda upp utseende först!"
},
"app.skins.preview.edit-button": {
"message": "Redigera utseende"
},
"app.skins.previewing-badge": {
"message": "Förhandsvisa"
},
"app.skins.rate-limit.text": {
"message": "Du ändrar ditt utseende för ofta. Mojangs servrar har temporärt blockerat ytterligare förfrågningar. Vänligen vänta en stund innan du försöker igen."
},
"app.skins.rate-limit.title": {
"message": "Sakta ner!"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Standard utseenden"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Du har fått dessa utseende eftersom du donerat till en Modrinth Pride insamling under pride månaden."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Sparade utseenden"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Logga in"
},
"app.skins.sign-in.description": {
"message": "Snälla logga in din Minecraft konto att använda hud hantering förmågor i Modrinth appen."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Exalterad Modrinth bot"
},
"app.skins.sign-in.title": {
"message": "Vänligen logga in"
},
"app.skins.title": {
"message": "Utseende väljare"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} har laddats ner. Ladda om för att uppdatera nu, eller automatiskt när du stänger Modrinth App."
@@ -348,7 +492,7 @@
"message": "Nedladdning slutförd"
},
"app.update-popup.reload": {
"message": "Ladda om"
"message": "Ladda om för att uppdatera"
},
"app.update-popup.title": {
"message": "Uppdatering tillgänglig"
@@ -359,15 +503,6 @@
"app.update.complete-toast.title": {
"message": "Version {version} har installerats!"
},
"app.update.download-update": {
"message": "Ladda ner uppdatering"
},
"app.update.downloading-update": {
"message": "Laddar ner uppdatering ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Ladda om för att installera uppdatering"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -557,6 +692,30 @@
"instance.settings.tabs.general.name": {
"message": "Namn"
},
"instance.settings.tabs.general.update-channel": {
"message": "Uppdateringskanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Utgåvor, beta och alfa versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Utgåvor och beta versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Utgåva"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Endast utgivna versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Välj uppdateringskanal"
},
"instance.settings.tabs.hooks": {
"message": "Startkrokar"
},
@@ -605,6 +764,24 @@
"instance.settings.tabs.java": {
"message": "Java och minne"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Anpassade miljövariabler"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Anpassade Java-argument"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Anpassad Java-installation"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Anpassad minnestilldelning"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Skriv in miljövariabler..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Skriv in Java-argument..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "Miljövariabler"
},
@@ -620,6 +797,9 @@
"instance.settings.tabs.java.java-memory": {
"message": "Tilldelat minne"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/sökväg/till/java"
},
"instance.settings.tabs.window": {
"message": "Fönster"
},
@@ -636,7 +816,7 @@
"message": "Höjd"
},
"instance.settings.tabs.window.height.description": {
"message": "Höjden på spel-fönstret vid uppstart."
"message": "Höjden på spelfönstret vid uppstart."
},
"instance.settings.tabs.window.height.enter": {
"message": "Ange höjd..."
@@ -645,7 +825,7 @@
"message": "Bredd"
},
"instance.settings.tabs.window.width.description": {
"message": "Bredden på spel-fönstret vid uppstart."
"message": "Bredden på spelfönstret vid uppstart."
},
"instance.settings.tabs.window.width.enter": {
"message": "Ange bredd..."
@@ -734,7 +914,25 @@
"search.filter.locked.server-loader.title": {
"message": "Loader tillhandahålls av servern"
},
"unknown-pack-warning-modal.body": {
"message": "En fil granskas bara om den laddas upp till Modrinth, oavsett dess filformat (däribland .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Visa inte denna varning igen"
},
"unknown-pack-warning-modal.header": {
"message": "Bekräfta installation"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Installera ändå"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Skadeprogram distribueras ofta via modpaketfiler genom att dela dem på plattformar som Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Vi kunde inte hitta filen på Modrinth. Vi rekommenderar starkt att endast installera filer från källor du litar på."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Okänd filvarning"
}
}
+204 -30
View File
@@ -1,36 +1,45 @@
{
"app.action-bar.downloading-java": {
"message": "ดาวน์โหลดเวอร์ชัน Java {version}"
"message": "กำลังดาวน์โหลด Java {version}"
},
"app.action-bar.downloading-update": {
"message": "กำลังดาวน์โหลดอัปเดต"
},
"app.action-bar.downloads": {
"message": "ดาวน์โหลด"
},
"app.action-bar.hide-more-running-instances": {
"message": "ซ่อนการดำเนินการโปรแกรมเบื้องหลัง"
"message": "ซ่อนอินสแตนซ์ที่ทำงานอยู่เพิ่มเติม"
},
"app.action-bar.make-primary-instance": {
"message": "กำหนดโปรแกรมหลัก"
"message": "กำหนดอินสแตนซ์หลัก"
},
"app.action-bar.no-instances-running": {
"message": "ไม่มีโปรแกรมใดดำเนินการ"
"message": "ไม่มีอินสแตนซ์ที่กำลังทำงาน"
},
"app.action-bar.offline": {
"message": "ออฟไลน์"
},
"app.action-bar.primary-instance": {
"message": "โปรแกรมหลัก"
"message": "อินสแตนซ์หลัก"
},
"app.action-bar.reload-to-update": {
"message": "รีโหลดเพื่ออัปเดต"
},
"app.action-bar.show-more-running-instances": {
"message": "แสดงการดำเนินการโปรแกรมเบื้องหลังเพิ่มเติม"
"message": "แสดงอินสแตนซ์ที่ทำงานอยู่เพิ่มเติม"
},
"app.action-bar.stop-instance": {
"message": "หยุดการทำงานโปรแกรม"
"message": "หยุดอินสแตนซ์"
},
"app.action-bar.update": {
"message": "อัปเดต"
},
"app.action-bar.view-active-downloads": {
"message": "ดูยอดการดาวน์โหลดที่ยังใช้งาน"
"message": "ดูการดาวน์โหลดที่กำลังดำเนินการอยู่"
},
"app.action-bar.view-instance": {
"message": "ดูโปรแกรม"
"message": "ดูอินสแตนซ์"
},
"app.action-bar.view-logs": {
"message": "ดู Log"
@@ -42,7 +51,7 @@
"message": "การเรนเดอร์ขั้นสูง"
},
"app.appearance-settings.color-theme.description": {
"message": "เลือกธีมสีที่คุณชื่นชอบสำหรับ Modrinth บนอุปกรณ์นี้"
"message": "เลือกธีมสีที่คุณต้องการสำหรับ Modrinth App"
},
"app.appearance-settings.color-theme.title": {
"message": "ธีมสี"
@@ -54,22 +63,22 @@
"message": "หน้าหลัก"
},
"app.appearance-settings.default-landing-page.library": {
"message": "รายการ"
"message": "ไลบรารี"
},
"app.appearance-settings.default-landing-page.title": {
"message": "หน้าเริ่มต้น"
},
"app.appearance-settings.hide-nametag.description": {
"message": "ปิดป้ายชื่อบนศีรษะของผู้เล่นในหน้าแสดงสกิน"
"message": "ซ่อนป้ายชื่อบนศีรษะของผู้เล่นในหน้าแสดงสกิน"
},
"app.appearance-settings.hide-nametag.title": {
"message": "ซ่อนป้ายชื่อ"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "การแสดงผลโลกล่าสุดที่ผู้เล่นล่ในส่วน \"กระโดดกลับเข้าไป\" นหน้าหลัก"
"message": "แสดงโลกที่เล่นล่าสุดในส่วน \"เล่นต่อ\" นหน้าหลัก"
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "กระโดดกลับเข้าไปในโลก"
"message": "เล่นต่อ"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "ย่อลันเชอร์เมื่อกระบวนการการรัน Minecraft เริ่มต้นแล้ว"
@@ -78,13 +87,19 @@
"message": "ย่อลันเชอร์"
},
"app.appearance-settings.native-decorations.description": {
"message": "ใช้ระบบกรอบหน้าต่าง (ต้องการการรีสตาร์ทโปรแกรมใหม่)"
"message": "ใช้กรอบหน้าต่างระบบ (ต้องรีสตาร์ทแอป)"
},
"app.appearance-settings.native-decorations.title": {
"message": "ของตกแต่งดั้งเดิม"
},
"app.appearance-settings.select-option": {
"message": "เลือกตัวเลือก"
"message": "เลือกตัวเลือก"
},
"app.appearance-settings.show-play-time.description": {
"message": "แสดงเวลาที่คุณใช้เล่นในแต่ละอินสแตนซ์"
},
"app.appearance-settings.show-play-time.title": {
"message": "แสดงระยะเวลาที่เล่น"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "เปิดใช้งานตัวเลือกในการเลือกย่อหรือขยายแถบด้านข้าง"
@@ -104,6 +119,12 @@
"app.auth-servers.unreachable.header": {
"message": "ไม่สามารถเชื่อมต่อถึงเซิร์ฟเวอร์ได้"
},
"app.browse.add-servers-to-instance": {
"message": "เพิ่มเซิร์ฟเวอร์ลงในอินสแตนซ์"
},
"app.browse.add-to-an-instance": {
"message": "เพิ่มลงในอินสแตนซ์"
},
"app.browse.add-to-instance": {
"message": "เพิ่มลงในโปรแกรม"
},
@@ -116,6 +137,9 @@
"app.browse.already-added": {
"message": "เพิ่มแล้ว"
},
"app.browse.back-to-instance": {
"message": "กลับสู่อินสแตนซ์"
},
"app.browse.discover-content": {
"message": "สำรวจเนื้อหา"
},
@@ -128,9 +152,15 @@
"app.browse.project-type.modpacks": {
"message": "แพ็กม็อด"
},
"app.browse.server-instance-content-warning": {
"message": "การเพิ่มเนื้อหาอาจทำให้ใช้งานร่วมกันไม่ได้เมื่อเข้าร่วมเซิร์ฟเวอร์ เนื้อหาที่เพิ่มเข้ามาจะหายไปเมื่อคุณอัปเดตอินสแตนซ์ของเซิร์ฟเวอร์"
},
"app.browse.server.installing": {
"message": "กำลังติดตั้ง"
},
"app.content-install.no-compatible-versions": {
"message": "ไม่มีเวอร์ชันที่เข้ากันได้ {compatibilityLabel} โปรดเลือกเวอร์ชันที่จะติดตั้ง ไฟล์ที่จำเป็นจะไม่ถูกติดตั้งโดยอัตโนมัติ"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -260,6 +290,9 @@
"app.modal.install-to-play.install-button": {
"message": "ติดตั้ง"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# ม็อด}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "มอดแพ็กที่จำเป็น"
},
@@ -284,6 +317,15 @@
"app.modal.update-to-play.update-required-description": {
"message": "จำเป็นต้องอัปเดตเพื่อเล่น {name} กรุณาอัปเดตเป็นเวอร์ชันล่าสุดเพื่อเปิดเกม"
},
"app.project.install-button.already-installed": {
"message": "โปรเจคนี้ถูกติดตั้งแล้ว"
},
"app.project.install-context.back-to-browse": {
"message": "กลับไปที่หน้าค้นหา"
},
"app.project.install-context.install-content-to-instance": {
"message": "ติดตั้งเนื้อหาลงสู่อินสแตนซ์"
},
"app.settings.developer-mode-enabled": {
"message": "โหมดนักพัฒนาเปิดอยู่"
},
@@ -308,8 +350,149 @@
"app.settings.tabs.resource-management": {
"message": "การจัดการทรัพยากร"
},
"app.update-popup.body": {
"message": "Modrinth v{version} พร้อมสำหรับการติดตั้งแล้ว! เปิดโปรแกรมใหม่อีกครั้งเพื่ออัปเดตตอนนี้ หรือจะรออัปเดตอัตโนมัติ ซึ่งจะเกิดขึ้นเมื่อคุณกดปิดโปรแกรม Modrinth"
"app.skins.add-button": {
"message": "เพิ่มสกิน"
},
"app.skins.add-button.drag-and-drop": {
"message": "ลากและวาง"
},
"app.skins.apply-button": {
"message": "นำไปใช้"
},
"app.skins.delete-button": {
"message": "ลบสกิน"
},
"app.skins.delete-modal.description": {
"message": "การดำเนินการนี้จะลบสกินที่เลือกอย่างถาวร การกระทำนี้ไม่สามารถย้อนกลับได้"
},
"app.skins.delete-modal.title": {
"message": "คุณแน่ใจหรือว่าต้องการที่จะลบสกินนี้?"
},
"app.skins.dropped-file-error.text": {
"message": "ไม่สามารถอ่านไฟล์ที่ถูกลากและวางได้"
},
"app.skins.dropped-file-error.title": {
"message": "ข้อผิดพลาดในการประมวลผลไฟล์"
},
"app.skins.edit-button": {
"message": "แก้ไขสกิน"
},
"app.skins.modal.add-skin-button": {
"message": "เพิ่มสกิน"
},
"app.skins.modal.add-title": {
"message": "การเพิ่มสกิน"
},
"app.skins.modal.arm-style-section": {
"message": "สไตล์แขน"
},
"app.skins.modal.arm-style-slim": {
"message": "เพรียว"
},
"app.skins.modal.arm-style-wide": {
"message": "กว้าง"
},
"app.skins.modal.cape-fallback-name": {
"message": "ผ้าคลุม"
},
"app.skins.modal.cape-section": {
"message": "ผ้าคลุม"
},
"app.skins.modal.edit-title": {
"message": "แก้ไขสกิน"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "แก้ไขสกินก่อน!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "ไม่มีผ้าคลุม"
},
"app.skins.modal.none-cape-option": {
"message": "ไม่มี"
},
"app.skins.modal.replace-texture-button": {
"message": "เปลี่ยนเท็กซ์เจอร์"
},
"app.skins.modal.save-skin-button": {
"message": "บันทึกสกิน"
},
"app.skins.modal.saving-tooltip": {
"message": "กำลังบันทึก"
},
"app.skins.modal.texture-section": {
"message": "เท็กซ์เจอร์"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "อัพโหลดสกินก่อน!"
},
"app.skins.preview.edit-button": {
"message": "แก้ไขสกิน"
},
"app.skins.previewing-badge": {
"message": "การแสดงตัวอย่าง"
},
"app.skins.rate-limit.text": {
"message": "คุณได้เปลี่ยนสกินบ่อยเกินไป เซิร์ฟเวอร์ของ Mojang ได้บล็อกคำขอเพิ่มเติมชั่วคราว โปรดรอสักครู่ก่อนลองอีกครั้ง"
},
"app.skins.rate-limit.title": {
"message": "ลดความถี่ลง!"
},
"app.skins.reorder-error.text": {
"message": "ไม่สามารถบันทึกลำดับสกินของคุณได้"
},
"app.skins.reorder-error.title": {
"message": "ไม่สามารถจัดเรียงสกินได้"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "สกินเริ่มต้น"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "คุณได้รับสกินเหล่านี้จากการร่วมบริจาคให้กับกิจกรรมระดมทุน Modrinth Pride ในช่วงเดือน Pride Month"
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "สกินที่บันทึกไว้"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "ลงชื่อเข้าใช้"
},
"app.skins.sign-in.description": {
"message": "โปรดลงชื่อเข้าใช้บัญชี Minecraft ของคุณเพื่อใช้งานฟีเจอร์จัดการสกินของแอป Modrinth"
},
"app.skins.sign-in.rinthbot-alt": {
"message": "บอท Modrinth ที่กำลังตื่นเต้น"
},
"app.skins.sign-in.title": {
"message": "โปรดลงชื่อเข้าใช้"
},
"app.update-popup.body.download-complete": {
"message": "ดาวน์โหลด Modrinth v{version} สำเร็จแล้ว เปิดโปรแกรมใหม่อีกครั้งเพื่ออัปเดตตอนนี้ หรือจะรออัปเดตอัตโนมัติ ซึ่งจะเกิดขึ้นเมื่อคุณกดปิดโปรแกรม Modrinth"
@@ -329,9 +512,6 @@
"app.update-popup.download-complete": {
"message": "การดาวน์โหลดเสร็จสมบูรณ์"
},
"app.update-popup.reload": {
"message": "โหลดใหม่"
},
"app.update-popup.title": {
"message": "มีการอัปเดตใหม่"
},
@@ -341,15 +521,6 @@
"app.update.complete-toast.title": {
"message": "เวอร์ชั่น {version} ถูกติดตั้งแล้ว"
},
"app.update.download-update": {
"message": "ดาวน์โหลดอัปเดต"
},
"app.update.downloading-update": {
"message": "ดาวน์โหลดอัปเดตไปแล้ว ({percent}%)"
},
"app.update.reload-to-update": {
"message": "รีโหลดเพื่อติดตั้งอัปเดต"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -371,6 +542,9 @@
"friends.action.add-friend": {
"message": "เพิ่มเพื่อน"
},
"friends.action.view-friend-requests": {
"message": "คำขอเป็นเพื่อน {count} {count, plural, other {คน}}"
},
"friends.add-friend.submit": {
"message": "ส่งคำขอเป็นเพื่อน"
},
+87 -39
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} indiriliyor"
},
"app.action-bar.downloading-update": {
"message": "Güncelleme indiriliyor"
},
"app.action-bar.downloads": {
"message": "İndirmeler"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Birincil kurulum"
},
"app.action-bar.reload-to-update": {
"message": "Güncellemek için yeniden yükle"
},
"app.action-bar.show-more-running-instances": {
"message": "Daha fazla çalışan kurulum göster"
},
"app.action-bar.stop-instance": {
"message": "Kurulumu durdur"
},
"app.action-bar.update": {
"message": "Güncelleme"
},
"app.action-bar.view-active-downloads": {
"message": "Şu anda indirilienleri göster"
},
@@ -45,7 +54,7 @@
"message": "Modrinth App için tercih ettiğiniz temayı seçin."
},
"app.appearance-settings.color-theme.title": {
"message": "Tema"
"message": "Tema Rengi"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Launcher açıldığında varsayılan olarak açılacak sayfayı seçin."
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Bir seçenek seçin"
},
"app.appearance-settings.show-play-time.description": {
"message": "Bir oyun oturumunda ne kadar zaman geçirdiğinizi gösterir."
},
"app.appearance-settings.show-play-time.title": {
"message": "Oynama Süresini Göster"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Kenar çubuğunu ayarlama özelliğini aktifleştirir."
},
@@ -129,7 +144,7 @@
"message": "İçerik keşfet"
},
"app.browse.discover-servers": {
"message": "Sunucu keşfet"
"message": "Sunucuları keşfet"
},
"app.browse.hide-added-servers": {
"message": "Zaten eklenmiş sunucuları gizle"
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Yükleniyor"
},
"app.content-install.no-compatible-versions": {
"message": "Kullanılabilir sürümler {compatibilityLabel} ile eşleşmiyor. Yine de kurulacak bir sürüm seçin. Bağımlılıklar otomatik olarak yüklenmeyecektir."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -165,7 +183,7 @@
"message": "Mod Paketi Adı"
},
"app.export-modal.modpack-name-placeholder": {
"message": "Modpaketi adı"
"message": "Mod paketi adı"
},
"app.export-modal.select-files-label": {
"message": "Bu dışa aktarmaya hangi dosyaların dahil edileceğini yapılandırma"
@@ -183,10 +201,10 @@
"message": "Bu eylem geri alınamaz"
},
"app.instance.confirm-delete.delete-button": {
"message": "Kurulumu sil"
"message": "Oturumu sil"
},
"app.instance.confirm-delete.header": {
"message": "Kurulumu sil"
"message": "Oturumu sil"
},
"app.instance.modpack-already-installed.body": {
"message": "Bu mod paketi zaten <bold>{instanceName}</bold> kurulumunda yüklü. Kopyalamak istediğinizden emin misiniz?"
@@ -273,7 +291,7 @@
"message": "Yükle"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural,one {#mod}other {#modlar}}"
"message": "{count, plural, one {#mod} other {#mods}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Gerekli mod paketi"
@@ -336,7 +354,7 @@
"message": "Skin ekle"
},
"app.skins.add-button.drag-and-drop": {
"message": "Sürükle bırak"
"message": "Sürükle ve bırak"
},
"app.skins.apply-button": {
"message": "Uygula"
@@ -419,11 +437,17 @@
"app.skins.rate-limit.title": {
"message": "Yavaşla!"
},
"app.skins.reorder-error.text": {
"message": "Skinini kaydedemedik."
},
"app.skins.reorder-error.title": {
"message": "Skinler sıralanamadı"
},
"app.skins.section.builders-and-biomes": {
"message": "İnşaatçılar & Biyomlar"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
"message": "Gökyüzünü kovala"
},
"app.skins.section.default-skins": {
"message": "Varsayılan skinler"
@@ -431,23 +455,32 @@
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Onuru"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Bu skinleri, Onur Ayı boyunca Modrinth'in düzenlediği bir bağış kampanyasına yaptığınız bağış karşılığında aldınız."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
"message": "Kaos Dağları"
},
"app.skins.section.saved-skins": {
"message": "Kaydedilen Skinler"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
"message": "Yürüyen Kahraman"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
"message": "Bakır Çağı"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
"message": "Bahçe Uyanıyor"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
"message": "Küçük Devralma"
},
"app.skins.sign-in.button": {
"message": "Giriş Yap"
@@ -464,11 +497,8 @@
"app.skins.title": {
"message": "Skin seçici"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} güncellemesi indirilmeye hazır! Hemen güncellemek için yeniden başlatın veya Modrinth Appi kapattığınızda otomatik olarak güncellenecek."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} indirildi. Güncellemek için yeniden başlatın veya Modrinth Appi kapattığınızda otomatik olarak güncellenecek."
"message": "Modrinth App v{version} indirildi. Güncellemek için yeniden başlatın veya Modrinth Appi kapattığınızda otomatik olarak güncellenecektir."
},
"app.update-popup.body.linux": {
"message": "Modrinth App v{version} yayımlandı. En yeni özellikler ve hata düzeltmeleri için paket yöneticin üzerinden güncelle!"
@@ -486,26 +516,17 @@
"message": "İndirme tamamlandı"
},
"app.update-popup.reload": {
"message": "Yeniden başlat"
"message": "Güncellemek için yeniden yükle"
},
"app.update-popup.title": {
"message": "Güncelleme mevcut"
},
"app.update.complete-toast.text": {
"message": "Değişiklikleri görüntülemek için buraya tıklayın."
"message": "Yama notlarını görüntülemek için buraya tıklayın."
},
"app.update.complete-toast.title": {
"message": "{version} sürümü başarıyla kuruldu!"
},
"app.update.download-update": {
"message": "Güncellemeyi indir"
},
"app.update.downloading-update": {
"message": "Güncelleme indiriliyor (%{percent})"
},
"app.update.reload-to-update": {
"message": "Güncellemeyi kurmak için yeniden başlatın"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -519,7 +540,7 @@
"message": "Henüz oynanmadı"
},
"app.world.world-item.offline": {
"message": "Çevrim dışı"
"message": "Çevrimdışı"
},
"app.world.world-item.players-online": {
"message": "{count} çevrimiçi"
@@ -527,6 +548,9 @@
"friends.action.add-friend": {
"message": "Bir arkadaş ekle"
},
"friends.action.view-friend-requests": {
"message": "{count} arkadaşlık isteği"
},
"friends.add-friend.submit": {
"message": "Arkadaşlık isteği gönder"
},
@@ -555,16 +579,16 @@
"message": "Arkadaşlık isteği gönderildi"
},
"friends.friend.view-profile": {
"message": "Profili gör"
"message": "Profili görüntüle"
},
"friends.heading": {
"message": "Arkadaşlar"
},
"friends.heading.active": {
"message": "Aktif"
"message": "Çevrimiçi"
},
"friends.heading.offline": {
"message": "Çevrim dışı"
"message": "Çevrimdışı"
},
"friends.heading.online": {
"message": "Çevrim içi"
@@ -630,7 +654,7 @@
"message": "Adres"
},
"instance.server-modal.name": {
"message": "Ad"
"message": "Sunucu Adı"
},
"instance.server-modal.placeholder-name": {
"message": "Minecraft Sunucusu"
@@ -657,7 +681,7 @@
"message": "Çoğalt"
},
"instance.settings.tabs.general.duplicate-button.tooltip.installing": {
"message": "İndirilirken çoğaltılamaz."
"message": "Kurulurken çoğaltılamaz."
},
"instance.settings.tabs.general.duplicate-instance": {
"message": "Profili çoğalt"
@@ -692,11 +716,35 @@
"instance.settings.tabs.general.name": {
"message": "Ad"
},
"instance.settings.tabs.general.update-channel": {
"message": "Güncelleme kanalı"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release, beta ve alpha versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release ve beta versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Yayın"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Yalnızca release versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Güncelleme kanalını seç"
},
"instance.settings.tabs.hooks": {
"message": "Başlatma kancaları"
},
"instance.settings.tabs.hooks.custom-hooks": {
"message": "Özel başlatma kancası"
"message": "Özel başlatma kancaları"
},
"instance.settings.tabs.hooks.description": {
"message": "Kancalar gelişmiş kullanıcıların oyunu başlattıktan önce ve sonra belirli sistem komutları çalıştırmasını sağlar."
@@ -723,13 +771,13 @@
"message": "Oyun başlatma kancaları"
},
"instance.settings.tabs.hooks.wrapper": {
"message": "Sarmalayıcı"
"message": "Paketleyici"
},
"instance.settings.tabs.hooks.wrapper.description": {
"message": "Minecraft'ı başlatmak için sarmalayıcı komutu."
"message": "Minecraft'ı başlatmak için paketleyici komutu."
},
"instance.settings.tabs.hooks.wrapper.enter": {
"message": "Sarmalayıcı komutu girin..."
"message": "Paketleyici komutu girin..."
},
"instance.settings.tabs.installation": {
"message": "Kurulum"
@@ -837,7 +885,7 @@
"message": "Yalnızca Minecraft Alpha 1.0.5 veya daha yüksek sunuculara hızlıca girebilirsin"
},
"instance.worlds.no_singleplayer_quick_play": {
"message": "Yalnızca Minecraft 1.20 veya daha yüksek sürümlerde tek oyunculu dünyalara doğrudan girebilirsin"
"message": "Yalnızca Minecraft 1.20+ sürümlerde tek oyunculu dünyalara doğrudan girebilirsin"
},
"instance.worlds.play_instance": {
"message": "Kurulumu oyna"
+79 -28
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Завантаження Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Завантаження оновлення"
},
"app.action-bar.downloads": {
"message": "Завантаження"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Основний профіль"
},
"app.action-bar.reload-to-update": {
"message": "Перезавантаження для оновлення"
},
"app.action-bar.show-more-running-instances": {
"message": "Показати більше запущених профілів"
},
"app.action-bar.stop-instance": {
"message": "Зупинити профіль"
},
"app.action-bar.update": {
"message": "Оновлення"
},
"app.action-bar.view-active-downloads": {
"message": "Переглянути активні завантаження"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Виберіть опцію"
},
"app.appearance-settings.show-play-time.description": {
"message": "Показує, скільки часу ви провели в цьому профілі."
},
"app.appearance-settings.show-play-time.title": {
"message": "Час гри"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Умикає можливість перемикання бокової панелі."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Установлення"
},
"app.content-install.no-compatible-versions": {
"message": "Жодна доступна версія не сумісна з {compatibilityLabel}. Виберіть версію, щоб усе одно її встановити. Залежності не встановлюватимуться автоматично."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -189,7 +207,7 @@
"message": "Видалити профіль"
},
"app.instance.modpack-already-installed.body": {
"message": "Ця збірка вже встановлена в профілі <bold>«{instanceName}»</bold>. Ви впевнені, що хочете її скопіювати?"
"message": "Ця збірка вже встановлена в профілі <bold>«{instanceName}»</bold>. Дійсно бажаєте її клонувати?"
},
"app.instance.modpack-already-installed.create": {
"message": "Створити"
@@ -263,6 +281,9 @@
"app.modal.install-to-play.install-button": {
"message": "Установити"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# мод} few {# мода} other {# модів}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Потрібна збірка"
},
@@ -279,13 +300,13 @@
"message": "Дивитися вміст"
},
"app.modal.update-to-play.header": {
"message": "Оновлення перед грою"
"message": "Оновлення для гри"
},
"app.modal.update-to-play.update-required": {
"message": "Необхідне оновлення"
},
"app.modal.update-to-play.update-required-description": {
"message": "«{name}» потребує оновлення, щоб грати. Будь ласка, оновіть гру до останньої версії, щоб запустити її."
"message": "«{name}» потребує оновлення, щоб грати. Будь ласка, оновіть до останньої версії, щоб запустити гру."
},
"app.project.install-button.already-installed": {
"message": "Проєкт уже встановлено"
@@ -324,7 +345,7 @@
"message": "Додати скін"
},
"app.skins.add-button.drag-and-drop": {
"message": "Перетягніть і вставте"
"message": "Перетягніть сюди"
},
"app.skins.apply-button": {
"message": "Застосувати"
@@ -336,7 +357,7 @@
"message": "Це видалить вибраний скін назавжди. Ця дія не може бути скасована."
},
"app.skins.delete-modal.title": {
"message": "Чи ви впевнені що хочете видалити цей скін?"
"message": "Ви дійсно бажаєте видалити цей скін?"
},
"app.skins.dropped-file-error.text": {
"message": "Неможливо відкрити вибраний файл."
@@ -372,19 +393,19 @@
"message": "Редагування скіна"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Відредагуйте скін для початку!"
"message": "Спершу відредагуйте скін!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Без плаща"
},
"app.skins.modal.none-cape-option": {
"message": "Без"
"message": "Немає"
},
"app.skins.modal.replace-texture-button": {
"message": "Замінити текстуру"
},
"app.skins.modal.save-skin-button": {
"message": "Зберегти вигляд"
"message": "Зберегти скін"
},
"app.skins.modal.saving-tooltip": {
"message": "Збереження…"
@@ -393,7 +414,7 @@
"message": "Текстура"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Завантажте скін для початку!"
"message": "Спершу завантажте скін!"
},
"app.skins.preview.edit-button": {
"message": "Редагувати скін"
@@ -402,10 +423,16 @@
"message": "Попередній перегляд"
},
"app.skins.rate-limit.text": {
"message": "Ви змінюєте свій скін дуже часто. Сервери Mojang тимчасово заблокували подальші запити. Зачекайте трохи перш ніж спробувати знову."
"message": "Ви змінюєте скін занадто часто. Сервери Mojang тимчасово заблокували подальші запити. Зачекайте трохи, перш ніж спробувати знову."
},
"app.skins.rate-limit.title": {
"message": "Шановний!"
"message": "Повільніше!"
},
"app.skins.reorder-error.text": {
"message": "Ваше замовлення скінів не вдалося зберегти."
},
"app.skins.reorder-error.title": {
"message": "Не вдалося змінити порядок скінів"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
@@ -419,6 +446,15 @@
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Ви отримали ці скіни за участь у благодійній акції Modrinth Pride під час місяця прайду."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
@@ -452,17 +488,14 @@
"app.skins.title": {
"message": "Вибір скіну"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} готовий до встановлення! Перезапустіть, щоб оновити зараз. Або, оновлення буде здійснено автоматично, коли закриєте застосунок Modrinth."
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} завантажено. Перезапустіть зараз, щоб оновити його, або це відбудеться автоматично після закриття Modrinth App."
"message": "Modrinth App v{version} завантажено. Перезапустіть, щоб оновити зараз, або це відбудеться автоматично після закриття застосунку."
},
"app.update-popup.body.linux": {
"message": "Modrinth App v{version} вже доступно. Скористайтеся вашим менеджером пакетів, щоб отримати найновіші функції та виправлення!"
"message": "Modrinth App v{version} вже доступно. Скористайтеся менеджером пакетів, щоб отримати найновіші функції та виправлення!"
},
"app.update-popup.body.metered": {
"message": "Modrinth App v{version} доступний для завантаження! Оскільки ви на лімітному з’єднанні, ми не завантажили його автоматично."
"message": "Modrinth App v{version} вже доступно! Оскільки ви використовуєте лімітне підключення, ми не завантажили його автоматично."
},
"app.update-popup.changelog": {
"message": "Журнал змін"
@@ -474,7 +507,7 @@
"message": "Завантаження завершено"
},
"app.update-popup.reload": {
"message": "Перезавантажити"
"message": "Перезавантаження для оновлення"
},
"app.update-popup.title": {
"message": "Доступне оновлення"
@@ -485,15 +518,6 @@
"app.update.complete-toast.title": {
"message": "Версію {version} успішно встановлено!"
},
"app.update.download-update": {
"message": "Завантажити оновлення"
},
"app.update.downloading-update": {
"message": "Завантаження оновлення ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Перезавантажте, щоб установити оновлення"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -507,7 +531,7 @@
"message": "Ще не зіграно"
},
"app.world.world-item.offline": {
"message": "Поза мережею"
"message": "Не в мережі"
},
"app.world.world-item.players-online": {
"message": "{count} у мережі"
@@ -515,6 +539,9 @@
"friends.action.add-friend": {
"message": "Додати друга"
},
"friends.action.view-friend-requests": {
"message": "{count} запит{count, plural, one { у} few {и в} many {ів у} other {у в}} друзі"
},
"friends.add-friend.submit": {
"message": "Надіслати запит у друзі"
},
@@ -680,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "Назва"
},
"instance.settings.tabs.general.update-channel": {
"message": "Випуск оновлення"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Альфа"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Випуски, бета- та альфа-версії будуть показуватися як доступні оновлення."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Бета"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Випуски та бета-версії будуть показуватися як доступні оновлення."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Випуск"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Тільки випуски версії будуть показуватися як доступні оновлення."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Оберіть випуск оновлення"
},
"instance.settings.tabs.hooks": {
"message": "Гуки запуску"
},
+54 -18
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Đang tải xuống Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Đang tải xuống bản cập nhật"
},
"app.action-bar.downloads": {
"message": "Tải xuống"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Bản instance chính"
},
"app.action-bar.reload-to-update": {
"message": "Tải lại để cập nhật"
},
"app.action-bar.show-more-running-instances": {
"message": "Hiện thêm các phiên bản đang chạy"
},
"app.action-bar.stop-instance": {
"message": "Dừng instance"
},
"app.action-bar.update": {
"message": "Cập nhật"
},
"app.action-bar.view-active-downloads": {
"message": "Xem các lượt tải xuống"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Chọn một tuỳ chọn"
},
"app.appearance-settings.show-play-time.description": {
"message": "Hiển thị thời gian bạn đã dành để chơi bằng một cấu hình."
},
"app.appearance-settings.show-play-time.title": {
"message": "Hiện thời gian chơi"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Bật tính năng ẩn hiển thị thanh menu bên."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Đang cài đặt"
},
"app.content-install.no-compatible-versions": {
"message": "Không có phiên bản nào phù hợp với {compatibilityLabel}. Nếu vẫn chọn một phiên bản để cài đặt, các gói phụ thuộc sẽ không được tải tự động."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -273,7 +291,7 @@
"message": "Tải xuống"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, one {# mod} other {# mods}}"
"message": "{count, plural, other {# mod}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "Yêu cầu modpack"
@@ -419,6 +437,12 @@
"app.skins.rate-limit.title": {
"message": "Bình tĩnh lại!"
},
"app.skins.reorder-error.text": {
"message": "Không thể lưu thứ tự skin của bạn."
},
"app.skins.reorder-error.title": {
"message": "Sắp xếp lại skin thất bại"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -438,7 +462,7 @@
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Bạn đã được tặng những skin này sau khi quyên góp cho Modrinth trong tháng Lẩu Gà Bình Thuận thế giới."
"message": "Bạn đã được tặng những skin này sau khi quyên góp cho Modrinth trong tháng tự hào LGBT thế giới."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
@@ -462,7 +486,7 @@
"message": "Đăng nhập"
},
"app.skins.sign-in.description": {
"message": "Đặng nhập vào mội tài khoản Minecraft để sử dụng trình sửa Skin trong ứng dụng này."
"message": "Đặng nhập vào mội tài khoản Minecraft để đổi skin Skin trong ứng dụng này."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Bot phấn khích"
@@ -471,10 +495,7 @@
"message": "Đăng nhập đã"
},
"app.skins.title": {
"message": "Trình sửa skin"
},
"app.update-popup.body": {
"message": "Phiên bản v{version} của Modrinth đã được chuẩn bị để cài đặt! Khởi động lại ứng dụng để cập nhật ngay bây giờ, hoặc cập nhật tự động khi bạn đóng Modrinth."
"message": "Đổi skin"
},
"app.update-popup.body.download-complete": {
"message": "Phiên bản v{version} của Modrinth đã sẵn sàng để có thể cài đặt. Khởi động lại ứng dụng để cập nhật ngay bây giờ, hoặc tự động cập nhật sau khi bạn tắt Modrinth."
@@ -495,7 +516,7 @@
"message": "Cài đặt thành công"
},
"app.update-popup.reload": {
"message": "Khởi động lại"
"message": "Tải lại để cập nhật"
},
"app.update-popup.title": {
"message": "Cập nhật mới hiện đang khả dụng"
@@ -506,15 +527,6 @@
"app.update.complete-toast.title": {
"message": "Phiên bản {version} đã được cài đặt thành công!"
},
"app.update.download-update": {
"message": "Tải về bản cập nhật"
},
"app.update.downloading-update": {
"message": "Đang tải xuống bản cập nhật ({percent}%)"
},
"app.update.reload-to-update": {
"message": "Hãy khởi động lại để cài đặt bản cập nhật"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -537,7 +549,7 @@
"message": "Thêm một người bạn"
},
"friends.action.view-friend-requests": {
"message": "{count} yêu cầu kết bạn {count, plural, one {request} other {requests}}"
"message": "{count} {count, plural, other {yêu cầu kết bạn}}"
},
"friends.add-friend.submit": {
"message": "Gửi yêu cầu kết bạn"
@@ -704,6 +716,30 @@
"instance.settings.tabs.general.name": {
"message": "Tên"
},
"instance.settings.tabs.general.update-channel": {
"message": "Kênh cập nhật"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Sơ khai"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Các phiên bản chính thức, thử nghiệm và sơ khai sẽ hiển thị trong danh sách cập nhật có sẵn."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Thử nghiệm"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Các phiên bản chính thức và thử nghiệm sẽ hiển thị trong danh sách cập nhật có sẵn."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Chính thức"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Chỉ phiên bản chính thức sẽ hiển thị trong danh sách cập nhật có sẵn."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Chọn kênh cập nhật"
},
"instance.settings.tabs.hooks": {
"message": "Khởi chạy Hooks"
},
+70 -22
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "正在下载 Java {version}"
},
"app.action-bar.downloading-update": {
"message": "下载更新中"
},
"app.action-bar.downloads": {
"message": "下载"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "主实例"
},
"app.action-bar.reload-to-update": {
"message": "重新加载以更新"
},
"app.action-bar.show-more-running-instances": {
"message": "显示更多运行中的实例"
},
"app.action-bar.stop-instance": {
"message": "停止实例"
},
"app.action-bar.update": {
"message": "更新"
},
"app.action-bar.view-active-downloads": {
"message": "查看正在下载内容"
},
@@ -63,7 +72,7 @@
"message": "在皮肤页面中禁用你玩家头顶上的名牌。"
},
"app.appearance-settings.hide-nametag.title": {
"message": "隐藏名称标签"
"message": "隐藏名"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "在主页的“快速回到”部分包含最近的世界。"
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "选择一个选项"
},
"app.appearance-settings.show-play-time.description": {
"message": "显示你在每个实例中游玩的时间。"
},
"app.appearance-settings.show-play-time.title": {
"message": "显示总游玩时间"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "启用切换侧边栏的功能。"
},
@@ -105,7 +120,7 @@
"message": "无法连接到身份验证服务器"
},
"app.browse.add-servers-to-instance": {
"message": "将服务器添加到实例"
"message": "正在添加服务器到此实例"
},
"app.browse.add-to-an-instance": {
"message": "添加到实例"
@@ -120,7 +135,7 @@
"message": "已添加"
},
"app.browse.already-added": {
"message": "已添加"
"message": "已添加"
},
"app.browse.back-to-instance": {
"message": "返回实例"
@@ -138,11 +153,14 @@
"message": "整合包"
},
"app.browse.server-instance-content-warning": {
"message": "加入服务器时,添加内容可能会破坏兼容性。当更新服务器实例内容时任何添加的内容也将丢失。"
"message": "加入服务器时,添加内容可能会破坏兼容性。当更新服务器实例内容时任何添加的内容也将丢失。"
},
"app.browse.server.installing": {
"message": "安装中"
},
"app.content-install.no-compatible-versions": {
"message": "没有与 {compatibilityLabel} 匹配的可用版本。如果仍要安装,请选择一个版本。依赖项将不会自动安装。"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -263,6 +281,9 @@
"app.modal.install-to-play.install-button": {
"message": "安装"
},
"app.modal.install-to-play.mod-count": {
"message": "{count} 个模组"
},
"app.modal.install-to-play.required-modpack": {
"message": "需求整合包"
},
@@ -407,11 +428,17 @@
"app.skins.rate-limit.title": {
"message": "慢点!"
},
"app.skins.reorder-error.text": {
"message": "未能保存皮肤顺序。"
},
"app.skins.reorder-error.title": {
"message": "皮肤排序失败"
},
"app.skins.section.builders-and-biomes": {
"message": "建造者与生物群系"
},
"app.skins.section.chase-the-skies": {
"message": "追逐苍穹"
"message": "追逐天空"
},
"app.skins.section.default-skins": {
"message": "默认皮肤"
@@ -422,23 +449,29 @@
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth 精选"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "你在骄傲月期间为 Modrinth 的骄傲月筹款活动捐款,因此获得了这些皮肤。"
},
"app.skins.section.mounts-of-mayhem": {
"message": "暴乱坐骑"
"message": "群骑纷争"
},
"app.skins.section.saved-skins": {
"message": "已保存皮肤"
},
"app.skins.section.striding-hero": {
"message": "阔步英雄"
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "铜器时代"
},
"app.skins.section.the-garden-awakens": {
"message": "花园苏醒"
"message": "苍园觉醒"
},
"app.skins.section.tiny-takeover": {
"message": "微小占领"
"message": "小鬼当家"
},
"app.skins.sign-in.button": {
"message": "登录"
@@ -455,9 +488,6 @@
"app.skins.title": {
"message": "皮肤选择器"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} 更新已就绪!立即重启更新,或退出时自动安装。"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} 更新已下载完成!立即重启更新,或退出时自动安装。"
},
@@ -477,7 +507,7 @@
"message": "下载完成"
},
"app.update-popup.reload": {
"message": "重新启动"
"message": "重新加载以更新"
},
"app.update-popup.title": {
"message": "有可用的更新"
@@ -488,15 +518,6 @@
"app.update.complete-toast.title": {
"message": "版本 {version} 已成功安装!"
},
"app.update.download-update": {
"message": "下载更新"
},
"app.update.downloading-update": {
"message": "下载更新中({percent}%"
},
"app.update.reload-to-update": {
"message": "重启以安装更新"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -518,6 +539,9 @@
"friends.action.add-friend": {
"message": "添加好友"
},
"friends.action.view-friend-requests": {
"message": "{count} 个好友请求"
},
"friends.add-friend.submit": {
"message": "发送好友请求"
},
@@ -683,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "名称"
},
"instance.settings.tabs.general.update-channel": {
"message": "更新渠道"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha 测试版"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "正式版,beta 和 alpha 测试版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta 测试版"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "正式版和 beta 测试版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "正式版"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "只有正式版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.select": {
"message": "选择更新渠道"
},
"instance.settings.tabs.hooks": {
"message": "启动 Hooks"
},
+204 -21
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "正在下載 Java {version}"
},
"app.action-bar.downloading-update": {
"message": "正在下載更新"
},
"app.action-bar.downloads": {
"message": "下載"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "主要實例"
},
"app.action-bar.reload-to-update": {
"message": "重新載入即可更新"
},
"app.action-bar.show-more-running-instances": {
"message": "顯示更多執行中的實例"
},
"app.action-bar.stop-instance": {
"message": "停止實例"
},
"app.action-bar.update": {
"message": "更新"
},
"app.action-bar.view-active-downloads": {
"message": "查看正在下載的項目"
},
@@ -33,10 +42,10 @@
"message": "查看實例"
},
"app.action-bar.view-logs": {
"message": "查看紀錄"
"message": "查看紀錄"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "啟用進階繪製(如模糊效果)若無硬體加速可能會導致效能問題。"
"message": "啟用進階繪製(如模糊效果)若無硬體加速可能會導致效能問題。"
},
"app.appearance-settings.advanced-rendering.title": {
"message": "進階繪製"
@@ -78,14 +87,20 @@
"message": "最小化啟動器"
},
"app.appearance-settings.native-decorations.description": {
"message": "使用系統視窗外框(需要重新啟動應用程式)。"
"message": "使用系統內建的視窗外框(需要重應用程式)。"
},
"app.appearance-settings.native-decorations.title": {
"message": "原生裝飾"
"message": "原生視窗"
},
"app.appearance-settings.select-option": {
"message": "選擇選項"
},
"app.appearance-settings.show-play-time.description": {
"message": "顯示你在該實例的遊玩時數。"
},
"app.appearance-settings.show-play-time.title": {
"message": "顯示遊玩時數"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "啟用切換側邊欄的功能。"
},
@@ -143,14 +158,17 @@
"app.browse.server.installing": {
"message": "安裝中"
},
"app.content-install.no-compatible-versions": {
"message": "沒有符合 {compatibilityLabel} 的可用版本。請選擇一個版本強制安裝。不會自動安裝相依模組。"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
"app.creation-modal.installing-modpack.title": {
"message": "正在安裝模組包……"
"message": "正在安裝模組包..."
},
"app.export-modal.description-placeholder": {
"message": "輸入模組包描述……"
"message": "輸入模組包描述..."
},
"app.export-modal.export-button": {
"message": "匯出"
@@ -249,7 +267,7 @@
"message": "確定要移除「{name}」嗎?"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "搜尋 {count} 個世界……"
"message": "搜尋 {count} 個世界..."
},
"app.instance.worlds.this-server": {
"message": "這個伺服器"
@@ -263,6 +281,9 @@
"app.modal.install-to-play.install-button": {
"message": "安裝"
},
"app.modal.install-to-play.mod-count": {
"message": "{count, plural, other {# 個模組}}"
},
"app.modal.install-to-play.required-modpack": {
"message": "所需模組包"
},
@@ -320,8 +341,152 @@
"app.settings.tabs.resource-management": {
"message": "資源管理"
},
"app.update-popup.body": {
"message": "Modrinth App v{version} 已準備好安裝!立即重新載入以更新,或在關閉 Modrinth App 時自動更新。"
"app.skins.add-button": {
"message": "新增外觀"
},
"app.skins.add-button.drag-and-drop": {
"message": "拖曳"
},
"app.skins.apply-button": {
"message": "套用"
},
"app.skins.delete-button": {
"message": "刪除外觀"
},
"app.skins.delete-modal.description": {
"message": "這會永久刪除所選的外觀,這個動作無法復原。"
},
"app.skins.delete-modal.title": {
"message": "你確定要刪除這個外觀嗎?"
},
"app.skins.dropped-file-error.text": {
"message": "無法讀取拖曳的檔案。"
},
"app.skins.dropped-file-error.title": {
"message": "處理檔案時發生錯誤"
},
"app.skins.edit-button": {
"message": "編輯外觀"
},
"app.skins.modal.add-skin-button": {
"message": "新增外觀"
},
"app.skins.modal.add-title": {
"message": "新增外觀"
},
"app.skins.modal.arm-style-section": {
"message": "手臂身形"
},
"app.skins.modal.arm-style-slim": {
"message": "苗條"
},
"app.skins.modal.arm-style-wide": {
"message": "寬"
},
"app.skins.modal.cape-fallback-name": {
"message": "披風"
},
"app.skins.modal.cape-section": {
"message": "披風"
},
"app.skins.modal.edit-title": {
"message": "編輯外觀"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "請先對外觀進行編輯!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "無披風"
},
"app.skins.modal.none-cape-option": {
"message": "無"
},
"app.skins.modal.replace-texture-button": {
"message": "取代材質"
},
"app.skins.modal.save-skin-button": {
"message": "儲存外觀"
},
"app.skins.modal.saving-tooltip": {
"message": "正在儲存..."
},
"app.skins.modal.texture-section": {
"message": "材質"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "請先上傳外觀檔案!"
},
"app.skins.preview.edit-button": {
"message": "編輯外觀"
},
"app.skins.previewing-badge": {
"message": "預覽"
},
"app.skins.rate-limit.text": {
"message": "你變更外觀的頻率太過頻繁。Mojang 伺服器已暫時封鎖後續請求,請稍等片刻,然後再試一次。"
},
"app.skins.rate-limit.title": {
"message": "慢一點!"
},
"app.skins.reorder-error.text": {
"message": "無法儲存你的外觀排序。"
},
"app.skins.reorder-error.title": {
"message": "重新排序外觀失敗"
},
"app.skins.section.builders-and-biomes": {
"message": "建造者&生態域"
},
"app.skins.section.chase-the-skies": {
"message": "追逐天空"
},
"app.skins.section.default-skins": {
"message": "預設外觀"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth 驕傲"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "這些外觀是你在驕傲月期間捐款給 Modrinth 驕傲月籌款活動所獲得的。"
},
"app.skins.section.mounts-of-mayhem": {
"message": "群騎紛爭"
},
"app.skins.section.saved-skins": {
"message": "已儲存外觀"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "銅器時代"
},
"app.skins.section.the-garden-awakens": {
"message": "蒼園覺醒"
},
"app.skins.section.tiny-takeover": {
"message": "小鬼當家"
},
"app.skins.sign-in.button": {
"message": "登入"
},
"app.skins.sign-in.description": {
"message": "請登入你的 Minecraft 帳號以使用 Modrinth App 的外觀管理功能。"
},
"app.skins.sign-in.rinthbot-alt": {
"message": "興奮的 Modrinth 機器人"
},
"app.skins.sign-in.title": {
"message": "請登入"
},
"app.skins.title": {
"message": "外觀選擇工具"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} 已完成下載!立即重新載入以更新,或在關閉 Modrinth App 時自動更新。"
@@ -342,7 +507,7 @@
"message": "下載完成"
},
"app.update-popup.reload": {
"message": "重新載入"
"message": "重新載入即可更新"
},
"app.update-popup.title": {
"message": "有可用的更新"
@@ -353,15 +518,6 @@
"app.update.complete-toast.title": {
"message": "版本 {version} 已成功安裝!"
},
"app.update.download-update": {
"message": "下載更新"
},
"app.update.downloading-update": {
"message": "正在下載更新 ({percent}%)"
},
"app.update.reload-to-update": {
"message": "重新載入即可安裝更新"
},
"app.world.server-modal.placeholder-address": {
"message": "example.modrinth.gg"
},
@@ -383,6 +539,9 @@
"friends.action.add-friend": {
"message": "新增好友"
},
"friends.action.view-friend-requests": {
"message": "{count} 個好友{count, plural, other {邀請}}"
},
"friends.add-friend.submit": {
"message": "送出好友邀請"
},
@@ -548,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "名稱"
},
"instance.settings.tabs.general.update-channel": {
"message": "更新通道"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha 版"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "相容的正式版、Beta 版和 Alpha 版本都會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta 版"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "相容的正式版和 Beta 版本都會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "正式版"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "僅有正式版會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.select": {
"message": "選擇更新通道"
},
"instance.settings.tabs.hooks": {
"message": "啟動掛勾"
},
@@ -609,10 +792,10 @@
"message": "自訂記憶體分配"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "輸入環境變數……"
"message": "輸入環境變數..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "輸入 Java 參數……"
"message": "輸入 Java 參數..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "環境變數"
+72 -22
View File
@@ -29,7 +29,7 @@ import {
import { useQueryClient } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import type { Ref } from 'vue'
import { computed, ref, watch } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import type { LocationQuery } from 'vue-router'
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'
@@ -41,6 +41,7 @@ import {
get_search_results_v3,
get_version_many,
} from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events.js'
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
import {
get as getInstance,
@@ -181,6 +182,28 @@ watchServerContextChanges()
await initInstanceContext()
async function refreshInstalledProjectIds() {
if (!route.query.i) return
if (route.query.from === 'worlds') {
const worlds = await get_profile_worlds(route.query.i as string).catch(handleError)
if (!worlds) return
const serverProjectIds = worlds
.filter((w) => w.type === 'server' && 'project_id' in w && w.project_id)
.map((w) => (w as { project_id: string }).project_id)
debugLog('installedServerProjectIds loaded', { count: serverProjectIds.length })
installedProjectIds.value = serverProjectIds
return
}
const ids = await getInstalledProjectIds(route.query.i as string).catch(handleError)
if (!ids) return
debugLog('installedProjectIds loaded', { count: ids.length })
installedProjectIds.value = ids
}
async function initInstanceContext() {
debugLog('initInstanceContext', {
queryI: route.query.i,
@@ -199,24 +222,7 @@ async function initInstanceContext() {
gameVersion: instance.value?.game_version,
})
if (route.query.from === 'worlds') {
get_profile_worlds(route.query.i as string)
.then((worlds) => {
const serverProjectIds = worlds
.filter((w) => w.type === 'server' && 'project_id' in w && w.project_id)
.map((w) => (w as { project_id: string }).project_id)
debugLog('installedServerProjectIds loaded', { count: serverProjectIds.length })
installedProjectIds.value = serverProjectIds
})
.catch(handleError)
} else {
getInstalledProjectIds(route.query.i as string)
.then((ids) => {
debugLog('installedProjectIds loaded', { count: ids?.length })
installedProjectIds.value = ids
})
.catch(handleError)
}
await refreshInstalledProjectIds()
if (instance.value?.linked_data?.project_id) {
debugLog('checking linked project for server status', instance.value.linked_data.project_id)
@@ -805,10 +811,10 @@ function getCardActions(
selectedInstall.versionId,
instance.value ? instance.value.path : null,
'SearchCard',
(versionId) => {
(versionId, installedProjectIds) => {
setProjectInstalling(projectResult.project_id, false)
if (versionId) {
onSearchResultInstalled(projectResult.project_id)
onSearchResultsInstalled(installedProjectIds ?? [projectResult.project_id])
}
},
(profile) => {
@@ -834,7 +840,19 @@ function onSearchResultInstalled(id: string) {
markServerProjectInstalled(id)
return
}
newlyInstalled.value.push(id)
if (!newlyInstalled.value.includes(id)) {
newlyInstalled.value = [...newlyInstalled.value, id]
}
}
function onSearchResultsInstalled(ids: string[]) {
if (isServerContext.value) {
for (const id of ids) {
markServerProjectInstalled(id)
}
return
}
newlyInstalled.value = Array.from(new Set([...newlyInstalled.value, ...ids]))
}
async function search(requestParams: string) {
@@ -966,6 +984,38 @@ if (instance.value?.game_version) {
await searchState.refreshSearch()
type UnlistenFn = () => void
let isUnmounted = false
let unlistenProfiles: UnlistenFn | null = null
onMounted(() => {
profile_listener(async (event: { event: string; profile_path_id: string }) => {
if (
instance.value &&
event.profile_path_id === instance.value.path &&
event.event === 'synced'
) {
await refreshInstalledProjectIds()
await searchState.refreshSearch()
}
})
.then((unlisten) => {
if (isUnmounted) {
unlisten()
return
}
unlistenProfiles = unlisten
})
.catch(handleError)
})
onUnmounted(() => {
isUnmounted = true
unlistenProfiles?.()
})
function getProjectBrowseQuery() {
if (!installContext.value) return undefined
return {
+225 -9
View File
@@ -30,7 +30,7 @@ import type AccountsCard from '@/components/ui/AccountsCard.vue'
import EditSkinModal from '@/components/ui/skin/EditSkinModal.vue'
import VirtualSkinSectionList from '@/components/ui/skin/VirtualSkinSectionList.vue'
import { trackEvent } from '@/helpers/analytics'
import { get_default_user, login as login_flow, users } from '@/helpers/auth'
import { check_reachable, get_default_user, login as login_flow, users } from '@/helpers/auth'
import type { RenderResult } from '@/helpers/rendering/batch-skin-renderer.ts'
import { generateSkinPreviews, skinBlobUrlMap } from '@/helpers/rendering/batch-skin-renderer.ts'
import type { Cape, Skin, SkinTextureUrl } from '@/helpers/skins.ts'
@@ -46,6 +46,8 @@ import {
get_normalized_skin_texture,
normalize_skin_texture,
remove_custom_skin,
save_custom_skin,
set_custom_skin_order,
} from '@/helpers/skins.ts'
import { hasPride26Badge } from '@/helpers/user-campaigns.ts'
import { handleSevereError } from '@/store/error'
@@ -129,6 +131,14 @@ const messages = defineMessages({
id: 'app.skins.dropped-file-error.text',
defaultMessage: 'Failed to read the dropped file.',
},
reorderSkinErrorTitle: {
id: 'app.skins.reorder-error.title',
defaultMessage: 'Failed to reorder skins',
},
reorderSkinErrorText: {
id: 'app.skins.reorder-error.text',
defaultMessage: 'Your skin order could not be saved.',
},
deleteSkinTitle: {
id: 'app.skins.delete-modal.title',
defaultMessage: 'Are you sure you want to delete this skin?',
@@ -181,6 +191,7 @@ const client = injectModrinthClient()
const themeStore = useTheming()
const skins = ref<Skin[]>([])
const capes = ref<Cape[]>([])
const offline = ref(!navigator.onLine)
const accountsCard = inject('accountsCard') as Ref<typeof AccountsCard>
const currentUser = ref(undefined)
@@ -200,6 +211,16 @@ const savedSkins = computed(() => {
return []
}
})
const authServerQuery = useQuery({
queryKey: ['authServerReachability'],
queryFn: async () => {
await check_reachable()
return true
},
refetchInterval: 5 * 60 * 1000,
retry: false,
refetchOnWindowFocus: false,
})
const { data: modrinthUser } = useQuery({
queryKey: computed(() => ['authenticated-user', 'campaigns', auth.user.value?.id]),
queryFn: () => client.labrinth.users_v3.getAuthenticated(),
@@ -249,8 +270,18 @@ const currentCape = computed(() => {
})
const skinTexture = computedAsync(async () => {
if (selectedSkin.value?.texture) {
return await get_normalized_skin_texture(selectedSkin.value)
const skin = selectedSkin.value
if (skin?.texture) {
try {
return await get_normalized_skin_texture(skin)
} catch (error) {
if (skin.texture.startsWith('data:image/')) {
return skin.texture
}
handleError(error as Error)
return ''
}
} else {
return ''
}
@@ -258,6 +289,9 @@ const skinTexture = computedAsync(async () => {
const capeTexture = computed(() => currentCape.value?.texture)
const skinVariant = computed(() => selectedSkin.value?.variant)
const skinNametag = computed(() => (themeStore.hideNametagSkinsPage ? undefined : username.value))
const isSkinManagementReadOnly = computed(
() => offline.value || (authServerQuery.isError.value && !authServerQuery.isLoading.value),
)
const hasPendingSkinChange = computed(
() => !skinsMatch(selectedSkin.value, originalSelectedSkin.value),
)
@@ -274,11 +308,15 @@ const deleteSkinModal = ref()
const skinToDelete = ref<Skin | null>(null)
function confirmDeleteSkin(skin: Skin) {
if (isSkinManagementReadOnly.value) return
skinToDelete.value = skin
deleteSkinModal.value?.show()
}
async function deleteSkin() {
if (isSkinManagementReadOnly.value) return
const deletedSkin = skinToDelete.value
if (!deletedSkin) return
@@ -304,7 +342,23 @@ async function loadCapes() {
async function loadSkins() {
try {
skins.value = (await get_available_skins()) ?? []
const loadedSkins = (await get_available_skins()) ?? []
const loadedEquippedSkin = loadedSkins.find((s) => s.is_equipped)
const locallyKnownEquippedSkin =
originalSelectedSkin.value &&
(loadedSkins.find((skin) => skinsMatch(skin, originalSelectedSkin.value)) ??
(originalSelectedSkin.value.texture.startsWith('data:image/')
? originalSelectedSkin.value
: undefined))
const shouldPreserveKnownEquippedSkin =
isSkinManagementReadOnly.value &&
locallyKnownEquippedSkin &&
!skinsMatch(loadedEquippedSkin, locallyKnownEquippedSkin)
skins.value =
shouldPreserveKnownEquippedSkin && locallyKnownEquippedSkin
? mergeEquippedSkin(loadedSkins, locallyKnownEquippedSkin)
: loadedSkins
generateSkinPreviews(skins.value, capes.value)
selectedSkin.value = skins.value.find((s) => s.is_equipped) ?? null
originalSelectedSkin.value = selectedSkin.value
@@ -315,6 +369,28 @@ async function loadSkins() {
}
}
function mergeEquippedSkin(list: Skin[], equippedSkin: Skin) {
let foundEquippedSkin = false
const mergedSkins = list.map((skin) => {
const isEquipped = skinsMatch(skin, equippedSkin)
foundEquippedSkin ||= isEquipped
return {
...skin,
is_equipped: isEquipped,
}
})
if (!foundEquippedSkin) {
mergedSkins.unshift({
...equippedSkin,
is_equipped: true,
})
}
return mergedSkins
}
function skinsMatch(a?: Skin | null, b?: Skin | null) {
return (
a?.source === b?.source &&
@@ -324,6 +400,14 @@ function skinsMatch(a?: Skin | null, b?: Skin | null) {
)
}
function skinsMatchIgnoringSource(a?: Skin | null, b?: Skin | null) {
return (
a?.texture_key === b?.texture_key &&
a?.variant === b?.variant &&
(a?.cape_id ?? null) === (b?.cape_id ?? null)
)
}
function isSkinSelected(skin: Skin) {
return skinsMatch(selectedSkin.value, skin)
}
@@ -385,6 +469,8 @@ function getDefaultSkinSectionSortIndex(section: string) {
}
function changeSkin(newSkin: Skin) {
if (isSkinManagementReadOnly.value) return
selectedSkin.value = newSkin
}
@@ -423,6 +509,19 @@ function setLocallyEquippedSkin(skinToApply: Skin) {
void accountsCard.value?.setEquippedSkin(originalSelectedSkin.value)
}
function insertLocalSkin(savedSkin: Skin) {
const firstNonCustomSkinIndex = skins.value.findIndex((skin) => skin.source !== 'custom')
if (firstNonCustomSkinIndex === -1) {
skins.value = [...skins.value, savedSkin]
return
}
const nextSkins = [...skins.value]
nextSkins.splice(firstNonCustomSkinIndex, 0, savedSkin)
skins.value = nextSkins
}
function updateLocalSkin(savedSkin: Skin, applied: boolean, previousSkin?: Skin) {
let foundSkin = false
const replacesSelectedSkin =
@@ -451,7 +550,7 @@ function updateLocalSkin(savedSkin: Skin, applied: boolean, previousSkin?: Skin)
})
if (!foundSkin) {
skins.value.unshift({
insertLocalSkin({
...savedSkin,
is_equipped: applied || savedSkin.is_equipped,
})
@@ -480,6 +579,81 @@ function updateLocalSkin(savedSkin: Skin, applied: boolean, previousSkin?: Skin)
generateSkinPreviews(skins.value, capes.value)
}
async function reorderSavedSkins(orderedSkins: Skin[]) {
const previousSkins = skins.value
const previousSelectedSkin = selectedSkin.value
const previousOriginalSelectedSkin = originalSelectedSkin.value
const orderedTextureKeys = orderedSkins.map((skin) => skin.texture_key)
const orderedTextureKeySet = new Set(orderedTextureKeys)
const remainingSavedSkins = previousSkins.filter(
(skin) => skin.source !== 'default' && !orderedTextureKeySet.has(skin.texture_key),
)
const defaultSkins = previousSkins.filter((skin) => skin.source === 'default')
const nextSavedSkins = [...orderedSkins, ...remainingSavedSkins]
skins.value = [...nextSavedSkins, ...defaultSkins]
generateSkinPreviews(skins.value, capes.value)
try {
const persistedSavedSkins = await preserveExternalSkins(nextSavedSkins)
if (persistedSavedSkins.some((skin, index) => skin !== nextSavedSkins[index])) {
skins.value = [...persistedSavedSkins, ...defaultSkins]
generateSkinPreviews(skins.value, capes.value)
}
await set_custom_skin_order(
persistedSavedSkins
.filter((skin) => skin.source === 'custom')
.map((skin) => skin.texture_key),
)
} catch (error) {
skins.value = previousSkins
selectedSkin.value = previousSelectedSkin
originalSelectedSkin.value = previousOriginalSelectedSkin
generateSkinPreviews(skins.value, capes.value)
addNotification({
type: 'error',
title: formatMessage(messages.reorderSkinErrorTitle),
text: error instanceof Error ? error.message : formatMessage(messages.reorderSkinErrorText),
})
await loadSkins()
}
}
async function preserveExternalSkins(skinsToPersist: Skin[]) {
const preservedSkins: Skin[] = []
for (const skin of skinsToPersist) {
if (skin.source !== 'custom_external') {
preservedSkins.push(skin)
continue
}
const textureBlob = await normalize_skin_texture(skin.texture)
const capeId = skin.cape_id ? capes.value.find((cape) => cape.id === skin.cape_id) : undefined
const savedSkin = await save_custom_skin(skin, textureBlob, skin.variant, capeId, false)
const preservedSkin: Skin = {
...savedSkin,
source: 'custom',
is_equipped: skin.is_equipped,
}
if (skinsMatchIgnoringSource(selectedSkin.value, skin)) {
selectedSkin.value = preservedSkin
}
if (skinsMatchIgnoringSource(originalSelectedSkin.value, skin)) {
originalSelectedSkin.value = preservedSkin
void accountsCard.value?.setEquippedSkin(preservedSkin)
}
preservedSkins.push(preservedSkin)
}
return preservedSkins
}
function schedulePendingSkinRefresh() {
if (pendingSkinRefreshTimeout !== null) {
window.clearTimeout(pendingSkinRefreshTimeout)
@@ -517,7 +691,13 @@ function schedulePendingSkinRefresh() {
async function applySelectedSkin() {
const skinToApply = selectedSkin.value
if (!skinToApply || !hasPendingSkinChange.value || isApplyingSkin.value) return
if (
!skinToApply ||
!hasPendingSkinChange.value ||
isApplyingSkin.value ||
isSkinManagementReadOnly.value
)
return
isApplyingSkin.value = true
try {
@@ -586,10 +766,14 @@ async function login() {
}
function openAddSkinFileBrowser() {
if (isSkinManagementReadOnly.value) return
addSkinFileInput.value?.click()
}
async function onAddSkinFileInputChange(e: Event) {
if (isSkinManagementReadOnly.value) return
const files = (e.target as HTMLInputElement).files
const file = files?.[0]
@@ -632,6 +816,8 @@ function isPositionOverAddSkinButton(position: { x: number; y: number }) {
}
async function handleAddSkinNativeDragDrop(event: { payload: DragDropEvent }) {
if (isSkinManagementReadOnly.value) return
const payload = event.payload
if (payload.type === 'leave') {
@@ -680,6 +866,8 @@ async function handleAddSkinNativeDragDrop(event: { payload: DragDropEvent }) {
}
function onAddSkinDragOver(event: DragEvent) {
if (isSkinManagementReadOnly.value) return
if (!isSkinFileDrag(event)) {
return
}
@@ -688,10 +876,14 @@ function onAddSkinDragOver(event: DragEvent) {
}
function onAddSkinDragLeave() {
if (isSkinManagementReadOnly.value) return
isAddSkinButtonDragActive.value = false
}
async function onAddSkinDrop(event: DragEvent) {
if (isSkinManagementReadOnly.value) return
isAddSkinButtonDragActive.value = false
const file = Array.from(event.dataTransfer?.files ?? []).find(
@@ -721,6 +913,8 @@ async function setupAddSkinDragDropListener() {
}
async function processSkinFileBuffer(buffer: Uint8Array | ArrayBuffer) {
if (isSkinManagementReadOnly.value) return
const fakeEvent = new MouseEvent('click')
const originalSkinTexUrl = `data:image/png;base64,` + arrayBufferToBase64(buffer)
try {
@@ -740,13 +934,24 @@ watch(
() => {},
)
watch(isSkinManagementReadOnly, (readOnly) => {
if (readOnly) {
isDraggingSkinFile.value = false
isAddSkinButtonDragActive.value = false
}
})
onMounted(() => {
window.addEventListener('offline', onOffline)
window.addEventListener('online', onOnline)
userCheckInterval = window.setInterval(checkUserChanges, 250)
void setupAddSkinDragDropListener()
})
onUnmounted(() => {
isUnmounted = true
window.removeEventListener('offline', onOffline)
window.removeEventListener('online', onOnline)
if (userCheckInterval !== null) {
window.clearInterval(userCheckInterval)
@@ -763,6 +968,15 @@ onUnmounted(() => {
}
})
function onOffline() {
offline.value = true
}
function onOnline() {
offline.value = false
void authServerQuery.refetch()
}
async function checkUserChanges() {
try {
const defaultId = await get_default_user()
@@ -834,7 +1048,7 @@ await loadSkins()
>
<button
class="flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-surface-4 px-4 py-2.5 text-base font-semibold leading-5 text-contrast shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="isApplyingSkin"
:disabled="isApplyingSkin || isSkinManagementReadOnly"
@click="resetSelectedSkin"
>
<RotateCounterClockwiseIcon />
@@ -842,7 +1056,7 @@ await loadSkins()
</button>
<button
class="flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-brand px-4 py-2.5 text-base font-semibold leading-5 text-[rgba(0,0,0,0.9)] shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="isApplyingSkin"
:disabled="isApplyingSkin || isSkinManagementReadOnly"
@click="applySelectedSkin"
>
<SpinnerIcon v-if="isApplyingSkin" class="animate-spin" />
@@ -853,7 +1067,7 @@ await loadSkins()
<button
v-else
class="flex h-10 min-w-0 cursor-pointer items-center justify-center gap-2 rounded-[14px] border-0 bg-surface-4 px-4 py-2.5 text-base font-semibold leading-5 shadow-md transition-[filter,transform] duration-200 enabled:hover:brightness-[--hover-brightness] enabled:focus-visible:brightness-[--hover-brightness] enabled:active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-5 [&>svg]:shrink-0"
:disabled="!selectedSkin"
:disabled="!selectedSkin || isSkinManagementReadOnly"
@click="(e: MouseEvent) => selectedSkin && editSkinModal?.show(e, selectedSkin)"
>
<EditIcon />
@@ -873,9 +1087,11 @@ await loadSkins()
:is-skin-selected="isSkinSelected"
:is-skin-active="isSkinActive"
:is-add-skin-button-drag-active="isAddSkinButtonDragActive"
:read-only="isSkinManagementReadOnly"
@select="changeSkin"
@edit="(skin, event) => editSkinModal?.show(event, skin)"
@delete="confirmDeleteSkin"
@reorder-saved-skins="reorderSavedSkins"
@add-skin="openAddSkinFileBrowser"
@add-skin-dragenter="onAddSkinDragOver"
@add-skin-dragover="onAddSkinDragOver"
+101 -13
View File
@@ -90,6 +90,7 @@ import {
useVIntl,
versionChangesGameVersion,
} from '@modrinth/ui'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { open } from '@tauri-apps/plugin-dialog'
import { openUrl } from '@tauri-apps/plugin-opener'
@@ -99,7 +100,7 @@ import { useRouter } from 'vue-router'
import ExportModal from '@/components/ui/ExportModal.vue'
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { get_project_versions, get_version } from '@/helpers/cache.js'
import { get_project_versions, get_version, get_version_many } from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events.js'
import { type InstanceContentData, loadInstanceContentData } from '@/helpers/instance-content'
import {
@@ -153,6 +154,7 @@ const { formatMessage } = useVIntl()
const { handleError, addNotification } = injectNotificationManager()
const { installingItems } = injectContentInstall()
const router = useRouter()
const queryClient = useQueryClient()
const debug = useDebugLogger('Mods:ContentUpdate')
const props = defineProps<{
@@ -212,6 +214,13 @@ const contentUpdaterModal = ref<InstanceType<typeof ContentUpdaterModal> | null>
const modpackContentModal = ref<InstanceType<typeof ModpackContentModal> | null>()
const modpackUpdateConfirmModal = ref<InstanceType<typeof ConfirmModpackUpdateModal> | null>()
const modpackContentQueryKey = computed(() => ['linkedModpackContent', props.instance.path])
const modpackContentQuery = useQuery({
queryKey: modpackContentQueryKey,
queryFn: () => get_linked_modpack_content(props.instance.path),
enabled: computed(() => !!props.instance?.path && !!props.instance?.linked_data),
})
// TODO: Extract content operation and updater modal state into composables; this page currently owns file mutations, dependency installs, busy flags, and version selection flow.
const updatingProject = ref<ContentItem | null>(null)
const updatingProjectVersions = ref<Labrinth.Versions.v2.Version[]>([])
@@ -451,6 +460,63 @@ async function removeMod(mod: ContentItem) {
}
}
function isBreakingDependency(dependency: Labrinth.Versions.v2.Dependency) {
return dependency.dependency_type === 'required' || dependency.dependency_type === 'embedded'
}
function dependencyTargetsItem(dependency: Labrinth.Versions.v2.Dependency, item: ContentItem) {
return (
(!!dependency.project_id && dependency.project_id === item.project?.id) ||
('version_id' in dependency &&
!!dependency.version_id &&
dependency.version_id === item.version?.id)
)
}
async function getDeleteDependencyWarning(items: ContentItem[]) {
if (props.isServerInstance) return null
const deletingIds = new Set(items.map(getContentItemId))
const remainingItems = projects.value.filter((item) => !deletingIds.has(getContentItemId(item)))
const versionIds = [
...new Set(remainingItems.map((item) => item.version?.id).filter((id): id is string => !!id)),
]
if (versionIds.length === 0) return null
const versions = (await get_version_many(versionIds).catch((err) => {
handleError(err as Error)
return null
})) as Labrinth.Versions.v2.Version[] | null
if (!versions) return null
const versionsById = new Map(versions.map((version) => [version.id, version]))
const dependents = remainingItems
.map((candidate) => {
const version = candidate.version?.id ? versionsById.get(candidate.version.id) : null
if (!version) return null
const dependencies = items.filter((item) => {
if (!item.project?.id && !item.version?.id) return false
return version.dependencies?.some(
(dependency) =>
isBreakingDependency(dependency) && dependencyTargetsItem(dependency, item),
)
})
return dependencies.length > 0 ? { item: candidate, dependencies } : null
})
.filter(
(dependent): dependent is { item: ContentItem; dependencies: ContentItem[] } =>
dependent !== null,
)
return dependents.length > 0 ? { items, dependents } : null
}
async function updateProject(mod: ContentItem) {
if (!mod.file_path) return
const operation = beginContentOperation(mod)
@@ -481,6 +547,7 @@ async function updateProject(mod: ContentItem) {
})
} catch (err) {
handleError(err as Error)
throw err
} finally {
await refreshContentState('must_revalidate')
finishContentOperation(mod, operation)
@@ -682,13 +749,19 @@ async function handleModpackContentBulkToggle(items: ContentItem[]) {
async function handleModpackContent() {
if (!props.instance?.path) return
if (modpackContentQuery.data.value !== undefined) {
modpackContentModal.value?.show(modpackContentQuery.data.value)
return
}
modpackContentModal.value?.showLoading()
const contentItems = await get_linked_modpack_content(props.instance.path).catch(handleError)
const { data, error } = await modpackContentQuery.refetch()
if (contentItems) {
modpackContentModal.value?.show(contentItems)
if (data !== undefined) {
modpackContentModal.value?.show(data)
} else {
if (error) handleError(error)
modpackContentModal.value?.hide()
}
}
@@ -696,9 +769,12 @@ async function handleModpackContent() {
async function refreshModpackContentItems(cacheBehaviour?: CacheBehaviour) {
if (!props.instance?.path) return
const contentItems = await get_linked_modpack_content(props.instance.path, cacheBehaviour).catch(
handleError,
)
const contentItems = await queryClient
.fetchQuery({
queryKey: modpackContentQueryKey.value,
queryFn: () => get_linked_modpack_content(props.instance.path, cacheBehaviour),
})
.catch(handleError)
if (contentItems) {
modpackContentModal.value?.setItems(contentItems)
@@ -885,13 +961,15 @@ async function handleModalUpdate(
} else if (updatingProject.value) {
const mod = updatingProject.value
if (mod.has_update && mod.update_version_id === selectedVersion.id) {
await updateProject(mod)
} else {
await switchProjectVersion(mod, selectedVersion)
try {
if (mod.has_update && mod.update_version_id === selectedVersion.id) {
await updateProject(mod)
} else {
await switchProjectVersion(mod, selectedVersion)
}
} finally {
resetUpdateState()
}
resetUpdateState()
}
}
@@ -1081,6 +1159,7 @@ provideContentManager({
deleteItem: removeMod,
bulkDeleteItems: (items: ContentItem[]) =>
Promise.all(items.map((item) => removeMod(item))).then(() => {}),
getDeleteDependencyWarning,
refresh: () => initProjects('must_revalidate'),
browse: handleBrowseContent,
uploadFiles: handleUploadFiles,
@@ -1227,6 +1306,15 @@ watch(
},
)
watch(
() => props.instance?.preferred_update_channel,
async (newValue, oldValue) => {
if (newValue !== oldValue) {
await initProjects('must_revalidate')
}
},
)
onUnmounted(() => {
isUnmounted = true
removeBeforeEach()
+18 -3
View File
@@ -406,6 +406,20 @@ function buildProjectHref(path, extraQuery = {}) {
return qs ? `${path}?${qs}` : path
}
function buildBrowseHref(path) {
const params = new URLSearchParams()
for (const [key, val] of Object.entries(route.query)) {
if (key === 'b') continue
if (Array.isArray(val)) {
for (const v of val) params.append(key, v)
} else if (val) {
params.append(key, String(val))
}
}
const qs = params.toString()
return qs ? `${path}?${qs}` : path
}
const projectDescriptionHref = computed(() => buildProjectHref(`/project/${route.params.id}`))
const versionsHref = computed(() =>
buildProjectHref(`/project/${route.params.id}/versions`, instanceFilters.value),
@@ -416,7 +430,7 @@ const projectBrowseBackUrl = computed(() => {
const browsePath = route.query.b
if (typeof browsePath === 'string' && browsePath.startsWith('/browse/')) return browsePath
const type = data.value?.project_type ? `${data.value.project_type}` : 'mod'
return `/browse/${type}`
return buildBrowseHref(`/browse/${type}`)
})
const projectInstallContext = computed(() => {
@@ -725,10 +739,11 @@ async function install(version) {
version,
instance.value ? instance.value.path : null,
'ProjectPage',
(version) => {
(version, installedProjectIds) => {
installing.value = false
if (instance.value && version) {
const installedIds = installedProjectIds ?? [data.value.id]
if (instance.value && version && installedIds.includes(data.value.id)) {
installed.value = true
installedVersion.value = version
}
@@ -5,7 +5,7 @@
:current-title="version.name"
:link-stack="[
{
href: `/project/${route.params.id}/versions`,
href: buildProjectHref(`/project/${route.params.id}/versions`),
label: 'Versions',
},
]"
@@ -249,6 +249,19 @@ const author = computed(() =>
const displayDependencies = ref({})
function buildProjectHref(path) {
const params = new URLSearchParams()
for (const [key, val] of Object.entries(route.query)) {
if (Array.isArray(val)) {
for (const v of val) params.append(key, v)
} else if (val) {
params.append(key, String(val))
}
}
const qs = params.toString()
return qs ? `${path}?${qs}` : path
}
async function refreshDisplayDependencies() {
const projectIds = new Set()
const versionIds = new Set()
@@ -282,7 +295,7 @@ async function refreshDisplayDependencies() {
icon: project?.icon_url,
title: project?.title || project?.name,
subtitle: `Version ${version.version_number} is ${dependency.dependency_type}`,
link: `/project/${project.slug}/version/${version.id}`,
link: buildProjectHref(`/project/${project.slug}/version/${version.id}`),
}
} else {
const project = dependencies.projects.find((obj) => obj.id === dependency.project_id)
@@ -292,7 +305,7 @@ async function refreshDisplayDependencies() {
icon: project?.icon_url,
title: project?.title || project?.name,
subtitle: `${dependency.dependency_type}`,
link: `/project/${project.slug}`,
link: buildProjectHref(`/project/${project.slug}`),
}
} else {
return {
@@ -5,7 +5,7 @@
:game-versions="gameVersions"
:versions="versions"
:project="project"
:version-link="(version) => `/project/${project.id}/version/${version.id}`"
:version-link="(version) => buildProjectHref(`/project/${project.id}/version/${version.id}`)"
>
<template #actions="{ version }">
<ButtonStyled circular type="transparent">
@@ -73,6 +73,7 @@ import {
ProjectPageVersions,
} from '@modrinth/ui'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import { SwapIcon } from '@/assets/icons/index.js'
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
@@ -109,6 +110,20 @@ defineProps({
})
const { handleError } = injectNotificationManager()
const route = useRoute()
function buildProjectHref(path) {
const params = new URLSearchParams()
for (const [key, val] of Object.entries(route.query)) {
if (Array.isArray(val)) {
for (const v of val) params.append(key, v)
} else if (val) {
params.append(key, String(val))
}
}
const qs = params.toString()
return qs ? `${path}?${qs}` : path
}
const [loaders, gameVersions] = await Promise.all([
get_loaders().catch(handleError).then(ref),
@@ -0,0 +1,171 @@
import { computed, ref } from 'vue'
export const APP_UPDATE_POPUP_DELAY_MS = 24 * 60 * 60 * 1000
const UPDATE_PROMPT_STORAGE_KEY = 'modrinth-app-update-prompt-state'
export interface AppUpdate {
rid: number
version: string
currentVersion?: string
}
interface UpdatePromptState {
version: string
stage: AppUpdatePromptStage
actionableSince: number
lastUserActionAt?: number
popupShownAt?: number
}
export type AppUpdatePromptStage = 'available' | 'downloaded'
interface AppUpdateActions {
download?: () => Promise<void> | void
install?: () => Promise<void> | void
changelog?: () => Promise<void> | void
}
const progress = ref(0)
const metered = ref(true)
const finishedDownloading = ref(false)
const downloading = ref(false)
const restarting = ref(false)
const availableUpdate = ref<AppUpdate | null>(null)
const updateSize = ref<number | null>(null)
const updatesEnabled = ref(true)
let actions: AppUpdateActions = {}
function getCurrentAppUpdatePromptStage(): AppUpdatePromptStage {
return finishedDownloading.value ? 'downloaded' : 'available'
}
export const appUpdateState = {
progress,
metered,
finishedDownloading,
downloading,
restarting,
availableUpdate,
updateSize,
updatesEnabled,
downloadProgress: computed(() => progress.value),
downloadPercent: computed(() => Math.trunc(progress.value * 100)),
isVisible: computed(() => !!availableUpdate.value && !restarting.value && updatesEnabled.value),
}
function readPromptState(): UpdatePromptState | null {
try {
const raw = localStorage.getItem(UPDATE_PROMPT_STORAGE_KEY)
if (!raw) {
return null
}
const parsed = JSON.parse(raw) as Partial<UpdatePromptState>
if (!parsed.version || typeof parsed.actionableSince !== 'number') {
return null
}
return {
...parsed,
stage: parsed.stage ?? 'available',
} as UpdatePromptState
} catch {
return null
}
}
function writePromptState(state: UpdatePromptState): void {
try {
localStorage.setItem(UPDATE_PROMPT_STORAGE_KEY, JSON.stringify(state))
} catch (error) {
console.warn('Failed to persist update prompt state:', error)
}
}
export function markAppUpdateActionable(
version: string,
stage: AppUpdatePromptStage = 'available',
now = Date.now(),
): void {
const existing = readPromptState()
if (existing?.version === version && existing.stage === stage) {
return
}
writePromptState({
version,
stage,
actionableSince: now,
})
}
export function recordAppUpdateUserAction(
version = availableUpdate.value?.version,
stage: AppUpdatePromptStage = getCurrentAppUpdatePromptStage(),
): void {
if (!version) {
return
}
const now = Date.now()
const existing = readPromptState()
const isSamePrompt = existing?.version === version && existing.stage === stage
writePromptState({
version,
stage,
actionableSince: isSamePrompt ? existing.actionableSince : now,
lastUserActionAt: now,
popupShownAt: isSamePrompt ? existing.popupShownAt : undefined,
})
}
export function markAppUpdatePopupShown(
version: string,
stage: AppUpdatePromptStage = 'available',
now = Date.now(),
): void {
const existing = readPromptState()
const isSamePrompt = existing?.version === version && existing.stage === stage
writePromptState({
version,
stage,
actionableSince: isSamePrompt ? existing.actionableSince : now,
lastUserActionAt: isSamePrompt ? existing.lastUserActionAt : undefined,
popupShownAt: now,
})
}
export function getNextAppUpdatePopupTime(
version: string,
stage: AppUpdatePromptStage = 'available',
): number | null {
const existing = readPromptState()
if (existing?.version !== version || existing.stage !== stage || existing.popupShownAt) {
return null
}
return (
Math.max(existing.actionableSince, existing.lastUserActionAt ?? 0) + APP_UPDATE_POPUP_DELAY_MS
)
}
export function setAppUpdateActions(nextActions: AppUpdateActions): void {
actions = nextActions
}
export async function downloadAvailableAppUpdate(): Promise<void> {
recordAppUpdateUserAction(undefined, 'available')
await actions.download?.()
}
export async function installAvailableAppUpdate(): Promise<void> {
recordAppUpdateUserAction(undefined, 'downloaded')
await actions.install?.()
}
export async function openAppUpdateChangelog(): Promise<void> {
recordAppUpdateUserAction()
await actions.changelog?.()
}
@@ -42,6 +42,8 @@ interface ModpackAlreadyInstalledModalRef {
show: (instanceName: string, instancePath: string) => void
}
export type ContentInstallCallback = (versionId?: string, installedProjectIds?: string[]) => void
const LOADER_ORDER = ['vanilla', 'fabric', 'quilt', 'neoforge', 'forge']
const SUPPORTED_LOADERS: Set<string> = new Set(['vanilla', 'forge', 'fabric', 'quilt', 'neoforge'])
const VANILLA_COMPATIBLE_LOADERS: Set<string> = new Set(['minecraft', 'datapack'])
@@ -102,7 +104,7 @@ export interface ContentInstallContext {
versionId?: string | null,
instancePath?: string | null,
source?: string,
callback?: (versionId?: string) => void,
callback?: ContentInstallCallback,
createInstanceCallback?: (profile: string) => void,
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
) => Promise<void>
@@ -256,25 +258,25 @@ export function createContentInstall(opts: {
let incompatibilityWarningModalRef: ModalRef | null = null
let currentProject: Labrinth.Projects.v2.Project | null = null
let currentVersions: Labrinth.Versions.v2.Version[] = []
let currentCallback: (versionId?: string) => void = () => {}
let currentCallback: ContentInstallCallback = () => {}
let profileMap: Record<string, GameInstance> = {}
let incompatibilityWarningInstance: GameInstance | null = null
let incompatibilityWarningProject: Labrinth.Projects.v2.Project | null = null
let incompatibilityWarningCallback: (versionId?: string) => void = () => {}
let incompatibilityWarningCallback: ContentInstallCallback = () => {}
let incompatibilityWarningInstalled = false
let pendingModpackInstall: {
project: Labrinth.Projects.v2.Project
version: string
source: string
callback: (versionId?: string) => void
callback: ContentInstallCallback
createInstanceCallback: (profile: string) => void
} | null = null
async function showModInstallModal(
project: Labrinth.Projects.v2.Project,
versions: Labrinth.Versions.v2.Version[],
onInstall: (versionId?: string) => void,
onInstall: ContentInstallCallback,
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
) {
currentProject = project
@@ -440,7 +442,7 @@ export function createContentInstall(opts: {
if (versionId && storeInstance) {
storeInstance.installed = true
}
currentCallback(versionId)
currentCallback(versionId, versionId && currentProject ? [currentProject.id] : undefined)
}
await showIncompatibilityWarning(
profile,
@@ -487,7 +489,7 @@ export function createContentInstall(opts: {
title: currentProject!.title,
source: 'ProjectInstallModal',
})
currentCallback(version.id)
currentCallback(version.id, installedProjectIds)
} catch (err) {
if (storeInstance) storeInstance.installing = false
opts.handleError(err)
@@ -501,7 +503,7 @@ export function createContentInstall(opts: {
project: Labrinth.Projects.v2.Project,
versions: Labrinth.Versions.v2.Version[],
version: Labrinth.Versions.v2.Version,
callback: (versionId?: string) => void,
callback: ContentInstallCallback,
) {
incompatibilityWarningInstance = instance
incompatibilityWarningProject = project
@@ -542,7 +544,7 @@ export function createContentInstall(opts: {
incompatibilityWarningInstalling.value = false
incompatibilityWarningInstalled = true
incompatibilityWarningCallback(version.id)
incompatibilityWarningCallback(version.id, [incompatibilityWarningProject.id])
incompatibilityWarningModalRef?.hide()
trackEvent('ProjectInstall', {
@@ -630,7 +632,7 @@ export function createContentInstall(opts: {
versionId?: string | null,
instancePath?: string | null,
source: string = 'unknown',
callback: (versionId?: string) => void = () => {},
callback: ContentInstallCallback = () => {},
createInstanceCallback: (profile: string) => void = () => {},
hints?: { preferredLoader?: string; preferredGameVersion?: string; showProjectInfo?: boolean },
) {
@@ -714,7 +716,7 @@ export function createContentInstall(opts: {
title: project.title,
source,
})
callback(version.id)
callback(version.id, installedProjectIds)
} finally {
removeInstallingItems(instancePath, installedProjectIds)
}
+2
View File
@@ -1,6 +1,7 @@
import type { AbstractPopupNotificationManager, AbstractWebNotificationManager } from '@modrinth/ui'
import { setupCreationModal } from './setup/creation-modal'
import { setupFileDropProvider } from './setup/file-drop'
import { setupFilePickerProvider } from './setup/file-picker'
import { setupInstanceImportProvider } from './setup/instance-import'
import { setupTagsProvider } from './setup/tags'
@@ -10,6 +11,7 @@ export function setupProviders(
popupNotificationManager: AbstractPopupNotificationManager,
) {
setupTagsProvider(notificationManager)
setupFileDropProvider()
setupFilePickerProvider()
setupInstanceImportProvider(notificationManager)
@@ -0,0 +1,62 @@
import { provideFileDrop } from '@modrinth/ui'
import { invoke } from '@tauri-apps/api/core'
import type { DragDropEvent } from '@tauri-apps/api/webview'
import { getCurrentWebview } from '@tauri-apps/api/webview'
function getFileName(path: string) {
return path.split(/[\\/]/).pop() || 'file'
}
function toLogicalPosition(position: { x: number; y: number }) {
const scale = window.devicePixelRatio || 1
return {
x: position.x / scale,
y: position.y / scale,
}
}
async function readDraggedFile(path: string) {
const data = await invoke<number[]>('plugin:files|file_read_dragged_file', { path })
return new Uint8Array(data)
}
export function setupFileDropProvider() {
let nativeFileDropPaths: string[] = []
provideFileDrop({
async listenNativeFileDrop(handler) {
return await getCurrentWebview().onDragDropEvent((event: { payload: DragDropEvent }) => {
const payload = event.payload
if (payload.type === 'leave') {
nativeFileDropPaths = []
void handler({
type: 'leave',
paths: [],
position: { x: 0, y: 0 },
})
return
}
if (payload.type === 'enter' || payload.type === 'drop') {
nativeFileDropPaths = payload.paths
}
void handler({
type: payload.type,
paths: nativeFileDropPaths,
position: toLogicalPosition(payload.position),
})
if (payload.type === 'drop') {
nativeFileDropPaths = []
}
})
},
async createFilesFromNativePaths(paths) {
return await Promise.all(
paths.map(async (path) => new File([await readDraggedFile(path)], getFileName(path))),
)
},
})
}
-1
View File
@@ -248,7 +248,6 @@ export default new createRouter({
component: Instance.Logs,
meta: {
useRootContext: true,
// renderMode: 'fixed',
breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Logs' }],
},
},
+4 -1
View File
@@ -1,5 +1,7 @@
import { defineStore } from 'pinia'
import { findMinecraftAuthError } from '@/components/ui/minecraft-auth-error-modal/minecraft-auth-errors'
export const useError = defineStore('errorsStore', {
state: () => ({
errorModal: null,
@@ -15,7 +17,8 @@ export const useError = defineStore('errorsStore', {
showError(error, context, closable = true, source = null) {
if (
error.message &&
error.message.includes('Minecraft authentication error:') &&
(error.message.includes('Minecraft authentication error:') ||
findMinecraftAuthError(error.message)) &&
this.minecraftAuthErrorModal
) {
this.minecraftAuthErrorModal.show(error)
+9 -5
View File
@@ -71,7 +71,7 @@ export const installVersionDependencies = async (profile, version, reason, onDep
return installed
}
const queueInstall = async (projectId, resolvedVersion) => {
const queueInstall = async (projectId, resolvedVersion, dependentOn) => {
if (!resolvedVersion?.id) return false
const versionId = resolvedVersion.id
@@ -91,7 +91,11 @@ export const installVersionDependencies = async (profile, version, reason, onDep
if (resolvedProjectId) {
queuedProjectVersions.set(resolvedProjectId, versionId)
}
queuedInstalls.push({ versionId, projectId: resolvedProjectId })
queuedInstalls.push({
versionId,
projectId: resolvedProjectId,
dependentOnVersionId: dependentOn?.id,
})
return true
}
@@ -159,7 +163,7 @@ export const installVersionDependencies = async (profile, version, reason, onDep
if (!resolved) continue
const { depVersion, depProjectId } = resolved
const queued = await queueInstall(depProjectId, depVersion)
const queued = await queueInstall(depProjectId, depVersion, inputVersion)
if (queued && depProjectId) {
await announceDependency(depProjectId, depVersion)
}
@@ -176,8 +180,8 @@ export const installVersionDependencies = async (profile, version, reason, onDep
for (let i = 0; i < queuedInstalls.length; i += batchSize) {
const batch = queuedInstalls.slice(i, i + batchSize)
await Promise.all(
batch.map(async ({ versionId }) => {
await add_project_from_version(profile.path, versionId, reason)
batch.map(async ({ versionId, dependentOnVersionId }) => {
await add_project_from_version(profile.path, versionId, reason, dependentOnVersionId)
}),
)
}
+11 -8
View File
@@ -1,5 +1,7 @@
import { defineStore } from 'pinia'
let systemThemeMq: MediaQueryList | null = null
export const DEFAULT_FEATURE_FLAGS = {
project_background: false,
page_path: false,
@@ -53,21 +55,22 @@ export const useTheming = defineStore('themeStore', {
this.setThemeClass()
},
setThemeClass() {
const html = document.getElementsByTagName('html')[0]
for (const theme of THEME_OPTIONS) {
document.getElementsByTagName('html')[0].classList.remove(`${theme}-mode`)
html.classList.remove(`${theme}-mode`)
}
systemThemeMq?.removeEventListener('change', this.setThemeClass)
systemThemeMq = null
let theme = this.selectedTheme
if (this.selectedTheme === 'system') {
const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)')
if (darkThemeMq.matches) {
theme = 'dark'
} else {
theme = 'light'
}
systemThemeMq = window.matchMedia('(prefers-color-scheme: dark)')
systemThemeMq.addEventListener('change', this.setThemeClass)
theme = systemThemeMq.matches ? 'dark' : 'light'
}
document.getElementsByTagName('html')[0].classList.add(`${theme}-mode`)
html.classList.add(`${theme}-mode`)
},
getFeatureFlag(key: FeatureFlag) {
return this.featureFlags[key] ?? DEFAULT_FEATURE_FLAGS[key]
+6 -1
View File
@@ -117,6 +117,7 @@ fn main() {
"equip_skin",
"remove_custom_skin",
"save_custom_skin",
"set_custom_skin_order",
"unequip_skin",
"flush_pending_skin_change",
"flush_pending_skin_change_for_profile",
@@ -270,7 +271,11 @@ fn main() {
.plugin(
"files",
InlinedPlugin::new()
.commands(&["file_extract_zip", "file_save_as"])
.commands(&[
"file_extract_zip",
"file_save_as",
"file_read_dragged_file",
])
.default_permission(
DefaultPermissionRule::AllowAllCommands,
),
+14
View File
@@ -11,6 +11,7 @@ pub fn init<R: Runtime>() -> tauri::plugin::TauriPlugin<R> {
.invoke_handler(tauri::generate_handler![
file_extract_zip,
file_save_as,
file_read_dragged_file,
])
.build()
}
@@ -21,6 +22,19 @@ pub struct ExtractDryRunResult {
conflicting_files: Vec<String>,
}
#[tauri::command]
pub async fn file_read_dragged_file(path: String) -> Result<Vec<u8>> {
let metadata = tokio::fs::metadata(&path).await?;
if !metadata.is_file() {
return Err(theseus::Error::from(theseus::ErrorKind::OtherError(
"Dropped path is not a file".to_string(),
))
.into());
}
Ok(tokio::fs::read(path).await?)
}
#[tauri::command]
pub async fn file_extract_zip(
instance_path: &str,
+9
View File
@@ -14,6 +14,7 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
equip_skin,
remove_custom_skin,
save_custom_skin,
set_custom_skin_order,
unequip_skin,
flush_pending_skin_change,
flush_pending_skin_change_for_profile,
@@ -91,6 +92,14 @@ pub async fn save_custom_skin(
.await?)
}
/// `invoke('plugin:minecraft-skins|set_custom_skin_order', texture_keys)`
///
/// See also: [minecraft_skins::set_custom_skin_order]
#[tauri::command]
pub async fn set_custom_skin_order(texture_keys: Vec<String>) -> Result<()> {
Ok(minecraft_skins::set_custom_skin_order(texture_keys).await?)
}
/// `invoke('plugin:minecraft-skins|unequip_skin')`
///
/// See also: [minecraft_skins::unequip_skin]
+14 -1
View File
@@ -251,8 +251,15 @@ pub async fn profile_add_project_from_version(
path: &str,
version_id: &str,
reason: DownloadReason,
dependent_on_version_id: Option<String>,
) -> Result<String> {
Ok(profile::add_project_from_version(path, version_id, reason).await?)
Ok(profile::add_project_from_version(
path,
version_id,
reason,
dependent_on_version_id,
)
.await?)
}
// Adds a project to a profile from a path
@@ -385,6 +392,7 @@ pub struct EditProfile {
with = "serde_with::rust::double_option"
)]
pub linked_data: Option<Option<LinkedData>>,
pub preferred_update_channel: Option<ReleaseChannel>,
#[serde(
default,
@@ -449,6 +457,11 @@ pub async fn profile_edit(path: &str, edit_profile: EditProfile) -> Result<()> {
if let Some(linked_data) = edit_profile.linked_data.clone() {
prof.linked_data = linked_data;
}
if let Some(preferred_update_channel) =
edit_profile.preferred_update_channel
{
prof.preferred_update_channel = preferred_update_channel;
}
if let Some(groups) = edit_profile.groups.clone() {
prof.groups = groups;
}
+5
View File
@@ -307,6 +307,11 @@ fn main() {
}
set_changelog_toast(Some(update.version.clone()));
let update = if should_restart {
(**update).clone()
} else {
(**update).clone().restart_after_install(false)
};
match update.install(data) {
Ok(()) => {
if should_restart {
+4 -1
View File
@@ -31,7 +31,10 @@
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDIwMzM5QkE0M0FCOERBMzkKUldRNTJyZzZwSnN6SUdPRGdZREtUUGxMblZqeG9OVHYxRUlRTzJBc2U3MUNJaDMvZDQ1UytZZmYK",
"endpoints": ["https://launcher-files.modrinth.com/updates.json"]
"endpoints": ["https://launcher-files.modrinth.com/updates.json"],
"windows": {
"installMode": "quiet"
}
}
}
}
+14
View File
@@ -432,6 +432,20 @@ components:
primary_file:
type: string
description: The multipart field name of the primary file
environment:
type: string
enum:
- client_and_server
- client_only
- client_only_server_optional
- singleplayer_only
- server_only
- server_only_client_optional
- dedicated_server_only
- client_or_server
- client_or_server_prefers_both
- unknown
description: The environment that this version is for.
required:
- file_parts
- project_id
@@ -756,16 +756,16 @@ svg.inline-svg {
padding: var(--gap-16);
h2 {
@apply font-semibold;
font-size: var(--text-18);
font-weight: var(--weight-extrabold);
color: var(--color-contrast);
line-height: initial;
margin: 0;
}
h3 {
@apply font-semibold;
font-size: var(--text-16);
font-weight: var(--weight-bold);
color: var(--color-base);
margin: 0;
}
@@ -812,6 +812,7 @@ svg.inline-svg {
}
.details-list__item {
@apply font-normal;
@extend .iconified-list-item;
.details-list__item__text--style-secondary {
@@ -25,7 +25,7 @@
class="inline-flex items-center"
>
<button
v-tooltip="legendEntry.projectName ?? ''"
v-tooltip="legendEntry.tooltip ?? legendEntry.projectName ?? ''"
type="button"
class="inline-flex items-center gap-1.5 px-2 py-0.5 text-sm !outline-0 transition-all focus-within:!outline-0 focus:!outline-0 focus-visible:!outline-0"
:class="[
@@ -64,6 +64,7 @@ export function useAnalyticsChartLegend({
id: dataset.projectId,
name: dataset.label,
projectName: dataset.projectName,
tooltip: dataset.tooltip,
color: dataset.borderColor,
totalValue: getChartDatasetTotal(dataset),
hidden: hiddenDatasetIds.value.has(dataset.projectId),
@@ -118,6 +119,7 @@ export function useAnalyticsChartLegend({
id: getPreviousPeriodDatasetId(entry.id),
name: formatMessage(analyticsChartMessages.previousPeriodSuffix, { name: entry.name }),
projectName: entry.projectName,
tooltip: entry.tooltip,
color: entry.color,
totalValue: previousDataset ? getChartDatasetTotal(previousDataset) : 0,
hidden: hiddenDatasetIds.value.has(getPreviousPeriodDatasetId(entry.id)),
@@ -153,6 +155,7 @@ export function useAnalyticsChartLegend({
name: dataset.label,
}),
projectName: dataset.projectName,
tooltip: dataset.tooltip,
data: previousData,
borderColor: dataset.borderColor,
backgroundColor: dataset.backgroundColor,
@@ -296,7 +299,7 @@ export function useAnalyticsChartLegend({
}
function getLegendEntryTooltip(legendEntry: AnalyticsChartLegendEntry) {
return legendEntry.projectName ?? ''
return legendEntry.tooltip ?? legendEntry.projectName ?? ''
}
function isUnmonetizedLegendEntry(legendEntry: AnalyticsChartLegendEntry) {
@@ -97,7 +97,7 @@
"
/>
<span
v-tooltip="entry.projectName ?? ''"
v-tooltip="entry.tooltip ?? entry.projectName ?? ''"
class="min-w-0 truncate"
:class="{
'line-through': entry.hidden,
@@ -145,6 +145,7 @@ export type AnalyticsChartTooltipEntry = {
projectId: string
name: string
projectName?: string
tooltip?: string
color: string
formattedValue: string
hidden: boolean
@@ -168,6 +168,14 @@ const {
onRangeSelected: (start, end, groupBy) => emit('range-select', start, end, groupBy),
})
function getTooltipTotalMetricValue(value: number): number {
if (props.activeStat === 'revenue' && Math.abs(value) < 1) {
return Math.round(value * 100) / 100
}
return value
}
const hoverTotalValue = computed(() => {
if (hoverState.sliceIndex === null) return 0
const sliceIndex = hoverState.sliceIndex
@@ -176,7 +184,7 @@ const hoverTotalValue = computed(() => {
return props.currentLegendEntries.reduce((sum, legendEntry) => {
if (legendEntry.hidden) return sum
const dataset = props.chartDatasetById.get(legendEntry.id)
return sum + (dataset?.data[sliceIndex] ?? 0)
return sum + getTooltipTotalMetricValue(dataset?.data[sliceIndex] ?? 0)
}, 0)
})
@@ -200,6 +208,7 @@ const hoverEntries = computed(() => {
projectId: legendEntry.id,
name: legendEntry.name,
projectName: legendEntry.projectName,
tooltip: legendEntry.tooltip,
color: legendEntry.color,
formattedValue: props.isRatioMode
? `${ratioValue.toFixed(1)}%`
@@ -14,6 +14,7 @@ export type AnalyticsChartLegendEntry = {
id: string
name: string
projectName?: string
tooltip?: string
color: string
totalValue: number
hidden: boolean
@@ -1,20 +1,24 @@
import type { Labrinth } from '@modrinth/api-client'
import type {
AnalyticsBreakdownPreset,
AnalyticsDashboardProject,
AnalyticsDashboardStat,
AnalyticsGroupByPreset,
import {
type AnalyticsBreakdownPreset,
type AnalyticsDashboardProject,
type AnalyticsDashboardStat,
type AnalyticsGroupByPreset,
type AnalyticsSelectedFilters,
doesAnalyticsPointMatchNormalizedFilters,
normalizeAnalyticsSelectedFilters,
} from '~/providers/analytics/analytics'
import type { FormatMessage } from '../analytics-messages'
import {
analyticsChartMessages,
analyticsMessages,
analyticsStatCardMessages,
formatAnalyticsDependentProjectFallbackLabel,
formatAnalyticsDownloadReasonLabel,
formatAnalyticsLoaderLabel,
formatAnalyticsMonetizationLabel,
type FormatMessage,
} from '../analytics-messages'
import {
ALL_BREAKDOWN_VALUE,
@@ -22,6 +26,8 @@ import {
getAnalyticsBreakdownDatasetId,
getAnalyticsBreakdownKey,
getAnalyticsBreakdownValues,
isNoDependentAnalyticsBreakdownValue,
isUnknownAnalyticsBreakdownValue,
UNKNOWN_BREAKDOWN_VALUE,
} from '../breakdown'
import { PREVIOUS_PERIOD_DATASET_ID_PREFIX } from './analytics-chart-constants'
@@ -30,6 +36,7 @@ export type ChartDataset = {
projectId: string
label: string
projectName?: string
tooltip?: string
data: number[]
borderColor: string
backgroundColor: string
@@ -136,6 +143,7 @@ export function formatBreakdownLabel(
breakdownValue: string,
selectedBreakdown: AnalyticsBreakdownPreset,
getVersionDisplayName: ((versionId: string) => string) | undefined,
userNamesById: ReadonlyMap<string, string> | undefined,
formatMessage: FormatMessage,
): string {
const normalizedValue = breakdownValue.trim()
@@ -160,6 +168,15 @@ export function formatBreakdownLabel(
if (selectedBreakdown === 'download_reason') {
return formatAnalyticsDownloadReasonLabel(normalizedLowercaseValue, formatMessage)
}
if (selectedBreakdown === 'dependent_project_download') {
if (isNoDependentAnalyticsBreakdownValue(breakdownValue)) {
return formatMessage(analyticsMessages.noDependent)
}
return breakdownValue
}
if (selectedBreakdown === 'user_id') {
return userNamesById?.get(breakdownValue) ?? breakdownValue
}
if (selectedBreakdown === 'version_id') {
return getVersionDisplayName?.(breakdownValue) ?? breakdownValue
}
@@ -174,19 +191,34 @@ export function formatBreakdownLabels(
breakdownValues: readonly string[],
selectedBreakdowns: readonly AnalyticsBreakdownPreset[],
getVersionDisplayName: ((versionId: string) => string) | undefined,
userNamesById: ReadonlyMap<string, string> | undefined,
formatMessage: FormatMessage,
): string {
const normalizedBreakdowns = selectedBreakdowns.filter((breakdown) => breakdown !== 'none')
const downloadReasonBreakdownIndex = normalizedBreakdowns.indexOf('download_reason')
return collapseRepeatedUnknownBreakdownLabels(
selectedBreakdowns
.filter((breakdown) => breakdown !== 'none')
.map((breakdown, index) =>
formatBreakdownLabel(
breakdownValues[index] ?? '',
breakdown,
getVersionDisplayName,
normalizedBreakdowns.map((breakdown, index) => {
const breakdownValue = breakdownValues[index] ?? ''
if (
breakdown === 'dependent_project_download' &&
downloadReasonBreakdownIndex !== -1 &&
isUnknownAnalyticsBreakdownValue(breakdownValue)
) {
return formatAnalyticsDependentProjectFallbackLabel(
breakdownValues[downloadReasonBreakdownIndex],
formatMessage,
),
),
)
}
return formatBreakdownLabel(
breakdownValue,
breakdown,
getVersionDisplayName,
userNamesById,
formatMessage,
)
}),
formatMessage,
).join(COMBINED_BREAKDOWN_LABEL_SEPARATOR)
}
@@ -258,6 +290,33 @@ type PaletteRankEntry = {
total: number
}
function formatDatasetTooltip(projectName: string | undefined): string | undefined {
return projectName
}
function formatDependentProjectDatasetTooltip(
versionName: string | undefined,
dependentProjectName: string | undefined,
dependencyProjectNames: readonly string[],
formatMessage: FormatMessage,
): string | undefined {
if (dependencyProjectNames.length === 0) {
return undefined
}
if (versionName && dependentProjectName) {
return formatMessage(analyticsChartMessages.dependentProjectVersionTooltip, {
dependentProject: dependentProjectName,
dependencyProject: dependencyProjectNames.join(', '),
version: versionName,
})
}
return formatMessage(analyticsChartMessages.dependentOnProjectTooltip, {
project: dependencyProjectNames.join(', '),
})
}
function getPaletteColorForIndex(index: number, palette: string[]): string {
if (palette.length === 0) return ''
@@ -320,6 +379,10 @@ export function buildChartDatasets(
activeStat: AnalyticsDashboardStat,
palette: string[],
selectedBreakdowns: readonly AnalyticsBreakdownPreset[],
selectedFilters: AnalyticsSelectedFilters,
dependentProjectTypesById: ReadonlyMap<string, readonly string[]>,
projectNamesById: ReadonlyMap<string, string>,
userNamesById: ReadonlyMap<string, string>,
getVersionDisplayName: ((versionId: string) => string) | undefined,
getVersionProjectName: ((versionId: string) => string | undefined) | undefined,
formatMessage: FormatMessage,
@@ -332,17 +395,43 @@ export function buildChartDatasets(
const dataLength = Math.max(sliceCount, timeSlices.length)
const normalizedBreakdowns = selectedBreakdowns.filter((breakdown) => breakdown !== 'none')
const projectNamesById = new Map(selectedProjects.map((project) => [project.id, project.name]))
const normalizedFilters = normalizeAnalyticsSelectedFilters(selectedFilters)
function formatChartBreakdownLabels(breakdownValues: readonly string[]): string {
const downloadReasonBreakdownIndex = normalizedBreakdowns.indexOf('download_reason')
return collapseRepeatedUnknownBreakdownLabels(
normalizedBreakdowns.map((breakdown, index) => {
const breakdownValue = breakdownValues[index] ?? ''
if (breakdown === 'project') {
if (breakdown === 'project' || breakdown === 'dependent_project_download') {
if (
breakdown === 'dependent_project_download' &&
isNoDependentAnalyticsBreakdownValue(breakdownValue)
) {
return formatMessage(analyticsMessages.noDependent)
}
if (
breakdown === 'dependent_project_download' &&
isUnknownAnalyticsBreakdownValue(breakdownValue)
) {
return downloadReasonBreakdownIndex === -1
? formatMessage(analyticsMessages.unknown)
: formatAnalyticsDependentProjectFallbackLabel(
breakdownValues[downloadReasonBreakdownIndex],
formatMessage,
)
}
return projectNamesById.get(breakdownValue) ?? breakdownValue
}
return formatBreakdownLabel(breakdownValue, breakdown, getVersionDisplayName, formatMessage)
return formatBreakdownLabel(
breakdownValue,
breakdown,
getVersionDisplayName,
userNamesById,
formatMessage,
)
}),
formatMessage,
).join(COMBINED_BREAKDOWN_LABEL_SEPARATOR)
@@ -352,13 +441,27 @@ export function buildChartDatasets(
normalizedBreakdowns.length > 0 &&
!(normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'project')
) {
const hasVersionBreakdown = normalizedBreakdowns.includes('version_id')
const hasDependentProjectBreakdown = normalizedBreakdowns.includes('dependent_project_download')
const shouldShowDependentProjectTooltip =
hasDependentProjectBreakdown && (selectedProjects.length > 1 || hasVersionBreakdown)
const dataByBreakdown = new Map<string, number[]>()
const breakdownValuesByKey = new Map<string, string[]>()
const downloadTotalsByBreakdown = new Map<string, number>()
const dependentOnProjectIdsByBreakdown = new Map<string, Set<string>>()
timeSlices.forEach((slice, sliceIndex) => {
for (const point of slice) {
if (!isProjectAnalyticsPointInSelectedProjects(point, selectedProjectIds)) continue
if (
!doesAnalyticsPointMatchNormalizedFilters(
point,
normalizedFilters,
dependentProjectTypesById,
)
) {
continue
}
const breakdownValues = getAnalyticsBreakdownValues(
point,
@@ -374,6 +477,11 @@ export function buildChartDatasets(
dataByBreakdown.set(breakdownKey, new Array(dataLength).fill(0))
breakdownValuesByKey.set(breakdownKey, breakdownValues)
}
if (shouldShowDependentProjectTooltip && point.metric_kind === 'downloads') {
const projectIds = dependentOnProjectIdsByBreakdown.get(breakdownKey) ?? new Set<string>()
projectIds.add(point.source_project)
dependentOnProjectIdsByBreakdown.set(breakdownKey, projectIds)
}
if (point.metric_kind === 'downloads') {
downloadTotalsByBreakdown.set(
@@ -402,6 +510,42 @@ export function buildChartDatasets(
return Array.from(dataByBreakdown.entries()).map(([breakdownKey, data]) => {
const breakdownValues = breakdownValuesByKey.get(breakdownKey) ?? []
const fallbackColor = colorsByBreakdown.get(breakdownKey) ?? ''
const versionBreakdownIndex = normalizedBreakdowns.indexOf('version_id')
const dependentProjectBreakdownIndex = normalizedBreakdowns.indexOf(
'dependent_project_download',
)
const versionName =
hasVersionBreakdown && versionBreakdownIndex !== -1
? getVersionDisplayName?.(breakdownValues[versionBreakdownIndex] ?? '')
: undefined
const dependentProjectId =
dependentProjectBreakdownIndex !== -1
? breakdownValues[dependentProjectBreakdownIndex]
: undefined
const dependentProjectName = dependentProjectId
? isMissingDependentProjectValue(dependentProjectId)
? undefined
: (projectNamesById.get(dependentProjectId) ?? dependentProjectId)
: undefined
const versionProjectName =
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined
const dependencyProjectNames = [...(dependentOnProjectIdsByBreakdown.get(breakdownKey) ?? [])]
.map((projectId) => projectNamesById.get(projectId) ?? projectId)
.sort((left, right) => left.localeCompare(right))
const dependentProjectTooltip = dependentProjectId
? isNoDependentAnalyticsBreakdownValue(dependentProjectId)
? formatMessage(analyticsMessages.noDependentTooltip)
: isUnknownAnalyticsBreakdownValue(dependentProjectId)
? formatMessage(analyticsMessages.unknown)
: formatDependentProjectDatasetTooltip(
versionName,
dependentProjectName,
dependencyProjectNames,
formatMessage,
)
: undefined
const color =
normalizedBreakdowns.length === 1
? getBreakdownColor(
@@ -414,10 +558,8 @@ export function buildChartDatasets(
return {
projectId: getAnalyticsBreakdownDatasetId(breakdownValues, normalizedBreakdowns),
label: formatChartBreakdownLabels(breakdownValues),
projectName:
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined,
projectName: versionProjectName,
tooltip: dependentProjectTooltip ?? formatDatasetTooltip(versionProjectName),
data,
borderColor: color,
backgroundColor: color,
@@ -432,6 +574,15 @@ export function buildChartDatasets(
timeSlices.forEach((slice, sliceIndex) => {
for (const point of slice) {
if (!isProjectAnalyticsPointInSelectedProjects(point, selectedProjectIds)) continue
if (
!doesAnalyticsPointMatchNormalizedFilters(
point,
normalizedFilters,
dependentProjectTypesById,
)
) {
continue
}
if (point.metric_kind === 'downloads') {
downloadTotal += getMetricValue(point, 'downloads')
@@ -481,6 +632,15 @@ export function buildChartDatasets(
timeSlices.forEach((slice, sliceIndex) => {
for (const point of slice) {
if (!isProjectAnalyticsPointInSelectedProjects(point, selectedProjectIds)) continue
if (
!doesAnalyticsPointMatchNormalizedFilters(
point,
normalizedFilters,
dependentProjectTypesById,
)
) {
continue
}
const breakdownValues = getAnalyticsBreakdownValues(
point,
@@ -526,6 +686,10 @@ export function buildChartDatasets(
return Array.from(dataByProjectBreakdown.entries()).map(([breakdownKey, data]) => {
const breakdownValues = breakdownValuesByKey.get(breakdownKey) ?? []
const fallbackColor = colorsByBreakdown.get(breakdownKey) ?? ''
const versionProjectName =
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined
const color =
normalizedBreakdowns.length === 1
? getBreakdownColor(
@@ -538,10 +702,8 @@ export function buildChartDatasets(
return {
projectId: getAnalyticsBreakdownDatasetId(breakdownValues, normalizedBreakdowns),
label: formatChartBreakdownLabels(breakdownValues),
projectName:
normalizedBreakdowns.length === 1 && normalizedBreakdowns[0] === 'version_id'
? getVersionProjectName?.(breakdownValues[0] ?? '')
: undefined,
projectName: versionProjectName,
tooltip: formatDatasetTooltip(versionProjectName),
data,
borderColor: color,
backgroundColor: color,
@@ -549,6 +711,10 @@ export function buildChartDatasets(
})
}
function isMissingDependentProjectValue(value: string | undefined): boolean {
return isUnknownAnalyticsBreakdownValue(value) || isNoDependentAnalyticsBreakdownValue(value)
}
export function getSliceCount(
timeRange: Labrinth.Analytics.v3.TimeRange,
fallback: number,
@@ -1,12 +1,15 @@
import type { Labrinth } from '@modrinth/api-client'
import { useVIntl } from '@modrinth/ui'
import { computed, type ComputedRef, ref, watch } from 'vue'
import { useTheme } from '~/composables/nuxt-accessors'
import { isDarkTheme } from '~/plugins/theme/index.ts'
import type {
AnalyticsBreakdownPreset,
AnalyticsDashboardContextValue,
AnalyticsDashboardProject,
AnalyticsDashboardStat,
AnalyticsSelectedFilters,
} from '~/providers/analytics/analytics'
import {
@@ -47,12 +50,16 @@ export function useAnalyticsChartDatasets(
| 'displayedPreviousTimeSlices'
| 'displayedSelectedGroupBy'
| 'displayedSelectedBreakdowns'
| 'displayedSelectedFilters'
| 'hiddenGraphDatasetIds'
| 'hasExplicitGraphDatasetSelection'
| 'isGraphDatasetSelectionActive'
| 'selectedGraphDatasetIds'
| 'defaultGraphDatasetIds'
| 'topGraphDatasetIds'
| 'projectNamesById'
| 'userNamesById'
| 'dependentProjectTypesById'
| 'getVersionDisplayName'
| 'getVersionProjectName'
>,
@@ -160,6 +167,10 @@ export function useAnalyticsChartDatasets(
selectedProjects.value,
legendPalette.value,
context.displayedSelectedBreakdowns.value,
context.displayedSelectedFilters.value,
context.dependentProjectTypesById.value,
context.projectNamesById.value,
context.userNamesById.value,
context.getVersionDisplayName,
showProjectVersionNames.value ? context.getVersionProjectName : undefined,
formatMessage,
@@ -172,6 +183,10 @@ export function useAnalyticsChartDatasets(
selectedProjects.value,
legendPalette.value,
context.displayedSelectedBreakdowns.value,
context.displayedSelectedFilters.value,
context.dependentProjectTypesById.value,
context.projectNamesById.value,
context.userNamesById.value,
context.getVersionDisplayName,
showProjectVersionNames.value ? context.getVersionProjectName : undefined,
formatMessage,
@@ -357,12 +372,16 @@ export function useAnalyticsChartDatasets(
}
function buildDatasetsByStat(
timeSlices: Parameters<typeof buildChartDatasets>[0],
timeSlices: Labrinth.Analytics.v3.TimeSlice[],
selectedProjects: AnalyticsDashboardProject[],
palette: string[],
selectedBreakdowns: Parameters<typeof buildChartDatasets>[4],
getVersionDisplayName: Parameters<typeof buildChartDatasets>[5],
getVersionProjectName: Parameters<typeof buildChartDatasets>[6],
selectedBreakdowns: readonly AnalyticsBreakdownPreset[],
selectedFilters: AnalyticsSelectedFilters,
dependentProjectTypesById: ReadonlyMap<string, readonly string[]>,
projectNamesById: ReadonlyMap<string, string>,
userNamesById: ReadonlyMap<string, string>,
getVersionDisplayName: (versionId: string) => string,
getVersionProjectName: ((versionId: string) => string | undefined) | undefined,
formatMessage: FormatMessage,
sliceCount: number,
) {
@@ -374,6 +393,10 @@ function buildDatasetsByStat(
stat,
palette,
selectedBreakdowns,
selectedFilters,
dependentProjectTypesById,
projectNamesById,
userNamesById,
getVersionDisplayName,
getVersionProjectName,
formatMessage,
@@ -13,6 +13,7 @@ export type AnalyticsBreakdownItemType =
| 'downloadSource'
| 'gameVersion'
| 'loader'
| 'member'
| 'monetization'
| 'project'
| 'projectVersion'
@@ -39,6 +40,14 @@ export const analyticsMessages = defineMessages({
id: 'analytics.project.all',
defaultMessage: 'All projects',
},
yourProjects: {
id: 'analytics.project.your',
defaultMessage: 'Your projects',
},
userProjects: {
id: 'analytics.project.user',
defaultMessage: "{username}'s projects",
},
selectProjects: {
id: 'analytics.project.select',
defaultMessage: 'Select projects',
@@ -75,6 +84,14 @@ export const analyticsMessages = defineMessages({
id: 'analytics.value.unknown',
defaultMessage: 'Unknown',
},
noDependent: {
id: 'analytics.value.no-dependent',
defaultMessage: 'None',
},
noDependentTooltip: {
id: 'analytics.value.no-dependent-tooltip',
defaultMessage: 'Downloaded for reasons other than being a dependency',
},
other: {
id: 'analytics.value.other',
defaultMessage: 'Other',
@@ -167,6 +184,14 @@ export const analyticsMessages = defineMessages({
id: 'analytics.filter.search.project-versions',
defaultMessage: 'Search project versions...',
},
searchDependentProjectsPlaceholder: {
id: 'analytics.filter.search.dependent-projects',
defaultMessage: 'Search projects...',
},
searchMembersPlaceholder: {
id: 'analytics.filter.search.members',
defaultMessage: 'Search members...',
},
searchVersionsPlaceholder: {
id: 'analytics.filter.search.versions',
defaultMessage: 'Search versions...',
@@ -343,6 +368,22 @@ export const analyticsBreakdownMessages = defineMessages({
id: 'analytics.breakdown.download-reason',
defaultMessage: 'Download reason',
},
members: {
id: 'analytics.breakdown.members',
defaultMessage: 'Member',
},
dependentProjectDownload: {
id: 'analytics.breakdown.dependent-project-download',
defaultMessage: 'Dependent project',
},
dependentProjectType: {
id: 'analytics.breakdown.dependent-project-type',
defaultMessage: 'Dependent project type',
},
dependentOn: {
id: 'analytics.breakdown.dependent-on',
defaultMessage: 'Dependent on',
},
versionId: {
id: 'analytics.breakdown.project-version',
defaultMessage: 'Project version',
@@ -509,25 +550,33 @@ export const analyticsChartMessages = defineMessages({
id: 'analytics.chart.action.show-top-eight',
defaultMessage: 'Show top 8',
},
dependentOnProjectTooltip: {
id: 'analytics.chart.tooltip.dependent-on-project',
defaultMessage: 'Dependent on {project}',
},
dependentProjectVersionTooltip: {
id: 'analytics.chart.tooltip.dependent-project-version',
defaultMessage: '{dependentProject} dependent on {dependencyProject}, {version}',
},
tableSelectionLimited: {
id: 'analytics.chart.table-selection.limited',
defaultMessage:
'Showing {limit} {itemType, select, project {{limit, plural, one {project} other {projects}}} country {{limit, plural, one {country} other {countries}}} monetization {{limit, plural, one {monetization value} other {monetization values}}} downloadSource {{limit, plural, one {download source} other {download sources}}} downloadReason {{limit, plural, one {download reason} other {download reasons}}} projectVersion {{limit, plural, one {project version} other {project versions}}} loader {{limit, plural, one {loader} other {loaders}}} gameVersion {{limit, plural, one {game version} other {game versions}}} other {{limit, plural, one {item} other {items}}}} from table',
'Showing {limit} {itemType, select, project {{limit, plural, one {project} other {projects}}} country {{limit, plural, one {country} other {countries}}} monetization {{limit, plural, one {monetization value} other {monetization values}}} downloadSource {{limit, plural, one {download source} other {download sources}}} downloadReason {{limit, plural, one {download reason} other {download reasons}}} member {{limit, plural, one {member} other {members}}} projectVersion {{limit, plural, one {project version} other {project versions}}} loader {{limit, plural, one {loader} other {loaders}}} gameVersion {{limit, plural, one {game version} other {game versions}}} other {{limit, plural, one {item} other {items}}}} from table',
},
tableSelectionAll: {
id: 'analytics.chart.table-selection.all',
defaultMessage:
'Showing all {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
'Showing all {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} member {{count, plural, one {member} other {members}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
},
tableSelectionTop: {
id: 'analytics.chart.table-selection.top',
defaultMessage:
'Showing top {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
'Showing top {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} member {{count, plural, one {member} other {members}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
},
tableSelectionCount: {
id: 'analytics.chart.table-selection.count',
defaultMessage:
'Showing {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
'Showing {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} member {{count, plural, one {member} other {members}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table',
},
lineView: {
id: 'analytics.chart.view.line',
@@ -773,6 +822,10 @@ export function formatAnalyticsBreakdownLabel(
return formatMessage(analyticsBreakdownMessages.userAgent)
case 'download_reason':
return formatMessage(analyticsBreakdownMessages.downloadReason)
case 'user_id':
return formatMessage(analyticsBreakdownMessages.members)
case 'dependent_project_download':
return formatMessage(analyticsBreakdownMessages.dependentProjectDownload)
case 'version_id':
return formatMessage(analyticsBreakdownMessages.versionId)
case 'loader':
@@ -802,6 +855,10 @@ export function getAnalyticsBreakdownItemType(
return 'downloadSource'
case 'download_reason':
return 'downloadReason'
case 'user_id':
return 'member'
case 'dependent_project_download':
return 'project'
case 'version_id':
return 'projectVersion'
case 'loader':
@@ -845,6 +902,18 @@ export function formatAnalyticsDownloadReasonLabel(
}
}
export function formatAnalyticsDependentProjectFallbackLabel(
downloadReason: string | undefined,
formatMessage: FormatMessage,
): string {
const normalizedReason = downloadReason?.trim().toLowerCase()
if (normalizedReason === 'standalone' || normalizedReason === 'update') {
return formatMessage(analyticsMessages.noDependent)
}
return formatMessage(analyticsMessages.unknown)
}
export function formatAnalyticsDownloadSourceLabel(
source: string,
formatMessage: FormatMessage,
@@ -19,6 +19,8 @@ import type {
MutableRouteQuery,
} from '~/providers/analytics/analytics-types'
import { getAnalyticsBreakdownsWithSharedStats } from './query-builder/query-filter-utils'
export const DEFAULT_TIMEFRAME_PRESET: AnalyticsTimeframePreset = 'last_30_days'
export const DEFAULT_TIMEFRAME_MODE: AnalyticsTimeframeMode = 'preset'
export const DEFAULT_LAST_TIMEFRAME_AMOUNT = 1
@@ -73,9 +75,11 @@ const BREAKDOWN_PRESET_VALUES: AnalyticsBreakdownPreset[] = [
'monetization',
'user_agent',
'download_reason',
'user_id',
'version_id',
'loader',
'game_version',
'dependent_project_download',
]
const ANALYTICS_DASHBOARD_STAT_VALUES: AnalyticsDashboardStat[] = [
@@ -89,15 +93,18 @@ const ANALYTICS_GRAPH_VIEW_MODE_VALUES: AnalyticsGraphViewMode[] = ['line', 'are
const ANALYTICS_TABLE_SORT_COLUMN_VALUES: AnalyticsTableSortColumn[] = [
'date',
'project',
'dependent_on',
'breakdown',
'breakdown_project',
'breakdown_country',
'breakdown_monetization',
'breakdown_user_agent',
'breakdown_download_reason',
'breakdown_user_id',
'breakdown_version_id',
'breakdown_loader',
'breakdown_game_version',
'breakdown_dependent_project_download',
'views',
'downloads',
'revenue',
@@ -131,9 +138,12 @@ const QUERY_KEY_FILTER_MONETIZATION = 'a_monetization'
const QUERY_KEY_FILTER_USER_AGENT = 'a_user_agent'
const QUERY_KEY_FILTER_LEGACY_DOWNLOAD_SOURCE = 'a_download_source'
const QUERY_KEY_FILTER_DOWNLOAD_REASON = 'a_download_reason'
const QUERY_KEY_FILTER_USER_ID = 'a_user_id'
const QUERY_KEY_FILTER_VERSION_ID = 'a_version_id'
const QUERY_KEY_FILTER_GAME_VERSION = 'a_game_version'
const QUERY_KEY_FILTER_LOADER_TYPE = 'a_loader_type'
const QUERY_KEY_FILTER_DEPENDENT_PROJECT_ID = 'a_dependent_project_id'
const QUERY_KEY_FILTER_DEPENDENT_PROJECT_TYPE = 'a_dependent_project_type'
const QUERY_KEY_STAT = 'a_stat'
const QUERY_KEY_GRAPH_VIEW_MODE = 'a_chart'
const QUERY_KEY_GRAPH_RATIO_MODE = 'a_ratio'
@@ -146,6 +156,7 @@ const QUERY_KEY_TABLE_SORT = 'a_table_sort'
const QUERY_KEY_TABLE_SORT_DIRECTION = 'a_table_sort_direction'
const QUERY_KEY_LEGACY_GRAPH_TOP_BREAKDOWN_FILTER = 'a_top_breakdown'
const QUERY_KEY_LEGACY_GRAPH_LEGEND_EXPANSION = 'a_legend_expanded'
const PROJECT_SELECTION_ALL_QUERY_VALUE = 'all'
const URL_FILTER_CATEGORIES: Exclude<AnalyticsQueryFilterCategory, 'project'>[] = [
'project_status',
@@ -153,9 +164,12 @@ const URL_FILTER_CATEGORIES: Exclude<AnalyticsQueryFilterCategory, 'project'>[]
'monetization',
'user_agent',
'download_reason',
'user_id',
'version_id',
'game_version',
'loader_type',
'dependent_project_id',
'dependent_project_type',
]
const FILTER_QUERY_KEY_BY_CATEGORY: Record<
@@ -167,9 +181,12 @@ const FILTER_QUERY_KEY_BY_CATEGORY: Record<
monetization: QUERY_KEY_FILTER_MONETIZATION,
user_agent: QUERY_KEY_FILTER_USER_AGENT,
download_reason: QUERY_KEY_FILTER_DOWNLOAD_REASON,
user_id: QUERY_KEY_FILTER_USER_ID,
version_id: QUERY_KEY_FILTER_VERSION_ID,
game_version: QUERY_KEY_FILTER_GAME_VERSION,
loader_type: QUERY_KEY_FILTER_LOADER_TYPE,
dependent_project_id: QUERY_KEY_FILTER_DEPENDENT_PROJECT_ID,
dependent_project_type: QUERY_KEY_FILTER_DEPENDENT_PROJECT_TYPE,
}
const ANALYTICS_QUERY_KEYS = [
@@ -188,9 +205,12 @@ const ANALYTICS_QUERY_KEYS = [
QUERY_KEY_FILTER_USER_AGENT,
QUERY_KEY_FILTER_LEGACY_DOWNLOAD_SOURCE,
QUERY_KEY_FILTER_DOWNLOAD_REASON,
QUERY_KEY_FILTER_USER_ID,
QUERY_KEY_FILTER_VERSION_ID,
QUERY_KEY_FILTER_GAME_VERSION,
QUERY_KEY_FILTER_LOADER_TYPE,
QUERY_KEY_FILTER_DEPENDENT_PROJECT_ID,
QUERY_KEY_FILTER_DEPENDENT_PROJECT_TYPE,
QUERY_KEY_STAT,
QUERY_KEY_GRAPH_VIEW_MODE,
QUERY_KEY_GRAPH_RATIO_MODE,
@@ -211,9 +231,12 @@ export function buildEmptySelectedFilters(): AnalyticsSelectedFilters {
monetization: [],
user_agent: [],
download_reason: [],
user_id: [],
version_id: [],
game_version: [],
loader_type: [],
dependent_project_id: [],
dependent_project_type: [],
}
}
@@ -254,7 +277,7 @@ function normalizeFilterQueryValues(
.filter((value) => PROJECT_STATUS_FILTER_VALUES.includes(value))
}
if (category !== 'loader_type') {
if (category !== 'loader_type' && category !== 'dependent_project_type') {
return values
}
@@ -405,9 +428,10 @@ export function buildDefaultAnalyticsGraphState(
export function buildDefaultAnalyticsQueryBuilderState(
availableProjectIds: string[],
defaultProjectIds: string[] = availableProjectIds,
): AnalyticsQueryBuilderState {
return {
selectedProjectIds: [...availableProjectIds],
selectedProjectIds: [...defaultProjectIds],
selectedTimeframeMode: DEFAULT_TIMEFRAME_MODE,
selectedTimeframe: DEFAULT_TIMEFRAME_PRESET,
selectedLastTimeframeAmount: DEFAULT_LAST_TIMEFRAME_AMOUNT,
@@ -415,7 +439,7 @@ export function buildDefaultAnalyticsQueryBuilderState(
selectedCustomTimeframeStartDate: getDefaultCustomStartDate(),
selectedCustomTimeframeEndDate: getDefaultCustomEndDate(),
selectedGroupBy: DEFAULT_GROUP_BY_PRESET,
selectedBreakdowns: getDefaultAnalyticsBreakdownPresets(availableProjectIds),
selectedBreakdowns: getDefaultAnalyticsBreakdownPresets(defaultProjectIds),
selectedFilters: buildEmptySelectedFilters(),
}
}
@@ -454,7 +478,7 @@ export function getAnalyticsBreakdownPresetsForProjectSelection(
}
}
return normalizedBreakdowns
return getAnalyticsBreakdownsWithSharedStats(normalizedBreakdowns)
}
export function getAnalyticsBreakdownPresetForProjectSelection(
@@ -475,12 +499,16 @@ export function getAnalyticsBreakdownPresetForProjectSelection(
export function isAnalyticsQueryBuilderStateDefault(
state: AnalyticsQueryBuilderState,
availableProjectIds: string[],
defaultProjectIds: string[] = availableProjectIds,
): boolean {
const defaultState = buildDefaultAnalyticsQueryBuilderState(availableProjectIds)
const defaultState = buildDefaultAnalyticsQueryBuilderState(
availableProjectIds,
defaultProjectIds,
)
const areDefaultProjectsSelected =
availableProjectIds.length === 0
defaultProjectIds.length === 0
? state.selectedProjectIds.length === 0
: areAllProjectsSelected(state.selectedProjectIds, availableProjectIds)
: areAllProjectsSelected(state.selectedProjectIds, defaultProjectIds)
return (
areDefaultProjectsSelected &&
@@ -666,13 +694,19 @@ export function readAnalyticsTableSortState(
export function readAnalyticsQueryBuilderState(
query: LocationQuery,
availableProjectIds: string[],
defaultProjectIds: string[] = availableProjectIds,
): AnalyticsQueryBuilderState {
const defaultState = buildDefaultAnalyticsQueryBuilderState(availableProjectIds)
const defaultState = buildDefaultAnalyticsQueryBuilderState(
availableProjectIds,
defaultProjectIds,
)
const selectedProjectIdsFromQuery = parseListQueryValue(query[QUERY_KEY_PROJECT_IDS])
const selectedProjectIds =
selectedProjectIdsFromQuery.length > 0
? selectedProjectIdsFromQuery
: defaultState.selectedProjectIds
let selectedProjectIds = defaultState.selectedProjectIds
if (selectedProjectIdsFromQuery.includes(PROJECT_SELECTION_ALL_QUERY_VALUE)) {
selectedProjectIds = [...availableProjectIds]
} else if (selectedProjectIdsFromQuery.length > 0) {
selectedProjectIds = selectedProjectIdsFromQuery
}
const selectedFilters = buildEmptySelectedFilters()
for (const category of URL_FILTER_CATEGORIES) {
@@ -764,6 +798,12 @@ export function hasAnalyticsProjectSelectionQuery(query: LocationQuery): boolean
return parseListQueryValue(query[QUERY_KEY_PROJECT_IDS]).length > 0
}
export function hasAnalyticsAllProjectSelectionQuery(query: LocationQuery): boolean {
return parseListQueryValue(query[QUERY_KEY_PROJECT_IDS]).includes(
PROJECT_SELECTION_ALL_QUERY_VALUE,
)
}
export function hasAnalyticsGraphProjectEventsVisibilityQuery(query: LocationQuery): boolean {
return query[QUERY_KEY_GRAPH_PROJECT_EVENTS_VISIBILITY] !== undefined
}
@@ -779,14 +819,17 @@ export function buildAnalyticsQueryBuilderRouteQuery(
state: AnalyticsQueryBuilderState,
availableProjectIds: string[],
graphState?: AnalyticsGraphState,
defaultProjectIds: string[] = availableProjectIds,
): MutableRouteQuery {
const nextRouteQuery = {
...currentRouteQuery,
} as MutableRouteQuery
const projectIdsQueryValue = areAllProjectsSelected(state.selectedProjectIds, availableProjectIds)
const projectIdsQueryValue = areAllProjectsSelected(state.selectedProjectIds, defaultProjectIds)
? undefined
: serializeListQueryValue(state.selectedProjectIds)
: areAllProjectsSelected(state.selectedProjectIds, availableProjectIds)
? PROJECT_SELECTION_ALL_QUERY_VALUE
: serializeListQueryValue(state.selectedProjectIds)
const isCustomTimeframeMode =
state.selectedTimeframeMode === 'custom_range' ||
state.selectedTimeframeMode === 'custom_datetime_range'
@@ -0,0 +1,62 @@
<template>
<div class="mr-2.5 flex min-w-0 items-center gap-2">
<span
v-if="!hideIcon"
v-tooltip="iconTooltip"
class="flex size-6 shrink-0 items-center justify-center overflow-hidden rounded text-primary"
>
<img
v-if="iconUrl"
:src="iconUrl"
:alt="formatMessage(analyticsMessages.projectIconAlt, { name: label })"
class="h-6 w-6 rounded object-cover"
/>
<BoxIcon v-else class="h-full w-full" />
</span>
<component
:is="labelHref ? 'a' : 'span'"
v-tooltip="labelTooltip"
:href="labelHref"
:target="labelHref ? '_blank' : undefined"
:rel="labelHref ? 'noopener noreferrer' : undefined"
class="line-clamp-2 min-w-0 truncate text-wrap font-semibold leading-tight text-primary"
:class="{ 'hover:underline': labelHref }"
:title="label"
>
{{ label }}
</component>
<component
:is="organizationHref ? 'a' : 'span'"
v-if="organizationTooltip"
v-tooltip="organizationTooltip"
:href="organizationHref"
:target="organizationHref ? '_blank' : undefined"
:rel="organizationHref ? 'noopener noreferrer' : undefined"
:aria-label="organizationTooltip"
class="flex size-4 shrink-0 items-center text-primary"
:class="{ 'hover:underline': organizationHref }"
>
<OrganizationIcon class="size-4" />
</component>
</div>
</template>
<script setup lang="ts">
import { BoxIcon, OrganizationIcon } from '@modrinth/assets'
import { useVIntl } from '@modrinth/ui'
import { analyticsMessages } from '../analytics-messages.ts'
defineProps<{
label: string
iconUrl?: string
iconTooltip?: string
hideIcon?: boolean
labelHref?: string
labelTooltip?: string
organizationHref?: string
organizationTooltip?: string
}>()
const { formatMessage } = useVIntl()
</script>
@@ -23,7 +23,6 @@ type BuildAnalyticsTableColumnsOptions = {
selectedBreakdowns: readonly AnalyticsTableBreakdownPreset[]
selectedFilters: AnalyticsSelectedFilters
showBreakdownColumn: boolean
showProjectVersionProjectColumn: boolean
formatMessage: FormatMessage
getRelevantAnalyticsDashboardStats: (
breakdowns: readonly AnalyticsBreakdownPreset[],
@@ -43,7 +42,6 @@ export function buildAnalyticsTableColumns({
selectedBreakdowns,
selectedFilters,
showBreakdownColumn,
showProjectVersionProjectColumn,
formatMessage,
getRelevantAnalyticsDashboardStats,
}: BuildAnalyticsTableColumnsOptions): TableColumn<AnalyticsTableColumnKey>[] {
@@ -66,20 +64,11 @@ export function buildAnalyticsTableColumns({
key: getAnalyticsTableBreakdownColumnKey(breakdown),
label: getAnalyticsTableBreakdownColumnLabel(breakdown, formatMessage),
enableSorting: true,
width: breakdown === 'project' ? '25%' : undefined,
width: breakdown === 'project' && selectedBreakdowns.length === 1 ? '45%' : undefined,
})
}
}
if (showProjectVersionProjectColumn) {
nextColumns.push({
key: 'project',
label: formatAnalyticsBreakdownLabel('project', formatMessage),
enableSorting: true,
width: '25%',
})
}
for (const stat of stats) {
const column = getAnalyticsTableMetricColumn(stat, formatMessage)
if (column) {
@@ -102,6 +91,7 @@ export function getAnalyticsTableMetricColumn(
enableSorting: true,
defaultSortDirection: 'desc',
align: 'right',
width: '20%',
}
case 'downloads':
return {
@@ -110,6 +100,7 @@ export function getAnalyticsTableMetricColumn(
enableSorting: true,
defaultSortDirection: 'desc',
align: 'right',
width: '20%',
}
case 'revenue':
return {
@@ -118,6 +109,7 @@ export function getAnalyticsTableMetricColumn(
enableSorting: true,
defaultSortDirection: 'desc',
align: 'right',
width: '20%',
}
case 'playtime':
return {
@@ -126,6 +118,7 @@ export function getAnalyticsTableMetricColumn(
enableSorting: true,
defaultSortDirection: 'desc',
align: 'right',
width: '20%',
}
default:
return null
@@ -67,6 +67,8 @@ function getAnalyticsTableCsvCellValue(
return row.date
case 'project':
return row.project
case 'dependent_on':
return row.dependent_on
case 'breakdown':
return row.breakdownDisplay
case 'views':
@@ -1,8 +1,11 @@
import type { Labrinth } from '@modrinth/api-client'
import type {
AnalyticsBreakdownPreset,
AnalyticsDashboardStat,
import {
type AnalyticsBreakdownPreset,
type AnalyticsDashboardStat,
type AnalyticsSelectedFilters,
doesAnalyticsPointMatchNormalizedFilters,
normalizeAnalyticsSelectedFilters,
} from '~/providers/analytics/analytics'
import {
@@ -12,13 +15,18 @@ import {
getSliceCount,
} from '../analytics-chart/analytics-chart-utils'
import type { FormatMessage } from '../analytics-messages'
import { analyticsMessages } from '../analytics-messages'
import {
analyticsMessages,
formatAnalyticsDependentProjectFallbackLabel,
} from '../analytics-messages'
import {
ALL_BREAKDOWN_VALUE,
COMBINED_BREAKDOWN_LABEL_SEPARATOR,
getAnalyticsBreakdownDatasetId,
getAnalyticsBreakdownKey,
getAnalyticsBreakdownValues,
isNoDependentAnalyticsBreakdownValue,
isUnknownAnalyticsBreakdownValue,
} from '../breakdown'
import { getAnalyticsTableBreakdownColumnKey } from './analytics-table-columns'
import type {
@@ -36,8 +44,12 @@ type BuildAnalyticsTableRowsOptions = {
timeSlices: Labrinth.Analytics.v3.TimeSlice[]
selectedBreakdowns: readonly AnalyticsTableBreakdownPreset[]
selectedProjectIds: ReadonlySet<string>
selectedFilters: AnalyticsSelectedFilters
dependentProjectTypesById: ReadonlyMap<string, readonly string[]>
includeDependentProjectTooltipContext: boolean
relevantStats: ReadonlySet<AnalyticsDashboardStat>
projectNamesById: ReadonlyMap<string, string>
userNamesById: ReadonlyMap<string, string>
getVersionDisplayName: (versionId: string) => string
getVersionProjectName: (versionId: string) => string | undefined
showTimeInBucketLabel: boolean
@@ -51,8 +63,12 @@ export function buildAnalyticsTableRows({
timeSlices,
selectedBreakdowns,
selectedProjectIds,
selectedFilters,
dependentProjectTypesById,
includeDependentProjectTooltipContext,
relevantStats,
projectNamesById,
userNamesById,
getVersionDisplayName,
getVersionProjectName,
showTimeInBucketLabel,
@@ -72,6 +88,7 @@ export function buildAnalyticsTableRows({
const projectDisplayValues = new Map<string, string>()
const nextRows = new Map<string, AnalyticsTableRow>()
const bucketLabelsBySliceIndex = new Map<number, { date: string; dateMs: number }>()
const normalizedFilters = normalizeAnalyticsSelectedFilters(selectedFilters)
function getBreakdownDisplayValue(
breakdownValue: string,
@@ -84,6 +101,7 @@ export function buildAnalyticsTableRows({
breakdownValue,
breakdown,
projectNamesById,
userNamesById,
getVersionDisplayName,
formatMessage,
)
@@ -111,6 +129,15 @@ export function buildAnalyticsTableRows({
return displayValue
}
function getProjectVersionIdForBreakdownValues(breakdownValues: readonly string[]) {
const versionBreakdownIndex = selectedBreakdowns.indexOf('version_id')
if (versionBreakdownIndex === -1) {
return ''
}
return breakdownValues[versionBreakdownIndex] ?? ''
}
function getBreakdownDisplays(breakdownValues: readonly string[]) {
const displays: AnalyticsTableBreakdownDisplayValues = {}
@@ -118,6 +145,19 @@ export function buildAnalyticsTableRows({
displays[breakdown] = getBreakdownDisplayValue(breakdownValues[index] ?? '', breakdown)
})
const dependentProjectBreakdownIndex = selectedBreakdowns.indexOf('dependent_project_download')
const downloadReasonBreakdownIndex = selectedBreakdowns.indexOf('download_reason')
if (
dependentProjectBreakdownIndex !== -1 &&
downloadReasonBreakdownIndex !== -1 &&
isUnknownAnalyticsBreakdownValue(breakdownValues[dependentProjectBreakdownIndex])
) {
displays.dependent_project_download = formatAnalyticsDependentProjectFallbackLabel(
breakdownValues[downloadReasonBreakdownIndex],
formatMessage,
)
}
return displays
}
@@ -157,6 +197,7 @@ export function buildAnalyticsTableRows({
function createRow(
rowId: string,
breakdownValues: readonly string[],
dependentOnProjectId?: string,
bucketLabel?: { date: string; dateMs: number },
) {
const breakdownKey =
@@ -169,6 +210,12 @@ export function buildAnalyticsTableRows({
date: bucketLabel?.date ?? '',
dateMs: bucketLabel?.dateMs ?? 0,
project: getProjectDisplayValueForBreakdownValues(breakdownValues),
projectVersionId: getProjectVersionIdForBreakdownValues(breakdownValues),
dependent_on: dependentOnProjectId
? (projectNamesById.get(dependentOnProjectId) ?? dependentOnProjectId)
: '',
dependentOnProjectId: dependentOnProjectId ?? '',
dependentOnProjectIds: dependentOnProjectId ? [dependentOnProjectId] : [],
breakdown: breakdownKey,
breakdownValues: Object.fromEntries(
selectedBreakdowns.map((breakdown, index) => [breakdown, breakdownValues[index] ?? '']),
@@ -190,6 +237,18 @@ export function buildAnalyticsTableRows({
return row
}
function addDependentOnProjectIdToRow(row: AnalyticsTableRow, projectId: string | undefined) {
if (!projectId || row.dependentOnProjectIds.includes(projectId)) {
return
}
row.dependentOnProjectIds.push(projectId)
if (!row.dependentOnProjectId) {
row.dependentOnProjectId = projectId
row.dependent_on = projectNamesById.get(projectId) ?? projectId
}
}
if (!includeDate && selectedBreakdowns.length === 0) {
createRow(ALL_PROJECTS_BREAKDOWN_VALUE, [])
}
@@ -211,6 +270,15 @@ export function buildAnalyticsTableRows({
if (!selectedProjectIds.has(point.source_project)) {
continue
}
if (
!doesAnalyticsPointMatchNormalizedFilters(
point,
normalizedFilters,
dependentProjectTypesById,
)
) {
continue
}
const pointStat = getAnalyticsTableStatForMetric(point.metric_kind)
if (!pointStat || !relevantStats.has(pointStat)) {
@@ -226,12 +294,21 @@ export function buildAnalyticsTableRows({
}
const nextBucketLabel = includeDate ? (bucketLabel ?? getBucketLabel(sliceIndex)) : undefined
const dependentOnProjectId = includeDependentProjectTooltipContext
? point.source_project
: undefined
const dependentTooltipProjectId = selectedBreakdowns.includes('dependent_project_download')
? point.source_project
: undefined
const breakdownKey =
breakdownValues.length === 0
? ALL_PROJECTS_BREAKDOWN_VALUE
: getAnalyticsBreakdownKey(breakdownValues)
const rowId = includeDate ? `${nextBucketLabel?.dateMs ?? 0}::${breakdownKey}` : breakdownKey
const row = nextRows.get(rowId) ?? createRow(rowId, breakdownValues, nextBucketLabel)
const row =
nextRows.get(rowId) ??
createRow(rowId, breakdownValues, dependentOnProjectId, nextBucketLabel)
addDependentOnProjectIdToRow(row, dependentTooltipProjectId)
addAnalyticsMetricToTableRow(row, point)
}
})
@@ -292,11 +369,21 @@ function formatAnalyticsTableBreakdownDisplayValue(
value: string,
breakdown: AnalyticsTableBreakdownPreset,
projectNamesById: ReadonlyMap<string, string>,
userNamesById: ReadonlyMap<string, string>,
getVersionDisplayName: (versionId: string) => string,
formatMessage: FormatMessage,
): string {
if (breakdown === 'project') {
if (breakdown === 'project' || breakdown === 'dependent_project_download') {
if (breakdown === 'dependent_project_download') {
if (isNoDependentAnalyticsBreakdownValue(value)) {
return formatMessage(analyticsMessages.noDependent)
}
if (isUnknownAnalyticsBreakdownValue(value)) {
return formatMessage(analyticsMessages.unknown)
}
}
return projectNamesById.get(value) ?? value
}
return formatBreakdownLabel(value, breakdown, getVersionDisplayName, formatMessage)
return formatBreakdownLabel(value, breakdown, getVersionDisplayName, userNamesById, formatMessage)
}
@@ -3,7 +3,7 @@ import type { TableColumn } from '@modrinth/ui'
import { isAnalyticsTableBreakdownColumnKey } from './analytics-table-columns'
import type { AnalyticsTableColumnKey, AnalyticsTableRow } from './analytics-table-types'
const SEARCHABLE_COLUMN_KEYS = new Set<AnalyticsTableColumnKey>(['date', 'project'])
const SEARCHABLE_COLUMN_KEYS = new Set<AnalyticsTableColumnKey>(['date', 'project', 'dependent_on'])
export function getAnalyticsTableSearchableColumns(
columns: TableColumn<AnalyticsTableColumnKey>[],
@@ -39,6 +39,8 @@ function getAnalyticsTableSearchableCellValue(
return row.date
case 'project':
return row.project
case 'dependent_on':
return row.dependent_on
case 'breakdown':
return row.breakdownDisplay
default:
@@ -129,6 +129,15 @@ function getAnalyticsTableRowComparator(
directionFactor,
sortCollator,
)
case 'dependent_on':
return (left, right) =>
compareAnalyticsTableRows(
left,
right,
sortCollator.compare(left.dependent_on, right.dependent_on),
directionFactor,
sortCollator,
)
case 'breakdown':
return (left, right) =>
compareAnalyticsTableRows(
@@ -18,11 +18,15 @@ export type AnalyticsTableSortState = {
export type AnalyticsTableSortDirectionValue = AnalyticsTableSortDirection
export type AnalyticsTableRow = {
[key: string]: string | number | AnalyticsTableBreakdownDisplayValues
[key: string]: string | number | string[] | AnalyticsTableBreakdownDisplayValues
id: string
date: string
dateMs: number
project: string
projectVersionId: string
dependent_on: string
dependentOnProjectId: string
dependentOnProjectIds: string[]
breakdown: string
breakdownValues: AnalyticsTableBreakdownDisplayValues
breakdownDisplays: AnalyticsTableBreakdownDisplayValues
@@ -55,32 +55,92 @@
<template #cell-date="{ value }">
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_project="{ value }">
<span class="text-primary">{{ value }}</span>
<template #cell-breakdown_project="{ row, value }">
<ProjectCell
:label="getProjectCellLabel(value)"
:icon-url="getProjectIconUrl(row.breakdownValues.project)"
:icon-tooltip="getProjectCellLabel(value)"
:label-href="getProjectPageHref(row.breakdownValues.project)"
:organization-href="getProjectOrganizationPageHref(row.breakdownValues.project)"
:organization-tooltip="getProjectOrganizationName(row.breakdownValues.project)"
/>
</template>
<template #cell-breakdown_country="{ value }">
<span class="text-primary">{{ value }}</span>
<span class="mr-2.5 text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_monetization="{ value }">
<span class="text-primary">{{ value }}</span>
<span class="mr-2.5 text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_user_agent="{ value }">
<span class="text-primary">{{ value }}</span>
<span class="mr-2.5 text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_download_reason="{ value }">
<span class="text-primary">{{ value }}</span>
<span class="mr-2.5 text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_version_id="{ value }">
<span class="text-primary">{{ value }}</span>
<template #cell-breakdown_user_id="{ row, value }">
<component
:is="getUserPageHref(row.breakdownValues.user_id) ? 'a' : 'span'"
:href="getUserPageHref(row.breakdownValues.user_id)"
:target="getUserPageHref(row.breakdownValues.user_id) ? '_blank' : undefined"
:rel="getUserPageHref(row.breakdownValues.user_id) ? 'noopener noreferrer' : undefined"
class="mr-2.5 flex min-w-0 items-center gap-2 text-primary"
:class="{ 'hover:underline': getUserPageHref(row.breakdownValues.user_id) }"
>
<span
v-tooltip="getUserCellLabel(value)"
class="flex size-6 shrink-0 items-center justify-center overflow-hidden rounded-full text-primary"
>
<img
v-if="getUserAvatarUrl(row.breakdownValues.user_id)"
:src="getUserAvatarUrl(row.breakdownValues.user_id)"
:alt="getUserCellLabel(value)"
class="h-6 w-6 rounded-full object-cover"
/>
<UserIcon v-else class="h-full w-full" />
</span>
<span class="min-w-0 truncate font-semibold leading-tight text-primary">
{{ value }}
</span>
</component>
</template>
<template #cell-breakdown_dependent_project_download="{ row, value }">
<ProjectCell
:label="getProjectCellLabel(value)"
:icon-url="
isMissingDependentProjectValue(row.breakdownValues.dependent_project_download)
? undefined
: getProjectIconUrl(row.breakdownValues.dependent_project_download)
"
:icon-tooltip="getProjectCellLabel(value)"
:hide-icon="
isMissingDependentProjectValue(row.breakdownValues.dependent_project_download)
"
:label-href="
isMissingDependentProjectValue(row.breakdownValues.dependent_project_download)
? undefined
: getProjectPageHref(row.breakdownValues.dependent_project_download)
"
:label-tooltip="getDependentProjectTooltip(row)"
/>
</template>
<template #cell-breakdown_version_id="{ row, value }">
<component
:is="getVersionPageHref(row.projectVersionId) ? 'a' : 'span'"
v-tooltip="getVersionProjectName(row.projectVersionId)"
:href="getVersionPageHref(row.projectVersionId)"
:target="getVersionPageHref(row.projectVersionId) ? '_blank' : undefined"
:rel="getVersionPageHref(row.projectVersionId) ? 'noopener noreferrer' : undefined"
class="mr-2.5 text-primary"
:class="{ 'hover:underline': getVersionPageHref(row.projectVersionId) }"
>
{{ value }}
</component>
</template>
<template #cell-breakdown_loader="{ value }">
<span class="text-primary">{{ value }}</span>
<span class="mr-2.5 text-primary">{{ value }}</span>
</template>
<template #cell-breakdown_game_version="{ value }">
<span class="text-primary">{{ value }}</span>
</template>
<template #cell-project="{ value }">
<span class="text-primary">{{ value }}</span>
<span class="mr-2.5 text-primary">{{ value }}</span>
</template>
<template #cell-views="{ row }">
<span>{{ formatInteger(row.views) }}</span>
@@ -130,7 +190,7 @@
</template>
<script setup lang="ts">
import { DownloadIcon, DropdownIcon, SearchIcon } from '@modrinth/assets'
import { DownloadIcon, DropdownIcon, SearchIcon, UserIcon } from '@modrinth/assets'
import {
ButtonStyled,
OverflowMenu,
@@ -159,10 +219,15 @@ import {
} from '../analytics-chart/analytics-chart-utils.ts'
import {
analyticsBreakdownMessages,
analyticsChartMessages,
analyticsMessages,
analyticsTableMessages,
} from '../analytics-messages.ts'
import AnalyticsLoadingBar from '../AnalyticsLoadingBar.vue'
import {
isNoDependentAnalyticsBreakdownValue,
isUnknownAnalyticsBreakdownValue,
} from '../breakdown.ts'
import {
buildAnalyticsTableColumns,
getAnalyticsTableBreakdownColumnLabel,
@@ -194,8 +259,10 @@ import { sortAnalyticsTableRows } from './analytics-table-sorting.ts'
import type {
AnalyticsTableColumnKey,
AnalyticsTableMode,
AnalyticsTableRow,
AnalyticsTableSortDirectionValue,
} from './analytics-table-types.ts'
import ProjectCell from './ProjectCell.vue'
import { useAnalyticsTableGraphSelection } from './use-analytics-table-graph-selection.ts'
import { useAnalyticsTablePagination } from './use-analytics-table-pagination.ts'
import { useAnalyticsTableRowCache } from './use-analytics-table-row-cache.ts'
@@ -221,7 +288,15 @@ const {
getRelevantAnalyticsDashboardStats,
isLoading,
versionNumbersById,
versionProjectIdsById,
versionProjectNamesById,
projectNamesById,
projectIconUrlsById,
projectOrganizationIdsById,
projectOrganizationNamesById,
userNamesById,
userAvatarUrlsById,
dependentProjectTypesById,
getVersionDisplayName,
getVersionProjectName,
} = injectAnalyticsDashboardContext()
@@ -263,11 +338,10 @@ const showGraphDatasetSelection = computed(() =>
? selectedProjectIdSet.value.size > 1
: selectedBreakdowns.value.length > 0,
)
const showProjectVersionProjectColumn = computed(
const includeDependentProjectTooltipContext = computed(
() =>
selectedBreakdownSet.value.has('version_id') &&
!selectedBreakdownSet.value.has('project') &&
selectedProjectIdSet.value.size > 1,
selectedBreakdownSet.value.has('dependent_project_download') &&
!selectedBreakdownSet.value.has('project'),
)
const includeDateColumn = computed(
() =>
@@ -314,9 +388,6 @@ const csvExportOptions = computed<OverflowMenuOption[]>(() => {
},
]
})
const projectNamesById = computed(
() => new Map(projects.value.map((project) => [project.id, project.name])),
)
const hasAvailableProjects = computed(() => projects.value.length > 0)
const analyticsPointCount = computed(() =>
timeSlices.value.reduce((sum, slice) => sum + slice.length, 0),
@@ -360,8 +431,12 @@ function buildTableRows(mode: AnalyticsTableMode) {
timeSlices: timeSlices.value,
selectedBreakdowns: selectedBreakdowns.value,
selectedProjectIds: selectedProjectIdSet.value,
selectedFilters: selectedFilters.value,
dependentProjectTypesById: dependentProjectTypesById.value,
includeDependentProjectTooltipContext: includeDependentProjectTooltipContext.value,
relevantStats: relevantStats.value,
projectNamesById: projectNamesById.value,
userNamesById: userNamesById.value,
getVersionDisplayName,
getVersionProjectName,
showTimeInBucketLabel: showTimeInBucketLabel.value,
@@ -379,12 +454,86 @@ function buildColumns(includeDate: boolean) {
selectedBreakdowns: selectedBreakdowns.value,
selectedFilters: selectedFilters.value,
showBreakdownColumn: showBreakdownColumn.value,
showProjectVersionProjectColumn: showProjectVersionProjectColumn.value,
formatMessage,
getRelevantAnalyticsDashboardStats,
})
}
function getProjectIconUrl(projectId: string | undefined) {
return projectId ? projectIconUrlsById.value.get(projectId) : undefined
}
function getProjectOrganizationName(projectId: string | undefined) {
return projectId ? projectOrganizationNamesById.value.get(projectId) : undefined
}
function getProjectCellLabel(value: unknown) {
return typeof value === 'string' ? value : String(value ?? '')
}
function getUserAvatarUrl(userId: string | undefined) {
return userId ? userAvatarUrlsById.value.get(userId) : undefined
}
function getUserCellLabel(value: unknown) {
return typeof value === 'string' ? value : String(value ?? '')
}
function getUserPageHref(userId: string | undefined) {
if (!userId) return undefined
const username = userNamesById.value.get(userId) ?? userId
return `/user/${encodeURIComponent(username)}`
}
function getProjectPageHref(projectId: string | undefined) {
return projectId ? `/project/${encodeURIComponent(projectId)}` : undefined
}
function getProjectOrganizationPageHref(projectId: string | undefined) {
if (!projectId) return undefined
const organizationId = projectOrganizationIdsById.value.get(projectId)
if (!organizationId) return undefined
return `/organization/${encodeURIComponent(organizationId)}`
}
function getVersionPageHref(versionId: string | undefined) {
if (!versionId) return undefined
const projectId = versionProjectIdsById.value.get(versionId)
if (!projectId) return undefined
return `/project/${encodeURIComponent(projectId)}/version/${encodeURIComponent(versionId)}`
}
function isMissingDependentProjectValue(value: string | undefined) {
return isUnknownAnalyticsBreakdownValue(value) || isNoDependentAnalyticsBreakdownValue(value)
}
function getDependentProjectTooltip(row: AnalyticsTableRow) {
if (isNoDependentAnalyticsBreakdownValue(row.breakdownValues.dependent_project_download)) {
return formatMessage(analyticsMessages.noDependentTooltip)
}
if (isUnknownAnalyticsBreakdownValue(row.breakdownValues.dependent_project_download)) {
return formatMessage(analyticsMessages.unknown)
}
const dependencyProjectIds = new Set(row.dependentOnProjectIds)
if (row.dependentOnProjectId) {
dependencyProjectIds.add(row.dependentOnProjectId)
}
const dependencyProjectNames = [...dependencyProjectIds]
.map((projectId) => projectNamesById.value.get(projectId) ?? projectId)
.sort((left, right) => left.localeCompare(right))
return dependencyProjectNames.length > 0
? formatMessage(analyticsChartMessages.dependentOnProjectTooltip, {
project: dependencyProjectNames.join(', '),
})
: undefined
}
watch(
activeColumns,
(nextColumns) => {
@@ -445,6 +594,9 @@ watch(
selectedBreakdowns,
selectedFilters,
projects,
dependentProjectTypesById,
projectNamesById,
userNamesById,
versionNumbersById,
versionProjectNamesById,
],
@@ -7,6 +7,10 @@ import type {
AnalyticsSelectedBreakdowns,
} from '~/providers/analytics/analytics'
import {
isNoDependentAnalyticsBreakdownValue,
isUnknownAnalyticsBreakdownValue,
} from '../breakdown'
import { getAnalyticsTableMetricSortedGraphDatasetIds } from './analytics-table-sorting'
import type { AnalyticsTableColumnKey, AnalyticsTableRow } from './analytics-table-types'
@@ -58,6 +62,9 @@ export function useAnalyticsTableGraphSelection({
const filteredSelectableGraphDatasetIds = computed(() =>
getAnalyticsTableSelectableGraphDatasetIds(filteredRows.value),
)
const excludedGraphDatasetIds = computed(() =>
getAnalyticsTableExcludedGraphDatasetIds(sortedRows.value),
)
const sortedMetricGraphDatasetIds = computed(() =>
getAnalyticsTableMetricSortedGraphDatasetIds(sortedRows.value, sortColumn.value, sortCollator),
)
@@ -65,7 +72,9 @@ export function useAnalyticsTableGraphSelection({
const sortedMetricIds = sortedMetricGraphDatasetIds.value
const defaultIds =
sortedMetricIds.length > 0 ? sortedMetricIds : selectableGraphDatasetIds.value
return defaultIds.slice(0, graphDatasetSelectionLimit)
return defaultIds
.filter((id) => !excludedGraphDatasetIds.value.has(id))
.slice(0, graphDatasetSelectionLimit)
})
const tableSelectedGraphDatasetIds = computed<unknown[]>({
get: () => selectedGraphDatasetIds.value,
@@ -149,7 +158,9 @@ export function useAnalyticsTableGraphSelection({
defaultGraphDatasetIds.value = nextShowGraphDatasetSelection
? [...nextDefaultGraphDatasetIds]
: []
topGraphDatasetIds.value = nextShowGraphDatasetSelection ? [...nextTopGraphDatasetIds] : []
topGraphDatasetIds.value = nextShowGraphDatasetSelection
? nextTopGraphDatasetIds.filter((id) => !excludedGraphDatasetIds.value.has(id))
: []
},
{ immediate: true },
)
@@ -184,6 +195,20 @@ export function useAnalyticsTableGraphSelection({
return Array.from(new Set(rows.map((row) => row.graphDatasetId)))
}
function getAnalyticsTableExcludedGraphDatasetIds(rows: AnalyticsTableRow[]): Set<string> {
return new Set(
rows
.filter((row) =>
Object.values(row.breakdownValues).some(
(value) =>
isUnknownAnalyticsBreakdownValue(value) ||
isNoDependentAnalyticsBreakdownValue(value),
),
)
.map((row) => row.graphDatasetId),
)
}
return {
filteredSelectableGraphDatasetIds,
tableSelectedGraphDatasetIds,

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