mirror of
https://github.com/modrinth/code.git
synced 2026-07-31 21:26:40 +00:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa769337cf | ||
|
|
453369ca07 | ||
|
|
faf593b2af | ||
|
|
e3d6a498d0 | ||
|
|
42cdcc7df9 | ||
|
|
e043a232bc | ||
|
|
c44ead2dbe | ||
|
|
11ac27f71f | ||
|
|
6862cf5ab2 | ||
|
|
16e1bf4611 | ||
|
|
77e4c41480 | ||
|
|
cb93c641d6 | ||
|
|
eebb353547 | ||
|
|
694ab09a01 | ||
|
|
da47c50320 | ||
|
|
bee4391df1 | ||
|
|
d1b122fb21 | ||
|
|
281bf066de |
@@ -55,76 +55,7 @@ jobs:
|
||||
- name: 📝 Extract app changelog
|
||||
env:
|
||||
VERSION: ${{ env.VERSION_TAG }}
|
||||
run: |
|
||||
node <<'EOF'
|
||||
const fs = require('fs');
|
||||
const version = process.env.VERSION.replace(/^v/, '');
|
||||
const src = fs.readFileSync('packages/blog/changelog.ts', 'utf8');
|
||||
|
||||
// Parse every entry in the VERSIONS array, preserving their order
|
||||
// (which is reverse chronological).
|
||||
const entryRe = /\{\s*date:\s*`([^`]+)`,\s*product:\s*'(\w+)',(?:\s*version:\s*[`']([^`']+)[`'],)?\s*body:\s*`([\s\S]*?)`,\s*\}/g;
|
||||
const entries = [];
|
||||
let match;
|
||||
while ((match = entryRe.exec(src)) !== null) {
|
||||
entries.push({
|
||||
date: match[1],
|
||||
product: match[2],
|
||||
version: match[3],
|
||||
body: match[4],
|
||||
});
|
||||
}
|
||||
|
||||
const currentIdx = entries.findIndex(
|
||||
(e) => e.product === 'app' && e.version === version,
|
||||
);
|
||||
if (currentIdx === -1) {
|
||||
console.error(`No app changelog entry found for version ${version}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Find the surrounding app entries so we can scope hosting changes to
|
||||
// exactly what shipped between the previous app release and this one.
|
||||
// Entries are in reverse chronological order, so newer entries have
|
||||
// smaller indices than older entries.
|
||||
let newerAppIdx = -1;
|
||||
for (let i = currentIdx - 1; i >= 0; i--) {
|
||||
if (entries[i].product === 'app') {
|
||||
newerAppIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let previousAppIdx = entries.length;
|
||||
for (let i = currentIdx + 1; i < entries.length; i++) {
|
||||
if (entries[i].product === 'app') {
|
||||
previousAppIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const hostingEntries = [];
|
||||
for (let i = newerAppIdx + 1; i < previousAppIdx; i++) {
|
||||
if (entries[i].product === 'hosting') {
|
||||
hostingEntries.push(entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
let output = entries[currentIdx].body;
|
||||
if (hostingEntries.length > 0) {
|
||||
// Demote any top-level section headings inside hosting bodies so
|
||||
// they nest cleanly under the "Modrinth Hosting (included)" header.
|
||||
const demoteHeadings = (body) =>
|
||||
body.replace(/^(#{1,5})\s/gm, (_, hashes) => `${hashes}# `);
|
||||
const hostingBody = hostingEntries
|
||||
.map((e) => demoteHeadings(e.body))
|
||||
.join('\n\n');
|
||||
output += `\n\n---\n\n## Modrinth Hosting (included)\n\n${hostingBody}`;
|
||||
}
|
||||
|
||||
fs.writeFileSync('release-notes.md', output);
|
||||
console.log(`Extracted changelog for app ${version}:`);
|
||||
console.log(output);
|
||||
EOF
|
||||
run: npx --yes tsx scripts/build-theseus-release-notes.ts
|
||||
|
||||
- name: 🛠️ Generate version manifest
|
||||
run: |
|
||||
|
||||
@@ -81,3 +81,6 @@ apps/frontend/src/public/robots.txt
|
||||
|
||||
# Oh My Code
|
||||
.omc/
|
||||
|
||||
# Local dry-run output for scripts/build-theseus-release-notes.mjs
|
||||
/test_result.md
|
||||
|
||||
Generated
+26
-14
@@ -27,7 +27,7 @@ checksum = "daa239b93927be1ff123eebada5a3ff23e89f0124ccb8609234e5103d5a5ae6d"
|
||||
dependencies = [
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -46,7 +46,7 @@ dependencies = [
|
||||
"actix-web",
|
||||
"bitflags 2.9.4",
|
||||
"bytes",
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"futures-core",
|
||||
"http-range",
|
||||
"log",
|
||||
@@ -72,7 +72,7 @@ dependencies = [
|
||||
"brotli",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
"foldhash",
|
||||
@@ -226,7 +226,7 @@ dependencies = [
|
||||
"bytestring",
|
||||
"cfg-if",
|
||||
"cookie 0.16.2",
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"encoding_rs",
|
||||
"foldhash",
|
||||
"futures-core",
|
||||
@@ -1902,6 +1902,15 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.16.2"
|
||||
@@ -2450,21 +2459,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.0.1"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||
dependencies = [
|
||||
"convert_case 0.10.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 2.0.106",
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -4914,7 +4925,7 @@ dependencies = [
|
||||
"const_format",
|
||||
"dashmap",
|
||||
"deadpool-redis",
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"dotenv-build",
|
||||
"dotenvy",
|
||||
"either",
|
||||
@@ -4941,6 +4952,7 @@ dependencies = [
|
||||
"paste",
|
||||
"path-util",
|
||||
"prometheus",
|
||||
"quick-xml 0.38.3",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"redis",
|
||||
@@ -5515,7 +5527,7 @@ name = "modrinth-util"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"dotenvy",
|
||||
"eyre",
|
||||
"modrinth-log",
|
||||
@@ -5581,7 +5593,7 @@ dependencies = [
|
||||
"arc-swap",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"reqwest 0.12.24",
|
||||
"rust_decimal",
|
||||
"rust_iso3166",
|
||||
@@ -6592,7 +6604,7 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
name = "path-util"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"itertools 0.14.0",
|
||||
"serde",
|
||||
"typed-path",
|
||||
@@ -9274,7 +9286,7 @@ dependencies = [
|
||||
name = "sqlx-tracing"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"futures",
|
||||
"opentelemetry",
|
||||
"opentelemetry-testing",
|
||||
@@ -10147,7 +10159,7 @@ dependencies = [
|
||||
"daedalus",
|
||||
"dashmap",
|
||||
"data-url",
|
||||
"derive_more 2.0.1",
|
||||
"derive_more 2.1.1",
|
||||
"dirs",
|
||||
"discord-rich-presence",
|
||||
"dotenvy",
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ darling = { version = "0.23" }
|
||||
dashmap = "6.1.0"
|
||||
data-url = "0.3.2"
|
||||
deadpool-redis = { git = "https://github.com/modrinth/deadpool", rev = "db5fb00b036ecc8fe5f18853c559b745ffe47bde", version = "0.22.1" }
|
||||
derive_more = "2.0.1"
|
||||
derive_more = "2.1.1"
|
||||
directories = "6.0.0"
|
||||
dirs = "6.0.0"
|
||||
discord-rich-presence = "1.0.0"
|
||||
|
||||
@@ -251,6 +251,9 @@
|
||||
"app.update.reload-to-update": {
|
||||
"message": "أعد التحميل لتثبيت التحديث"
|
||||
},
|
||||
"app.world.server-modal.placeholder-address": {
|
||||
"message": "مثال.مودرنث.جج"
|
||||
},
|
||||
"app.world.server-modal.select-an-option": {
|
||||
"message": "حدد خيارا"
|
||||
},
|
||||
|
||||
@@ -5,12 +5,147 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Připojení k autorizačním serverům se nezdařilo"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Přidat server do instance"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Přidat servery do instance"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Přidat do instance"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "Přidat do {instanceName}"
|
||||
},
|
||||
"app.browse.added": {
|
||||
"message": "Přidáno"
|
||||
},
|
||||
"app.browse.already-added": {
|
||||
"message": "Už přidáno"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Prozkoumat obsah"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Prozkoumat servery"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Skrýt přidané servery"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Skrýt nainstalovaný obsah"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Nainstalovat obsah do instnce"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Přidej popis modpacku..."
|
||||
},
|
||||
"app.export-modal.export-button": {
|
||||
"message": "Exportovat"
|
||||
},
|
||||
"app.export-modal.header": {
|
||||
"message": "Exportovat modpack"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Jméno Modpacku"
|
||||
},
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Jméno modpacku"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Vybrat soubory a složky co zahrnout do modpacku"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Číslo verze"
|
||||
},
|
||||
"app.export-modal.version-number-placeholder": {
|
||||
"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."
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-header": {
|
||||
"message": "Tato akce nemůže být vrácena"
|
||||
},
|
||||
"app.instance.confirm-delete.delete-button": {
|
||||
"message": "Odstranit instanci"
|
||||
},
|
||||
"app.instance.confirm-delete.header": {
|
||||
"message": "Odstanit instanci"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "Tento modpack je už nainstalovaný v instanci <bold>{instanceName}</bold>. Opravdu ho chceš duplikovat?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "Vytvořit"
|
||||
},
|
||||
"app.instance.modpack-already-installed.header": {
|
||||
"message": "Modpack už je nainstalovaný"
|
||||
},
|
||||
"app.instance.modpack-already-installed.instance": {
|
||||
"message": "Instance"
|
||||
},
|
||||
"app.instance.mods.content-type-project": {
|
||||
"message": "projekt"
|
||||
},
|
||||
"app.instance.mods.project-was-added": {
|
||||
"message": "\"{name}\" bylo přidáno"
|
||||
},
|
||||
"app.instance.mods.projects-were-added": {
|
||||
"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!"
|
||||
},
|
||||
"app.instance.mods.share-title": {
|
||||
"message": "Sdílení obsahu modpacku"
|
||||
},
|
||||
"app.instance.mods.successfully-uploaded": {
|
||||
"message": "Úspěšně nahráno"
|
||||
},
|
||||
"app.instance.worlds.add-server": {
|
||||
"message": "Přidat server"
|
||||
},
|
||||
"app.instance.worlds.browse-servers": {
|
||||
"message": "Procházet servery"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "\"{name}\" bude **navždy smazáno** a nebude způsob, jak to obnovit."
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "Opravdu si jsi jistý, že chceš navždy smazat tenhle svět?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Modováno"
|
||||
},
|
||||
"app.instance.worlds.filter-offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
"app.instance.worlds.filter-online": {
|
||||
"message": "Online"
|
||||
},
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "Vanilla"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-description": {
|
||||
"message": "Přidej nebo procházej servery"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-heading": {
|
||||
"message": "Žádné servery nebo světy nebyly přidány"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "„{name}“ bude odstraněn z tvého seznamu, včetně hry, a nebude žádný způsob, jak ho obnovit."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Opravdu chceš odstranit {name}?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Hledat ve světech {count}..."
|
||||
},
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "tento server"
|
||||
},
|
||||
"app.modal.install-to-play.header": {
|
||||
"message": "Nainstaluj ke hraní"
|
||||
},
|
||||
@@ -81,7 +216,7 @@
|
||||
"message": "Přidat přítele"
|
||||
},
|
||||
"friends.action.view-friend-requests": {
|
||||
"message": "{count} {count, plural, one {žádost} few {žádosti} other {žádostí}} o přátelství"
|
||||
"message": "{count} přátelé {count, plural, one {request} other {requests}}"
|
||||
},
|
||||
"friends.add-friend.submit": {
|
||||
"message": "Poslat žádost o přátelství"
|
||||
@@ -363,7 +498,7 @@
|
||||
"message": "Můžeš rovnou skočit na server pouze v Minecraftu Alpha 1.0.5+"
|
||||
},
|
||||
"instance.worlds.no_singleplayer_quick_play": {
|
||||
"message": "Můžeš se rovnou připojit do světa jednoho hráče pouze v Minecraftu 1.20+"
|
||||
"message": "V Minecraftu 1.20+ se dá rovnou přeskočit pouze do singleplayerových světů"
|
||||
},
|
||||
"instance.worlds.play_instance": {
|
||||
"message": "Hrát instanci"
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"message": "1.0.0"
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-body": {
|
||||
"message": "Alle Daten für deine Instanz werden permanent gelöscht, einschließlich deiner Welten, Konfigurationen und allen installierten Inhalten."
|
||||
"message": "Alle Daten deiner Instanz werden permanent gelöscht, einschließlich deiner Welten, Konfigurationen und allen installierten Inhalten."
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-header": {
|
||||
"message": "Diese Aktion kann nicht rückgängig gemacht werden"
|
||||
|
||||
@@ -5,8 +5,53 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Todennuspalvelimiin ei saada yhteyttä"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Lisää palvelin instanssiin"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Lisää palvelimia instanssiisi"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Lisää instanssiin"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "Lisää instanssiin {instanceName}"
|
||||
},
|
||||
"app.browse.added": {
|
||||
"message": "Lisätty"
|
||||
},
|
||||
"app.browse.already-added": {
|
||||
"message": "On jo asennettu"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Löydä sisältöä"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Löydä palvelimia"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Piilota lisätyt palvelimet"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Piilota asennettu sisältö"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Lataa sisältöä instanssiin"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Lisää modipaketin kuvaus..."
|
||||
},
|
||||
"app.export-modal.export-button": {
|
||||
"message": "Vie"
|
||||
},
|
||||
"app.export-modal.header": {
|
||||
"message": "Vie modipaketti"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Modipaketin nimi"
|
||||
},
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Modpack nimi"
|
||||
"message": "Modipaketin nimi"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Valitse tiedostot ja kansiot pakettiin"
|
||||
@@ -17,12 +62,90 @@
|
||||
"app.export-modal.version-number-placeholder": {
|
||||
"message": "1.0.0"
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-body": {
|
||||
"message": "Kaikki sinun instanssisi data poistetaan pysyvästi, sisältäen maailmasi, konfiguraatiosi ja asennetun sisällön."
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-header": {
|
||||
"message": "Tätä toimintoa ei voi peruuttaa"
|
||||
},
|
||||
"app.instance.confirm-delete.delete-button": {
|
||||
"message": "Poista instannsi"
|
||||
},
|
||||
"app.instance.confirm-delete.header": {
|
||||
"message": "Poista instanssi"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "Tämä modipaketti on jo asennettu instanssiin <bold>{instanceName}</bold>. Oletko varma että haluat kopioida sen?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "Luo"
|
||||
},
|
||||
"app.instance.modpack-already-installed.header": {
|
||||
"message": "Modipaketti on jo asennettu"
|
||||
},
|
||||
"app.instance.modpack-already-installed.instance": {
|
||||
"message": "Instanssi"
|
||||
},
|
||||
"app.instance.mods.content-type-project": {
|
||||
"message": "projekti"
|
||||
},
|
||||
"app.instance.mods.project-was-added": {
|
||||
"message": "\"{name}\" lisättiin"
|
||||
},
|
||||
"app.instance.mods.projects-were-added": {
|
||||
"message": "{count} projektia lisättiin"
|
||||
},
|
||||
"app.instance.mods.share-text": {
|
||||
"message": "Tsekkaa projektit joita käytän minun modipaketissani!"
|
||||
},
|
||||
"app.instance.mods.successfully-uploaded": {
|
||||
"message": "Onnistuneesti ladattu"
|
||||
},
|
||||
"app.instance.worlds.add-server": {
|
||||
"message": "Lisää palvelin"
|
||||
},
|
||||
"app.instance.worlds.browse-servers": {
|
||||
"message": "Selaa palvelimia"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "'{name}' tullaan **poistamaan pysyvästi**, eikä tule olemaan mitään tapaa palauttaa sitä."
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "Oletko varma että haluat pysyvästi poistaa tämän maailman?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Modattu"
|
||||
},
|
||||
"app.instance.worlds.filter-offline": {
|
||||
"message": "Offline-tilassa"
|
||||
},
|
||||
"app.instance.worlds.filter-online": {
|
||||
"message": "Online-tilassa"
|
||||
},
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "Vanilla"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-description": {
|
||||
"message": "Lisää palvelin tai selaa aloittaaksesi"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-heading": {
|
||||
"message": "Ei palvelimia tai maailmoja lisättynä"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "'{name}' tullaan poistamaan sinun listaltasi, mukaan lukien pelin sisällä, eikä tule olemaan mitään tapaa palauttaa sitä."
|
||||
},
|
||||
"app.instance.worlds.remove-server-description-with-address": {
|
||||
"message": "'{name}' ({address}) tullaan poistamaan sinun listaltasi, mukaan lukien pelin sisällä, eikä tule olemaan mitään tapaa palauttaa sitä."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Oletko varma että haluat poistaa {name}?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Hae {count} maailmasta..."
|
||||
},
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "tämän palvelimen"
|
||||
},
|
||||
"app.modal.install-to-play.content-required": {
|
||||
"message": "Sisältö vaaditaan"
|
||||
},
|
||||
@@ -33,7 +156,7 @@
|
||||
"message": "Asenna"
|
||||
},
|
||||
"app.modal.install-to-play.mod-count": {
|
||||
"message": "one {{count, plural, one {# mod} other {# modia}}"
|
||||
"message": "{count, plural, one {# mod} other {# modia}}"
|
||||
},
|
||||
"app.modal.install-to-play.required-modpack": {
|
||||
"message": "Vaadittu modipaketti"
|
||||
@@ -125,6 +248,24 @@
|
||||
"app.update.reload-to-update": {
|
||||
"message": "Lataa uudelleen asentaaksesi päivityksen"
|
||||
},
|
||||
"app.world.server-modal.placeholder-address": {
|
||||
"message": "example.modrinth.gg"
|
||||
},
|
||||
"app.world.server-modal.select-an-option": {
|
||||
"message": "Valitse vaihtoehto"
|
||||
},
|
||||
"app.world.world-item.incompatible-version": {
|
||||
"message": "Yhteensopimaton versio {version}"
|
||||
},
|
||||
"app.world.world-item.not-played-yet": {
|
||||
"message": "Ei pelattu vielä"
|
||||
},
|
||||
"app.world.world-item.offline": {
|
||||
"message": "Offline-tilassa"
|
||||
},
|
||||
"app.world.world-item.players-online": {
|
||||
"message": "{count} paikalla"
|
||||
},
|
||||
"friends.action.add-friend": {
|
||||
"message": "Lisää ystävä"
|
||||
},
|
||||
@@ -224,6 +365,12 @@
|
||||
"instance.edit-world.title": {
|
||||
"message": "Muokkaa maailmaa"
|
||||
},
|
||||
"instance.files.adding-files": {
|
||||
"message": "Lisätään tiedostoja ({completed}/{total})"
|
||||
},
|
||||
"instance.files.save-as": {
|
||||
"message": "Tallenna nimellä..."
|
||||
},
|
||||
"instance.server-modal.address": {
|
||||
"message": "Osoite"
|
||||
},
|
||||
@@ -441,7 +588,7 @@
|
||||
"message": "Palvelimen tarjoama"
|
||||
},
|
||||
"search.filter.locked.server-environment.title": {
|
||||
"message": "Voit lisätä vain paikallisia modeja palvelinasennukseen"
|
||||
"message": "Voit lisätä vain paikallisia modeja palvelin instanssiin"
|
||||
},
|
||||
"search.filter.locked.server-game-version.title": {
|
||||
"message": "Peliversio on palvelimen tarjoama"
|
||||
|
||||
@@ -110,6 +110,15 @@
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "Vanilla"
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Sigurado ka bang gusto mong tanggalin ang {name}?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Hanapin sa {count} mundo..."
|
||||
},
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "server na ito"
|
||||
},
|
||||
"app.modal.install-to-play.content-required": {
|
||||
"message": "Nangangailangan ng kontento"
|
||||
},
|
||||
@@ -212,6 +221,15 @@
|
||||
"app.update.reload-to-update": {
|
||||
"message": "Handang ma-install ang update"
|
||||
},
|
||||
"app.world.server-modal.placeholder-address": {
|
||||
"message": "example.modrinth.gg"
|
||||
},
|
||||
"app.world.server-modal.select-an-option": {
|
||||
"message": "Pumili ng opsiyon"
|
||||
},
|
||||
"app.world.world-item.incompatible-version": {
|
||||
"message": "Di-magkatugmang bersiyong {version}"
|
||||
},
|
||||
"app.world.world-item.offline": {
|
||||
"message": "Offline"
|
||||
},
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
"message": "Jette un coup d'œil aux projets que j'utilise dans mon modpack !"
|
||||
},
|
||||
"app.instance.mods.share-title": {
|
||||
"message": "Partagez le contenu de votre modpack"
|
||||
"message": "Partage du contenu de votre modpack"
|
||||
},
|
||||
"app.instance.mods.successfully-uploaded": {
|
||||
"message": "Mis en ligne avec succès"
|
||||
|
||||
@@ -5,6 +5,39 @@
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Tidak dapat terhubung ke server autentikasi"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Tambah server ke instans"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Tambah server ke instans Anda"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Tambah ke instans"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "Tambah ke {instanceName}"
|
||||
},
|
||||
"app.browse.added": {
|
||||
"message": "Ditambahkan"
|
||||
},
|
||||
"app.browse.already-added": {
|
||||
"message": "Telah ditambahkan"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Temukan konten"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Temukan server"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Sembuyikan server tertambah"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Sembunyikan konten terpasang"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Pasang konten ke instans"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Masukkan deskripsi paket mod..."
|
||||
},
|
||||
@@ -41,9 +74,18 @@
|
||||
"app.instance.confirm-delete.header": {
|
||||
"message": "Hapus instans"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "Paket mod ini telah terpasang di instans <bold>{instanceName}</bold>. Apakah Anda yakin ingin menggandakannya?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "Buat"
|
||||
},
|
||||
"app.instance.modpack-already-installed.header": {
|
||||
"message": "Paket mod telah terpasang"
|
||||
},
|
||||
"app.instance.modpack-already-installed.instance": {
|
||||
"message": "Instans"
|
||||
},
|
||||
"app.instance.mods.content-type-project": {
|
||||
"message": "proyek"
|
||||
},
|
||||
@@ -62,6 +104,51 @@
|
||||
"app.instance.mods.successfully-uploaded": {
|
||||
"message": "Berhasil diunggah"
|
||||
},
|
||||
"app.instance.worlds.add-server": {
|
||||
"message": "Tambah server"
|
||||
},
|
||||
"app.instance.worlds.browse-servers": {
|
||||
"message": "Telusuri server"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "'{name}' akan **dihapus secara permanen**, dan Anda tidak akan dapat memulihkannya."
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "Apakah Anda yakin ingin menghapus dunia ini secara permanen?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Dimodifikasi"
|
||||
},
|
||||
"app.instance.worlds.filter-offline": {
|
||||
"message": "Luring"
|
||||
},
|
||||
"app.instance.worlds.filter-online": {
|
||||
"message": "Daring"
|
||||
},
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "Vanila"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-description": {
|
||||
"message": "Tambah server atau telusuri untuk memulai"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-heading": {
|
||||
"message": "Tidak ada server atau dunia yang ditambahkan"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "'{name}' akan dihapus dari daftar Anda, termasuk di dalam permainan, dan Anda tidak akan dapat memulihkannya."
|
||||
},
|
||||
"app.instance.worlds.remove-server-description-with-address": {
|
||||
"message": "'{name}' ({address}) akan dihapus dari daftar Anda, termasuk di dalam permainan, dan Anda tidak akan dapat memulihkannya."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Apakah Anda yakin ingin menghapus {name}?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Cari {count} dunia..."
|
||||
},
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "server ini"
|
||||
},
|
||||
"app.modal.install-to-play.content-required": {
|
||||
"message": "Konten diperlukan"
|
||||
},
|
||||
@@ -164,6 +251,24 @@
|
||||
"app.update.reload-to-update": {
|
||||
"message": "Muat ulang untuk memasang pembaruan"
|
||||
},
|
||||
"app.world.server-modal.placeholder-address": {
|
||||
"message": "example.modrinth.gg"
|
||||
},
|
||||
"app.world.server-modal.select-an-option": {
|
||||
"message": "Pilih opsi"
|
||||
},
|
||||
"app.world.world-item.incompatible-version": {
|
||||
"message": "Versi {version} tidak cocok"
|
||||
},
|
||||
"app.world.world-item.not-played-yet": {
|
||||
"message": "Belum pernah dimainkan"
|
||||
},
|
||||
"app.world.world-item.offline": {
|
||||
"message": "Luring"
|
||||
},
|
||||
"app.world.world-item.players-online": {
|
||||
"message": "{count} orang daring"
|
||||
},
|
||||
"friends.action.add-friend": {
|
||||
"message": "Tambah teman"
|
||||
},
|
||||
@@ -263,6 +368,12 @@
|
||||
"instance.edit-world.title": {
|
||||
"message": "Sunting dunia"
|
||||
},
|
||||
"instance.files.adding-files": {
|
||||
"message": "Menambahkan berkas ({completed}/{total})"
|
||||
},
|
||||
"instance.files.save-as": {
|
||||
"message": "Simpan sebagai..."
|
||||
},
|
||||
"instance.server-modal.address": {
|
||||
"message": "Alamat"
|
||||
},
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"message": "Verwijder instantie"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "Deze modpakket is al in de <bold>{instanceName}<bold> instantie geïnstalleerd. Ben je zeker dat je hetgeen wil dupliceren?"
|
||||
"message": "Deze modpakket is al in de <bold>{instanceName}<bold> instantie geïnstalleerd. Ben je zeker dat je het geen wil dupliceren?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "Maak"
|
||||
@@ -93,7 +93,7 @@
|
||||
"message": "\"{name}\" was toegevoegd"
|
||||
},
|
||||
"app.instance.mods.projects-were-added": {
|
||||
"message": "{count} projecten waren toegevoegd"
|
||||
"message": "{count} Projecten waren toegevoegd"
|
||||
},
|
||||
"app.instance.mods.share-text": {
|
||||
"message": "Bekijk de projecten die ik in mijn modpack gebruik!"
|
||||
@@ -111,7 +111,10 @@
|
||||
"message": "Zoek servers"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "{name} zal **permanent verwijderd** worden en kan niet hersteld worden."
|
||||
"message": "{name} Zal **permanent verwijderd** worden en kan niet hersteld worden."
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "Weet je zeker dat je deze wereld definitief wilt verwijderen?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Gemod"
|
||||
@@ -131,6 +134,12 @@
|
||||
"app.instance.worlds.no-worlds-heading": {
|
||||
"message": "Noch servers noch werelden toegevoegd"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "'{name}' wordt uit je lijst verwijderd, ook in de game, en kan op geen enkele manier worden hersteld."
|
||||
},
|
||||
"app.instance.worlds.remove-server-description-with-address": {
|
||||
"message": "'{name}' ({address}) wordt uit je lijst verwijderd, ook in de game, en kan op geen enkele manier worden hersteld."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Ben je zeker dat je {name} wil verwijderen?"
|
||||
},
|
||||
@@ -447,7 +456,7 @@
|
||||
"message": "Uitgevoerd nadat het spel is afgesloten."
|
||||
},
|
||||
"instance.settings.tabs.hooks.post-exit.enter": {
|
||||
"message": "Voer na-afsluitcommando in..."
|
||||
"message": "Voer een opdracht na het afsluiten in..."
|
||||
},
|
||||
"instance.settings.tabs.hooks.pre-launch": {
|
||||
"message": "Voor het opstarten"
|
||||
@@ -513,7 +522,7 @@
|
||||
"message": "De hoogte van het spelvenster wanneer opgestart."
|
||||
},
|
||||
"instance.settings.tabs.window.height.enter": {
|
||||
"message": "Vul hoogte in..."
|
||||
"message": "Voer de lengte in..."
|
||||
},
|
||||
"instance.settings.tabs.window.width": {
|
||||
"message": "Breedte"
|
||||
@@ -555,7 +564,7 @@
|
||||
"message": "Je kan alleen direct de servers in springen in Minecraft Alpha 1.0.5+"
|
||||
},
|
||||
"instance.worlds.no_singleplayer_quick_play": {
|
||||
"message": "Je kan alleen direct je eigen werelden in springen in Minecraft 1.20+"
|
||||
"message": "Je kan alleen direct je eigen werelden in springen in Minecraft 1,20+"
|
||||
},
|
||||
"instance.worlds.play_instance": {
|
||||
"message": "Speel instantie"
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"message": "Instalar conteúdo na instância"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Insira a descrição do pacote de mods..."
|
||||
"message": "Insira uma descrição para o pacote de mods..."
|
||||
},
|
||||
"app.export-modal.export-button": {
|
||||
"message": "Exportar"
|
||||
|
||||
@@ -107,6 +107,9 @@
|
||||
"app.update.reload-to-update": {
|
||||
"message": "Reîncarcă pentru a instala actualizarea"
|
||||
},
|
||||
"app.world.server-modal.select-an-option": {
|
||||
"message": "Selectează o opțiune"
|
||||
},
|
||||
"friends.action.add-friend": {
|
||||
"message": "Adaugă un prieten"
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "Скрывать установленные"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Установить контент в сборку"
|
||||
"message": "Установка контента в сборку"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Введите описание сборки..."
|
||||
@@ -45,7 +45,7 @@
|
||||
"message": "Экспортировать"
|
||||
},
|
||||
"app.export-modal.header": {
|
||||
"message": "Экспорт сборки модов"
|
||||
"message": "Экспорт сборки"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Название сборки"
|
||||
@@ -63,10 +63,10 @@
|
||||
"message": "1.0.0"
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-body": {
|
||||
"message": "Все данные вашей инстанции будут окончательно удалены, включая ваши миры, настройки и весь установленный контент."
|
||||
"message": "Все данные сборки будут удалены навсегда, в том числе миры, настройки и весь установленный контент."
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-header": {
|
||||
"message": "Это действие нельзя обратить"
|
||||
"message": "Это действие необратимо"
|
||||
},
|
||||
"app.instance.confirm-delete.delete-button": {
|
||||
"message": "Удалить сборку"
|
||||
@@ -75,13 +75,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": "Сборка"
|
||||
@@ -96,7 +96,7 @@
|
||||
"message": "{count} Проектов было добавлено"
|
||||
},
|
||||
"app.instance.mods.share-text": {
|
||||
"message": "Посмотрите проекты, которые я использую в своей сборке модов!"
|
||||
"message": "Что в моей сборке:"
|
||||
},
|
||||
"app.instance.mods.share-title": {
|
||||
"message": "Содержимое сборки"
|
||||
@@ -111,10 +111,10 @@
|
||||
"message": "Найти серверы"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "'{name}' будет **удалён навсегда**, и будет невозможно восстановить."
|
||||
"message": "«{name}» будет **удалён навсегда**. Его невозможно восстановить."
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "Вы уверены, что хотите удалить этот мир навсегда?"
|
||||
"message": "Вы действительно хотите удалить этот мир?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Модовые"
|
||||
@@ -129,19 +129,19 @@
|
||||
"message": "Ванильные"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-description": {
|
||||
"message": "Добавить сервер или найти, чтобы начать"
|
||||
"message": "Добавьте или найдите сервер"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-heading": {
|
||||
"message": "Сервера или мира не добавлены"
|
||||
"message": "Нет серверов и миров"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "'{name}' будет удален из вашего списка, включая в игре, и восстановить будет невозможно."
|
||||
"message": "«{name}» будет удалён из списка — в том числе в игре. Его невозможно восстановить."
|
||||
},
|
||||
"app.instance.worlds.remove-server-description-with-address": {
|
||||
"message": "'{name}' ({address}) будет удален из вашего списка, включая в игре, и восстановить будет невозможно."
|
||||
"message": "«{name}» ({address}) будет удалён из списка — в том числе в игре. Его невозможно восстановить."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Вы уверены, что хотите удалить {name}?"
|
||||
"message": "Вы действительно хотите удалить {name}?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Поиск по {count, plural, one {# миру} other {# мирам}}..."
|
||||
@@ -261,7 +261,7 @@
|
||||
"message": "Несовместимая версия {version}"
|
||||
},
|
||||
"app.world.world-item.not-played-yet": {
|
||||
"message": "Ещё не играли"
|
||||
"message": "Ни разу не сыграно"
|
||||
},
|
||||
"app.world.world-item.offline": {
|
||||
"message": "Не в сети"
|
||||
@@ -291,7 +291,7 @@
|
||||
"message": "Какое имя у друга на Modrinth?"
|
||||
},
|
||||
"friends.add-friends-to-share": {
|
||||
"message": "<link>Добавьте друзей</link>, чтобы видеть, во что они играют!"
|
||||
"message": "<link>Добавьте друзей</link> и следите за ними!"
|
||||
},
|
||||
"friends.friend.cancel-request": {
|
||||
"message": "Отменить запрос"
|
||||
@@ -330,7 +330,7 @@
|
||||
"message": "{title} — {count}"
|
||||
},
|
||||
"friends.sign-in-to-add-friends": {
|
||||
"message": "<link>Войдите в аккаунт Modrinth</link>, чтобы добавлять друзей и видеть, во что они играют!"
|
||||
"message": "<link>Войдите в аккаунт Modrinth</link>, чтобы добавлять друзей и следить за ними!"
|
||||
},
|
||||
"instance.add-server.add-and-play": {
|
||||
"message": "Добавить и играть"
|
||||
@@ -396,7 +396,7 @@
|
||||
"message": "Удалить сборку"
|
||||
},
|
||||
"instance.settings.tabs.general.delete.description": {
|
||||
"message": "Навсегда удаляет сборку с вашего устройства, включая миры, настройки и весь установленный контент. Учтите, что после удаления сборки восстановить её будет невозможно."
|
||||
"message": "Навсегда удаляет сборку с устройства, в том числе миры, настройки и весь установленный контент. Учтите, что после удаления сборки её невозможно восстановить."
|
||||
},
|
||||
"instance.settings.tabs.general.deleting.button": {
|
||||
"message": "Удаление..."
|
||||
@@ -432,7 +432,7 @@
|
||||
"message": "Создать новую группу"
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups.description": {
|
||||
"message": "Разделение по группам позволяет организовать сборки по разным разделам в библиотеке."
|
||||
"message": "Позволяет организовать сборки по разным разделам в библиотеке."
|
||||
},
|
||||
"instance.settings.tabs.general.library-groups.enter-name": {
|
||||
"message": "Введите название группы"
|
||||
@@ -591,7 +591,7 @@
|
||||
"message": "Управляется сервером"
|
||||
},
|
||||
"search.filter.locked.server-environment.title": {
|
||||
"message": "В серверную сборку можно добавлять только моды для клиента"
|
||||
"message": "В серверной сборке доступны только клиентские моды"
|
||||
},
|
||||
"search.filter.locked.server-game-version.title": {
|
||||
"message": "Версия игры управляется сервером"
|
||||
|
||||
@@ -1,16 +1,154 @@
|
||||
{
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "Minecraft serveri za autentifikaciju su možda trenutno nedostupni. Molimo vas da proverite vašu internet vezu i pokušajte ponovo kasnije."
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "Serveri za autentifikaciju su nedostupni"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Dodaj server na instancu"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Dodaj servere na tvoju instancu"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Dodaj na instancu"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "Dodaj na {instanceName}"
|
||||
},
|
||||
"app.browse.added": {
|
||||
"message": "Dodato"
|
||||
},
|
||||
"app.browse.already-added": {
|
||||
"message": "Već je dodato"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "Otkrij sadržaj"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Otkrij servere"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Sakrije dodate servere"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Sakrije instalirani sadržaj"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "Instaliraj sadržaj na instancu"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Ukucaj opis modpacka..."
|
||||
},
|
||||
"app.export-modal.export-button": {
|
||||
"message": "Izvoz"
|
||||
},
|
||||
"app.export-modal.header": {
|
||||
"message": "Izvezi modpack"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Ime Modpacka"
|
||||
},
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Ime modpacka"
|
||||
},
|
||||
"app.export-modal.select-files-label": {
|
||||
"message": "Izaberi fajlove i foldere da uključiš u pack"
|
||||
},
|
||||
"app.export-modal.version-number-label": {
|
||||
"message": "Broj verzije"
|
||||
},
|
||||
"app.export-modal.version-number-placeholder": {
|
||||
"message": "1.0.0"
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-body": {
|
||||
"message": "Svi podaci za tvoju instancu će biti trajno izbrisani, uključujući tvoje svetove, konfiguracije, i sav instaliran sadržaj."
|
||||
},
|
||||
"app.instance.confirm-delete.admonition-header": {
|
||||
"message": "Ova akcija ne može biti poništena"
|
||||
},
|
||||
"app.instance.confirm-delete.delete-button": {
|
||||
"message": "Izbriši instancu"
|
||||
},
|
||||
"app.instance.confirm-delete.header": {
|
||||
"message": "Izbriši instancu"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "Ovaj modpack je već instaliran u <bold>{instanceName}</bold> instancu. Da li ste sigurni da želite da je duplirate?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "Napravi"
|
||||
},
|
||||
"app.instance.modpack-already-installed.header": {
|
||||
"message": "Modpack je već instaliran"
|
||||
},
|
||||
"app.instance.modpack-already-installed.instance": {
|
||||
"message": "Instanca"
|
||||
},
|
||||
"app.instance.mods.content-type-project": {
|
||||
"message": "projekat"
|
||||
},
|
||||
"app.instance.mods.project-was-added": {
|
||||
"message": "\"{name}\" je dodato"
|
||||
},
|
||||
"app.instance.mods.projects-were-added": {
|
||||
"message": "{count} projekata je dodato"
|
||||
},
|
||||
"app.instance.mods.share-text": {
|
||||
"message": "Pogledaj ove projekte koje ja koristim u svom modpacku!"
|
||||
},
|
||||
"app.instance.mods.share-title": {
|
||||
"message": "Deljenje sadržaja modpacka"
|
||||
},
|
||||
"app.instance.mods.successfully-uploaded": {
|
||||
"message": "Uspešno otpremljeno"
|
||||
},
|
||||
"app.instance.worlds.add-server": {
|
||||
"message": "Dodaj server"
|
||||
},
|
||||
"app.instance.worlds.browse-servers": {
|
||||
"message": "Pretraži servere"
|
||||
},
|
||||
"app.instance.worlds.delete-world-description": {
|
||||
"message": "'{name}' će biti **trajno izbrisan**, i neće biti načina da se vrati."
|
||||
},
|
||||
"app.instance.worlds.delete-world-title": {
|
||||
"message": "Da li ste sigurni da hoćete da trajno izbrišete ovaj svet?"
|
||||
},
|
||||
"app.instance.worlds.filter-modded": {
|
||||
"message": "Modifikovano"
|
||||
},
|
||||
"app.instance.worlds.filter-offline": {
|
||||
"message": "Oflajn"
|
||||
},
|
||||
"app.instance.worlds.filter-online": {
|
||||
"message": "Onlajn"
|
||||
},
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "Vanila"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-description": {
|
||||
"message": "Dodaj server ili pretraži da bi započeo"
|
||||
},
|
||||
"app.instance.worlds.no-worlds-heading": {
|
||||
"message": "Nema dodatih servera niti svetova"
|
||||
},
|
||||
"app.instance.worlds.remove-server-description": {
|
||||
"message": "'{name}' će biti izbrisan sa vaše liste, uključujući i unutar igre, i neće biti načina da se oporavi."
|
||||
},
|
||||
"app.instance.worlds.remove-server-description-with-address": {
|
||||
"message": "'{name}' ({address}) će biti izbrisan sa vaše liste, uključujući i unutar igre, i neće biti načina da se oporavi."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "Da li ste sigurni da hoćete da izbrišete {name}?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "Pretraži {count} svetova..."
|
||||
},
|
||||
"app.instance.worlds.this-server": {
|
||||
"message": "ovaj server"
|
||||
},
|
||||
"app.settings.tabs.appearance": {
|
||||
"message": "Izgled"
|
||||
},
|
||||
|
||||
@@ -2,18 +2,45 @@
|
||||
"app.auth-servers.unreachable.body": {
|
||||
"message": "เซิร์ฟเวอร์ตรวจสอบสิทธิ์ของ Minecraft อาจใช้งานไม่ได้ในขณะนี้ โปรดตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณและลองใหม่อีกครั้งในภายหลัง"
|
||||
},
|
||||
"app.auth-servers.unreachable.header": {
|
||||
"message": "ไม่สามารถเชื่อมต่อถึงเซิร์ฟเวอร์ได้"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "เพิ่มเซิร์ฟเวอร์ลงในอินสแตนซ์"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "เพิ่มเซิร์ฟเวอร์ลงในอินสแตนซ์ของคุณ"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "เพิ่มลงในอินสแตนซ์"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "เพิ่มลงใน {instanceName}"
|
||||
},
|
||||
"app.browse.added": {
|
||||
"message": "เพิ่ม"
|
||||
},
|
||||
"app.browse.already-added": {
|
||||
"message": "เพิ่มแล้ว"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "ซ่อนเซิร์ฟเวอร์ที่เพิ่มแล้ว"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "ซ่อนเนื้อหาที่ติดตั้งแล้ว"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "ติดตั้งเนื้อหาลงในอินสแตนซ์"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "กรอกคำอธิบายมอดแพ็ก.."
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "ชื่อมอดแพ็ก"
|
||||
},
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "ชื่อ ม็อดแพ็ก"
|
||||
},
|
||||
"app.export-modal.version-number-placeholder": {
|
||||
"message": "1.0.0"
|
||||
},
|
||||
@@ -26,6 +53,9 @@
|
||||
"app.instance.confirm-delete.delete-button": {
|
||||
"message": "ลบอินสแตนซ์"
|
||||
},
|
||||
"app.instance.confirm-delete.header": {
|
||||
"message": "ลบอินสแตนซ์"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "มอดแพ็กนี้ถูกติดตั้งไว้ในอินสแตนซ์ <bold>{instanceName}</bold> อยู่แล้ว คุณแน่ใจหรือไม่ว่าต้องการสร้างสำเนาซ้ำ?"
|
||||
},
|
||||
@@ -53,6 +83,9 @@
|
||||
"app.instance.mods.successfully-uploaded": {
|
||||
"message": "อัปโหลดสำเร็จ"
|
||||
},
|
||||
"app.instance.worlds.add-server": {
|
||||
"message": "เพิ่มเซิร์ฟเวอร์"
|
||||
},
|
||||
"app.instance.worlds.browse-servers": {
|
||||
"message": "ค้นหาเซิฟเวอร์"
|
||||
},
|
||||
@@ -116,6 +149,9 @@
|
||||
"app.modal.install-to-play.shared-server-instance": {
|
||||
"message": "อินสแตนซ์เซิร์ฟเวอร์ที่ใช้ร่วมกัน"
|
||||
},
|
||||
"app.modal.install-to-play.view-contents": {
|
||||
"message": "ดูเนื้อหา"
|
||||
},
|
||||
"app.modal.update-to-play.header": {
|
||||
"message": "อัปเดตเพื่อเล่น"
|
||||
},
|
||||
@@ -149,9 +185,21 @@
|
||||
"app.settings.tabs.resource-management": {
|
||||
"message": "การจัดการทรัพยากร"
|
||||
},
|
||||
"app.update-popup.body": {
|
||||
"message": "แอฟ Modrinth v{version} พร้อมสำหรับการติดตั้งแล้ว! รีโหลดแอปเพื่ออัปเดต หรือ อัปเดตอัตโนมัติเมื่อคุณปิดแอฟ Modrinth"
|
||||
},
|
||||
"app.update-popup.body.download-complete": {
|
||||
"message": "แอฟ Modrinth v{version} ดาวน์โหลดเสร็จแล้ว รีโหลดแอปเพื่ออัปเดต หรือ อัปเดตอัตโนมัติเมื่อคุณปิดแอฟ Modrinth"
|
||||
},
|
||||
"app.update-popup.body.linux": {
|
||||
"message": "Modrinth App v{version} พร้อมให้อัปเดตแล้ว โปรดใช้ตัวจัดการแพ็กเกจ ของคุณเพื่ออัปเดตฟีเจอร์ใหม่และตัวแก้ไขล่าสุด!"
|
||||
},
|
||||
"app.update-popup.body.metered": {
|
||||
"message": "แอฟ Modrinth v{version} พร้อมแล้ว! เนื่องจากคุณเปิดโหมดจำกัดปริมาณข้อมูล เราจึงไม่ได้อัปเดตอัตโนมัติ"
|
||||
},
|
||||
"app.update-popup.download": {
|
||||
"message": "ดาวโหลด ({size})"
|
||||
},
|
||||
"app.update-popup.download-complete": {
|
||||
"message": "ดาวน์โหลดเสร็จสมบูรณ์"
|
||||
},
|
||||
@@ -176,6 +224,9 @@
|
||||
"app.world.server-modal.placeholder-address": {
|
||||
"message": "example.modrinth.gg"
|
||||
},
|
||||
"app.world.server-modal.select-an-option": {
|
||||
"message": "เลือกตัวเลือก"
|
||||
},
|
||||
"app.world.world-item.incompatible-version": {
|
||||
"message": "เวอร์ชัน {version} ไม่รองรับ"
|
||||
},
|
||||
@@ -473,6 +524,9 @@
|
||||
"search.filter.locked.instance": {
|
||||
"message": "อินสแตนซ์เป็นผู้จัดเตรียม"
|
||||
},
|
||||
"search.filter.locked.instance.sync": {
|
||||
"message": "ซิงค์กับอินสแตนซ์"
|
||||
},
|
||||
"search.filter.locked.server": {
|
||||
"message": "เซิร์ฟเวอร์เป็นผู้จัดเตรียม"
|
||||
},
|
||||
|
||||
@@ -6,49 +6,49 @@
|
||||
"message": "Doğrulama sunucularına erişilemedi"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "Örneğe sunucuyu ekle"
|
||||
"message": "Sunucuyu kuruluma ekle"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "Örneğinize sunucu ekleyin"
|
||||
"message": "Kurulumunuza sunucular ekleyin"
|
||||
},
|
||||
"app.browse.add-to-instance": {
|
||||
"message": "Örneğe ekle"
|
||||
"message": "Kuruluma ekle"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "{instanceName} örneğine ekle"
|
||||
"message": "{instanceName}'a ekle"
|
||||
},
|
||||
"app.browse.added": {
|
||||
"message": "Eklendi"
|
||||
"message": "Ekli"
|
||||
},
|
||||
"app.browse.already-added": {
|
||||
"message": "Zaten eklendi"
|
||||
"message": "Zaten ekli"
|
||||
},
|
||||
"app.browse.discover-content": {
|
||||
"message": "İçerikleri keşfet"
|
||||
"message": "İçerik keşfet"
|
||||
},
|
||||
"app.browse.discover-servers": {
|
||||
"message": "Sunucuları keşfet"
|
||||
"message": "Sunucu keşfet"
|
||||
},
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Eklenen sunucuları gizle\n"
|
||||
"message": "Ekli sunucuları gizle"
|
||||
},
|
||||
"app.browse.hide-installed-content": {
|
||||
"message": "Yüklü İçerikleri Gizle"
|
||||
"message": "Kurulu içerikleri gizle"
|
||||
},
|
||||
"app.browse.install-content-to-instance": {
|
||||
"message": "İçeriği örneğe yükle"
|
||||
"message": "İçeriği kuruluma yükle"
|
||||
},
|
||||
"app.export-modal.description-placeholder": {
|
||||
"message": "Mod paketi açıklaması yazın..."
|
||||
"message": "Mod paketi açıklaması girin..."
|
||||
},
|
||||
"app.export-modal.export-button": {
|
||||
"message": "Çıkart"
|
||||
"message": "Dışa aktar"
|
||||
},
|
||||
"app.export-modal.header": {
|
||||
"message": "Modpaketi çıkart"
|
||||
"message": "Mod paketini dışa aktar"
|
||||
},
|
||||
"app.export-modal.modpack-name-label": {
|
||||
"message": "Modpaketi Adı"
|
||||
"message": "Mod Paketi Adı"
|
||||
},
|
||||
"app.export-modal.modpack-name-placeholder": {
|
||||
"message": "Modpaketi adı"
|
||||
@@ -69,22 +69,22 @@
|
||||
"message": "Bu eylem geri alınamaz"
|
||||
},
|
||||
"app.instance.confirm-delete.delete-button": {
|
||||
"message": "Örneği Sil"
|
||||
"message": "Kurulumu sil"
|
||||
},
|
||||
"app.instance.confirm-delete.header": {
|
||||
"message": "Örneği Sil"
|
||||
"message": "Kurulumu sil"
|
||||
},
|
||||
"app.instance.modpack-already-installed.body": {
|
||||
"message": "Bu mod paketi zaten <bold>{instanceName}</bold> örneğinde yüklü. Kopyalamak istediğinizden emin misiniz?"
|
||||
"message": "Bu mod paketi zaten <bold>{instanceName}</bold> kurulumunda yüklü. Kopyalamak istediğinizden emin misiniz?"
|
||||
},
|
||||
"app.instance.modpack-already-installed.create": {
|
||||
"message": "Oluştur"
|
||||
},
|
||||
"app.instance.modpack-already-installed.header": {
|
||||
"message": "Modpaketi zaten kurulu"
|
||||
"message": "Modpaketi zaten yüklü"
|
||||
},
|
||||
"app.instance.modpack-already-installed.instance": {
|
||||
"message": "Örnek"
|
||||
"message": "Kurulum"
|
||||
},
|
||||
"app.instance.mods.content-type-project": {
|
||||
"message": "proje"
|
||||
@@ -120,10 +120,10 @@
|
||||
"message": "Modlu"
|
||||
},
|
||||
"app.instance.worlds.filter-offline": {
|
||||
"message": "Çevrimdışı"
|
||||
"message": "Çevrim dışı"
|
||||
},
|
||||
"app.instance.worlds.filter-online": {
|
||||
"message": "Çevrimiçi"
|
||||
"message": "Çevrim içi"
|
||||
},
|
||||
"app.instance.worlds.filter-vanilla": {
|
||||
"message": "Vanilla"
|
||||
@@ -141,7 +141,7 @@
|
||||
"message": "'{name}' ({address}) oyun içi dahil olmak üzere listenizden kaldırılacak ve geri getirilmesi mümkün olmayacaktır."
|
||||
},
|
||||
"app.instance.worlds.remove-server-title": {
|
||||
"message": "{name} öğesini kaldırmak istediğinizden emin misiniz?"
|
||||
"message": "{name}'i kaldırmak istediğinizden emin misiniz?"
|
||||
},
|
||||
"app.instance.worlds.search-worlds-placeholder": {
|
||||
"message": "{count} dünya ara..."
|
||||
@@ -153,7 +153,7 @@
|
||||
"message": "İçerik gerekli"
|
||||
},
|
||||
"app.modal.install-to-play.header": {
|
||||
"message": "Yükleyip oynayın"
|
||||
"message": "Oynamak için yükleyin"
|
||||
},
|
||||
"app.modal.install-to-play.install-button": {
|
||||
"message": "Yükle"
|
||||
@@ -210,19 +210,19 @@
|
||||
"message": "Kaynak yönetimi"
|
||||
},
|
||||
"app.update-popup.body": {
|
||||
"message": "Modrinth App v{version} güncellemesi hazır! Hemen güncellemek için yeniden yükle veya Modrinth App’i kapattığında otomatik olarak güncellenecek."
|
||||
"message": "Modrinth App v{version} güncellemesi indirilmeye hazır! Hemen güncellemek için yeniden başlatın veya Modrinth App’i kapattığınızda otomatik olarak güncellenecek."
|
||||
},
|
||||
"app.update-popup.body.download-complete": {
|
||||
"message": "Modrinth App v{version} indirildi. Güncellemek için sayfayı yeniden yükle veya Modrinth App’i kapattığında otomatik olarak güncellenecek."
|
||||
"message": "Modrinth App v{version} indirildi. Güncellemek için yeniden başlatın veya Modrinth App’i kapattığınızda otomatik olarak güncellenecek."
|
||||
},
|
||||
"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!"
|
||||
},
|
||||
"app.update-popup.body.metered": {
|
||||
"message": "Modrinth App v{version} artık mevcut! Ölçüllü bağlantıda olduğunuzdan otomatik olarak indirmedik."
|
||||
"message": "Modrinth App v{version} mevcut! Kısıtlı bağlantıda olduğunuzdan otomatik olarak indirmedik."
|
||||
},
|
||||
"app.update-popup.changelog": {
|
||||
"message": "Değişiklikler"
|
||||
"message": "Yama Notları"
|
||||
},
|
||||
"app.update-popup.download": {
|
||||
"message": "İndir ({size})"
|
||||
@@ -231,7 +231,7 @@
|
||||
"message": "İndirme tamamlandı"
|
||||
},
|
||||
"app.update-popup.reload": {
|
||||
"message": "Yeniden yükle"
|
||||
"message": "Yeniden başlat"
|
||||
},
|
||||
"app.update-popup.title": {
|
||||
"message": "Güncelleme mevcut"
|
||||
@@ -249,7 +249,7 @@
|
||||
"message": "Güncelleme indiriliyor (%{percent})"
|
||||
},
|
||||
"app.update.reload-to-update": {
|
||||
"message": "Güncellemeyi kurmak için yeniden yükleyin"
|
||||
"message": "Güncellemeyi kurmak için yeniden başlatın"
|
||||
},
|
||||
"app.world.server-modal.placeholder-address": {
|
||||
"message": "example.modrinth.gg"
|
||||
@@ -264,7 +264,7 @@
|
||||
"message": "Henüz oynanmadı"
|
||||
},
|
||||
"app.world.world-item.offline": {
|
||||
"message": "Çevrimdışı"
|
||||
"message": "Çevrim dışı"
|
||||
},
|
||||
"app.world.world-item.players-online": {
|
||||
"message": "{count} çevrimiçi"
|
||||
@@ -390,10 +390,10 @@
|
||||
"message": "Genel"
|
||||
},
|
||||
"instance.settings.tabs.general.delete": {
|
||||
"message": "Profili sil"
|
||||
"message": "Kurulumu sil"
|
||||
},
|
||||
"instance.settings.tabs.general.delete.button": {
|
||||
"message": "Profili sil"
|
||||
"message": "Kurulumu sil"
|
||||
},
|
||||
"instance.settings.tabs.general.delete.description": {
|
||||
"message": "Profili, içindeki dünyaları, ayarları ve indirilen şeyleri cihazınızdan sonsuza kadar siler. Dikkatli olun, bir profil silindikten sonra geri alınamaz."
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"message": "無法連線到驗證伺服器"
|
||||
},
|
||||
"app.browse.add-server-to-instance": {
|
||||
"message": "新增此伺服器到實例中"
|
||||
"message": "將伺服器新增到實例"
|
||||
},
|
||||
"app.browse.add-servers-to-instance": {
|
||||
"message": "新增伺服器到實例中"
|
||||
@@ -15,7 +15,7 @@
|
||||
"message": "新增至實例"
|
||||
},
|
||||
"app.browse.add-to-instance-name": {
|
||||
"message": "新增到 {instanceName}"
|
||||
"message": "新增到「{instanceName}」"
|
||||
},
|
||||
"app.browse.added": {
|
||||
"message": "已新增"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div v-if="instance" class="flex h-full flex-col">
|
||||
<div v-if="instance" :class="{ 'flex h-full flex-col': isFixedRender }">
|
||||
<div
|
||||
class="shrink-0 p-6 pr-2 pb-4"
|
||||
:class="['p-6 pr-2 pb-4', { 'shrink-0': isFixedRender }]"
|
||||
@contextmenu.prevent.stop="(event) => handleRightClick(event)"
|
||||
>
|
||||
<ExportModal ref="exportModal" :instance="instance" />
|
||||
@@ -208,10 +208,10 @@
|
||||
</template>
|
||||
</ContentPageHeader>
|
||||
</div>
|
||||
<div class="shrink-0 px-6">
|
||||
<div :class="['px-6', { 'shrink-0': isFixedRender }]">
|
||||
<NavTabs :links="tabs" />
|
||||
</div>
|
||||
<div v-if="!!instance" class="min-h-0 flex-1 overflow-y-auto p-6 pt-4">
|
||||
<div :class="['p-6 pt-4', { 'min-h-0 flex-1 overflow-y-auto': isFixedRender }]">
|
||||
<RouterView
|
||||
v-if="route.path.startsWith('/instance')"
|
||||
v-slot="{ Component }"
|
||||
@@ -436,6 +436,19 @@ watch(
|
||||
|
||||
const basePath = computed(() => `/instance/${encodeURIComponent(route.params.id as string)}`)
|
||||
|
||||
/**
|
||||
* Per-route layout mode.
|
||||
* - `'scroll'` (default): the whole instance page scrolls inside `.app-viewport`. This lets
|
||||
* `position: sticky` children (and the viewport-rooted `IntersectionObserver` used by
|
||||
* `useStickyObserver`) work correctly.
|
||||
* - `'fixed'`: the header + tabs are pinned and only the tab body scrolls in its own container.
|
||||
* Used by tabs whose content (e.g. the log console) needs a bounded height to resolve `h-full`.
|
||||
*/
|
||||
const renderMode = computed<'scroll' | 'fixed'>(() =>
|
||||
route.meta.renderMode === 'fixed' ? 'fixed' : 'scroll',
|
||||
)
|
||||
const isFixedRender = computed(() => renderMode.value === 'fixed')
|
||||
|
||||
const tabs = computed(() => [
|
||||
{
|
||||
label: 'Content',
|
||||
|
||||
@@ -240,6 +240,7 @@ export default new createRouter({
|
||||
component: Instance.Logs,
|
||||
meta: {
|
||||
useRootContext: true,
|
||||
renderMode: 'fixed',
|
||||
breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Logs' }],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3155,6 +3155,12 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "monatlich"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "Quartal"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "vierteljährlich"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "Jahr"
|
||||
},
|
||||
@@ -3269,6 +3275,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "Fehler beim erneuten Abonnieren"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "Falls der Server derzeit abgesagt ist, kann die Einrichtung des Servers 10-15 Minuten dauern."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "Anfrage zum erneuten Abonnieren übermittelt"
|
||||
},
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"message": "Lade das RPM herunter"
|
||||
},
|
||||
"app-marketing.download.flathub": {
|
||||
"message": "Jetzt auf Flathub"
|
||||
"message": "Jetzt auf Flathub herunterladen"
|
||||
},
|
||||
"app-marketing.download.linux": {
|
||||
"message": "Linux"
|
||||
@@ -369,13 +369,13 @@
|
||||
"message": "Dadurch wird diese Kollektion dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden."
|
||||
},
|
||||
"collection.delete-modal.title": {
|
||||
"message": "Bist du Dir sicher, dass du diese Sammlung löschen willst?"
|
||||
"message": "Möchtest du diese Kollektion wirklich löschen?"
|
||||
},
|
||||
"collection.description": {
|
||||
"message": "{description} – Schau dir die Kollektion {name} von {username} auf Modrinth an"
|
||||
},
|
||||
"collection.description.following": {
|
||||
"message": "Automatisch generierte Sammlung aller Projekte, denen du folgst."
|
||||
"message": "Automatisch generierte Kollektion aller Projekte, denen du folgst."
|
||||
},
|
||||
"collection.editing": {
|
||||
"message": "Kollektion bearbeiten"
|
||||
@@ -3155,6 +3155,12 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "monatlich"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "Quartal"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "vierteljährlich"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "Jahr"
|
||||
},
|
||||
@@ -3269,6 +3275,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "Fehler beim erneuten Abonnieren"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "Falls der Server derzeit gekündigt ist kann die Einrichtung des Servers 10-15 Minuten dauern."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "Anfrage zum erneuten Abonnieren übermittelt"
|
||||
},
|
||||
|
||||
@@ -554,6 +554,9 @@
|
||||
"dashboard.affiliate-links.create.button": {
|
||||
"message": "Create affiliate link"
|
||||
},
|
||||
"dashboard.affiliate-links.empty.no-codes": {
|
||||
"message": "No affiliate codes found."
|
||||
},
|
||||
"dashboard.affiliate-links.error.title": {
|
||||
"message": "Error loading affiliate links"
|
||||
},
|
||||
@@ -572,6 +575,15 @@
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "Search affiliate links..."
|
||||
},
|
||||
"dashboard.analytics.from-projects": {
|
||||
"message": "from {count} {count, plural, one {project} other {projects}}"
|
||||
},
|
||||
"dashboard.analytics.total-downloads": {
|
||||
"message": "Total downloads"
|
||||
},
|
||||
"dashboard.analytics.total-followers": {
|
||||
"message": "Total followers"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "Create new"
|
||||
},
|
||||
@@ -596,6 +608,18 @@
|
||||
"dashboard.collections.long-title": {
|
||||
"message": "Your collections"
|
||||
},
|
||||
"dashboard.collections.placeholder.search": {
|
||||
"message": "Search collections..."
|
||||
},
|
||||
"dashboard.collections.sort.name-ascending": {
|
||||
"message": "Name (A-Z)"
|
||||
},
|
||||
"dashboard.collections.sort.recently-created": {
|
||||
"message": "Recently Created"
|
||||
},
|
||||
"dashboard.collections.sort.recently-updated": {
|
||||
"message": "Recently Updated"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "Download {formType}"
|
||||
},
|
||||
@@ -848,6 +872,168 @@
|
||||
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
|
||||
"message": "You've used up your <b>{withdrawLimit}</b> withdrawal limit. You must complete a tax form to withdraw more."
|
||||
},
|
||||
"dashboard.head-title": {
|
||||
"message": "Dashboard"
|
||||
},
|
||||
"dashboard.notifications.button.mark-all-as-read": {
|
||||
"message": "Mark all as read"
|
||||
},
|
||||
"dashboard.notifications.button.view-history": {
|
||||
"message": "View history"
|
||||
},
|
||||
"dashboard.notifications.empty.no-unread": {
|
||||
"message": "You don't have any unread notifications."
|
||||
},
|
||||
"dashboard.notifications.error.loading": {
|
||||
"message": "Error loading notifications:"
|
||||
},
|
||||
"dashboard.notifications.history.label": {
|
||||
"message": "History"
|
||||
},
|
||||
"dashboard.notifications.history.title": {
|
||||
"message": "Notification history"
|
||||
},
|
||||
"dashboard.notifications.link.see-all": {
|
||||
"message": "See all"
|
||||
},
|
||||
"dashboard.notifications.link.view-history": {
|
||||
"message": "View notification history"
|
||||
},
|
||||
"dashboard.notifications.link.view-more": {
|
||||
"message": "View {extraNotifs} more {extraNotifs, plural, one {notification} other {notifications}}"
|
||||
},
|
||||
"dashboard.notifications.loading": {
|
||||
"message": "Loading notifications..."
|
||||
},
|
||||
"dashboard.organizations.button.create": {
|
||||
"message": "Create organization"
|
||||
},
|
||||
"dashboard.organizations.empty.cta": {
|
||||
"message": "Make an organization!"
|
||||
},
|
||||
"dashboard.organizations.error.fetch": {
|
||||
"message": "Failed to fetch organizations"
|
||||
},
|
||||
"dashboard.organizations.member-count": {
|
||||
"message": "{count} {count, plural, one {member} other {members}}"
|
||||
},
|
||||
"dashboard.organizations.title": {
|
||||
"message": "Organizations"
|
||||
},
|
||||
"dashboard.projects.bulk-edit-hint": {
|
||||
"message": "You can edit multiple projects at once by selecting them below."
|
||||
},
|
||||
"dashboard.projects.bulk-edit.server-disabled": {
|
||||
"message": "Server projects do not support bulk editing"
|
||||
},
|
||||
"dashboard.projects.empty": {
|
||||
"message": "You don't have any projects yet. Click the green button above to begin."
|
||||
},
|
||||
"dashboard.projects.head-title": {
|
||||
"message": "Projects"
|
||||
},
|
||||
"dashboard.projects.links.and-more": {
|
||||
"message": "and {count} more..."
|
||||
},
|
||||
"dashboard.projects.links.button.clear-link": {
|
||||
"message": "Clear link"
|
||||
},
|
||||
"dashboard.projects.links.button.edit": {
|
||||
"message": "Edit links"
|
||||
},
|
||||
"dashboard.projects.links.changes-applied": {
|
||||
"message": "Changes will be applied to <strong>{count}</strong> {count, plural, one {project} other {projects}}."
|
||||
},
|
||||
"dashboard.projects.links.description": {
|
||||
"message": "Any links you specify below will be overwritten on each of the selected projects. Any you leave blank will be ignored. You can clear a link from all selected projects using the trash can button."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.description": {
|
||||
"message": "An invitation link to your Discord server."
|
||||
},
|
||||
"dashboard.projects.links.discord-invite.label": {
|
||||
"message": "Discord invite"
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.description": {
|
||||
"message": "A place for users to report bugs, issues, and concerns about your project."
|
||||
},
|
||||
"dashboard.projects.links.issue-tracker.label": {
|
||||
"message": "Issue tracker"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.cleared": {
|
||||
"message": "Existing link will be cleared"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-discord-url": {
|
||||
"message": "Enter a valid Discord invite URL"
|
||||
},
|
||||
"dashboard.projects.links.placeholder.valid-url": {
|
||||
"message": "Enter a valid URL"
|
||||
},
|
||||
"dashboard.projects.links.show-all-projects": {
|
||||
"message": "Show all projects"
|
||||
},
|
||||
"dashboard.projects.links.source-code.description": {
|
||||
"message": "A page/repository containing the source code for your project"
|
||||
},
|
||||
"dashboard.projects.links.source-code.label": {
|
||||
"message": "Source code"
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.description": {
|
||||
"message": "A page containing information, documentation, and help for the project."
|
||||
},
|
||||
"dashboard.projects.links.wiki-page.label": {
|
||||
"message": "Wiki page"
|
||||
},
|
||||
"dashboard.projects.notification.bulk-edit-success": {
|
||||
"message": "Bulk edited selected project's links."
|
||||
},
|
||||
"dashboard.projects.project.icon-alt": {
|
||||
"message": "Icon for {title}"
|
||||
},
|
||||
"dashboard.projects.project.moderator-message-aria": {
|
||||
"message": "Project has a message from the moderators. View the project to see more."
|
||||
},
|
||||
"dashboard.projects.project.review-environment-metadata": {
|
||||
"message": "Please review environment metadata"
|
||||
},
|
||||
"dashboard.projects.sort.ascending": {
|
||||
"message": "Ascending"
|
||||
},
|
||||
"dashboard.projects.sort.descending": {
|
||||
"message": "Descending"
|
||||
},
|
||||
"dashboard.projects.sort.option.name": {
|
||||
"message": "Name"
|
||||
},
|
||||
"dashboard.projects.sort.option.status": {
|
||||
"message": "Status"
|
||||
},
|
||||
"dashboard.projects.sort.option.type": {
|
||||
"message": "Type"
|
||||
},
|
||||
"dashboard.projects.table.icon": {
|
||||
"message": "Icon"
|
||||
},
|
||||
"dashboard.projects.table.id": {
|
||||
"message": "ID"
|
||||
},
|
||||
"dashboard.projects.table.name": {
|
||||
"message": "Name"
|
||||
},
|
||||
"dashboard.projects.table.status": {
|
||||
"message": "Status"
|
||||
},
|
||||
"dashboard.projects.table.type": {
|
||||
"message": "Type"
|
||||
},
|
||||
"dashboard.report.title": {
|
||||
"message": "Report {id}"
|
||||
},
|
||||
"dashboard.reports.active-title": {
|
||||
"message": "Active reports"
|
||||
},
|
||||
"dashboard.reports.title": {
|
||||
"message": "Reports"
|
||||
},
|
||||
"dashboard.revenue.available-now": {
|
||||
"message": "Available now"
|
||||
},
|
||||
@@ -884,6 +1070,9 @@
|
||||
"dashboard.revenue.transactions.btn.download-csv": {
|
||||
"message": "Download as CSV"
|
||||
},
|
||||
"dashboard.revenue.transactions.head-title": {
|
||||
"message": "Transaction history"
|
||||
},
|
||||
"dashboard.revenue.transactions.header": {
|
||||
"message": "Transactions"
|
||||
},
|
||||
@@ -893,9 +1082,18 @@
|
||||
"dashboard.revenue.transactions.none.desc": {
|
||||
"message": "Your payouts and withdrawals will appear here."
|
||||
},
|
||||
"dashboard.revenue.transactions.period.last-month": {
|
||||
"message": "Last month"
|
||||
},
|
||||
"dashboard.revenue.transactions.period.this-month": {
|
||||
"message": "This month"
|
||||
},
|
||||
"dashboard.revenue.transactions.see-all": {
|
||||
"message": "See all"
|
||||
},
|
||||
"dashboard.revenue.transactions.year.all": {
|
||||
"message": "All years"
|
||||
},
|
||||
"dashboard.revenue.withdraw.blocked-tin-mismatch": {
|
||||
"message": "Your withdrawals are temporarily locked because your TIN or SSN didn't match IRS records. Please contact support to reset and resubmit your tax form."
|
||||
},
|
||||
@@ -908,6 +1106,33 @@
|
||||
"dashboard.revenue.withdraw.header": {
|
||||
"message": "Withdraw"
|
||||
},
|
||||
"dashboard.sidebar.label.activeReports": {
|
||||
"message": "Active reports"
|
||||
},
|
||||
"dashboard.sidebar.label.analytics": {
|
||||
"message": "Analytics"
|
||||
},
|
||||
"dashboard.sidebar.label.creators": {
|
||||
"message": "Creators"
|
||||
},
|
||||
"dashboard.sidebar.label.dashboard": {
|
||||
"message": "Dashboard"
|
||||
},
|
||||
"dashboard.sidebar.label.notifications": {
|
||||
"message": "Notifications"
|
||||
},
|
||||
"dashboard.sidebar.label.organizations": {
|
||||
"message": "Organizations"
|
||||
},
|
||||
"dashboard.sidebar.label.overview": {
|
||||
"message": "Overview"
|
||||
},
|
||||
"dashboard.sidebar.label.projects": {
|
||||
"message": "Projects"
|
||||
},
|
||||
"dashboard.sidebar.label.revenue": {
|
||||
"message": "Revenue"
|
||||
},
|
||||
"dashboard.withdraw.completion.account": {
|
||||
"message": "Account"
|
||||
},
|
||||
|
||||
@@ -1371,7 +1371,7 @@
|
||||
"message": "Encuentre lo que busca, de forma rápida y sencilla"
|
||||
},
|
||||
"landing.heading.the-place-for-minecraft": {
|
||||
"message": "El lugar para Minecraft {content}"
|
||||
"message": "El lugar para {content} de Minecraft"
|
||||
},
|
||||
"landing.heading.the-place-for-minecraft.data-packs": {
|
||||
"message": "paquetes de datos"
|
||||
@@ -2646,13 +2646,13 @@
|
||||
"message": "Ver todos tus proyectos"
|
||||
},
|
||||
"scopes.projectRead.label": {
|
||||
"message": "Ver proyectos"
|
||||
"message": "Leer proyectos"
|
||||
},
|
||||
"scopes.projectWrite.description": {
|
||||
"message": "Escribir datos del proyecto"
|
||||
},
|
||||
"scopes.projectWrite.label": {
|
||||
"message": "Redactar proyectos"
|
||||
"message": "Escribir proyectos"
|
||||
},
|
||||
"scopes.reportCreate.description": {
|
||||
"message": "Crear informes"
|
||||
@@ -3348,7 +3348,7 @@
|
||||
"message": "Modo de desarrollador desactivado"
|
||||
},
|
||||
"settings.display.project-list-layouts.datapack": {
|
||||
"message": "Página de paquetes de datos"
|
||||
"message": "Página de Paquetes de Datos"
|
||||
},
|
||||
"settings.display.project-list-layouts.description": {
|
||||
"message": "Selecciona tu diseño preferido para cada página que muestre listas de proyectos en este dispositivo."
|
||||
@@ -3378,7 +3378,7 @@
|
||||
"message": "Página de servidores"
|
||||
},
|
||||
"settings.display.project-list-layouts.shader": {
|
||||
"message": "Página de Shaders"
|
||||
"message": "Página de Sombreadores"
|
||||
},
|
||||
"settings.display.project-list-layouts.title": {
|
||||
"message": "Diseños de la lista de proyectos"
|
||||
@@ -3474,7 +3474,7 @@
|
||||
"message": "Expira {inTime}"
|
||||
},
|
||||
"settings.pats.token.last-used": {
|
||||
"message": "Último uso usada"
|
||||
"message": "Último uso {ago}"
|
||||
},
|
||||
"settings.pats.token.never-used": {
|
||||
"message": "Nunca utilizado"
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
"app-marketing.download.download-rpm": {
|
||||
"message": "Lataa RPM"
|
||||
},
|
||||
"app-marketing.download.flathub": {
|
||||
"message": "Lataa: Flathub"
|
||||
},
|
||||
"app-marketing.download.linux": {
|
||||
"message": "Linux"
|
||||
},
|
||||
@@ -149,6 +152,9 @@
|
||||
"app-marketing.features.play.title": {
|
||||
"message": "Pelaa lempi modeillasi"
|
||||
},
|
||||
"app-marketing.features.sharing.description": {
|
||||
"message": "Rakenna, jaa, ja pelaa modipaketteja millä tahansa tuhansista modeista ja modipaketeista Modrinthissa."
|
||||
},
|
||||
"app-marketing.features.sharing.modpack": {
|
||||
"message": "Modipaketti"
|
||||
},
|
||||
@@ -161,12 +167,21 @@
|
||||
"app-marketing.features.unlike-any-launcher": {
|
||||
"message": "Erilainen kuin mikään muu launcheri"
|
||||
},
|
||||
"app-marketing.features.website.description": {
|
||||
"message": "Modrinth App on täysin integroitu sivuston kanssa, jotta saat pääsyn kaikkiin lempi projekteihisi appin kautta!"
|
||||
},
|
||||
"app-marketing.features.website.title": {
|
||||
"message": "Nettisivu integraatio"
|
||||
},
|
||||
"app-marketing.features.youve-used-before": {
|
||||
"message": "jota olet käyttänyt ennen"
|
||||
},
|
||||
"app-marketing.hero.app-screenshot-alt": {
|
||||
"message": "Kuvakaappaus Modrinth Appista jossa on Cobblemon instanssi auki sisältö sivulle."
|
||||
},
|
||||
"app-marketing.hero.description": {
|
||||
"message": "Modrinth App on uniikki, avoimen lähdekoodin launcheri joka antaa sinun pelata lempi modeillasi, ja pitää ne ajantasalla, kaikki yhdessä siistissä pakkauksessa."
|
||||
},
|
||||
"app-marketing.hero.download-button": {
|
||||
"message": "Lataa Modrinth App"
|
||||
},
|
||||
|
||||
@@ -2957,12 +2957,21 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "buwanan"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "tatlong buwan"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "taluhang-buwan"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "taon"
|
||||
},
|
||||
"settings.billing.interval.yearly": {
|
||||
"message": "taunan"
|
||||
},
|
||||
"settings.billing.midas.benefits.title": {
|
||||
"message": "Mga pakinabang"
|
||||
},
|
||||
"settings.billing.modal.cancel.action": {
|
||||
"message": "Kanselahin ang iyong subscription"
|
||||
},
|
||||
|
||||
@@ -567,7 +567,7 @@
|
||||
"message": "Révoquer"
|
||||
},
|
||||
"dashboard.affiliate-links.revoke-confirm.title": {
|
||||
"message": "Êtes-vous sûr.e de vouloir révoquer votre lien d'affiliation ''{title}'' ?"
|
||||
"message": "Êtes-vous sûr.e de vouloir révoquer votre lien d'affiliation « {title} » ?"
|
||||
},
|
||||
"dashboard.affiliate-links.search": {
|
||||
"message": "Rechercher des liens d'affiliation..."
|
||||
@@ -1278,7 +1278,7 @@
|
||||
"message": "Choisissez parmi les milliers de modpacks disponibles sur Modrinth ou créez le vôtre. Invitez vos amis lorsque vous êtes prêt à jouer."
|
||||
},
|
||||
"hosting-marketing.why.heading": {
|
||||
"message": "De modpack à serveur"
|
||||
"message": "Un modpack. Pouf, c'est un serveur."
|
||||
},
|
||||
"hosting-marketing.why.modern-reliable-hosting": {
|
||||
"message": "Profitez d'un hébergement moderne et fiable"
|
||||
@@ -3155,6 +3155,12 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "mensuel"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "trimestre"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "trimestriel"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "an"
|
||||
},
|
||||
@@ -3269,6 +3275,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "Erreur lors du réabonnement"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "Si le serveur est actuellement annulé, le mettre en place prendra 10 à 15 minutes."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "Demande de réabonnement envoyée"
|
||||
},
|
||||
|
||||
@@ -333,7 +333,7 @@
|
||||
"message": "Verifica email"
|
||||
},
|
||||
"auth.welcome.checkbox.subscribe": {
|
||||
"message": "Ricevi novità riguardo a Modrinth"
|
||||
"message": "Ricevi novità riguardanti Modrinth"
|
||||
},
|
||||
"auth.welcome.description": {
|
||||
"message": "Fai ora parte della grandiosa comunità di creatori ed esploratori che già costruiscono, aggiornano e stanno sul pezzo con le fantastiche mod."
|
||||
@@ -2037,7 +2037,7 @@
|
||||
"message": "{count} download"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Account creato"
|
||||
"message": "Iscrizione"
|
||||
},
|
||||
"profile.label.no-collections": {
|
||||
"message": "Questo utente non ha raccolte!"
|
||||
@@ -2217,7 +2217,7 @@
|
||||
"message": "Rivedi progetto"
|
||||
},
|
||||
"project.actions.servers-promo.description": {
|
||||
"message": "Modrinth Hosting è il modo più facile per giocare coi tuoi amici senza problemi!"
|
||||
"message": "Modrinth Hosting è il modo più facile per invitare i tuoi amici a giocare spensieratamente!"
|
||||
},
|
||||
"project.actions.servers-promo.pricing": {
|
||||
"message": "A partire da {price}<small> al mese</small>"
|
||||
@@ -3276,7 +3276,7 @@
|
||||
"message": "Errore nel riabbonamento"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "Se il server era stato annullato, potrebbero volerci 10-15 minuti per impostarlo."
|
||||
"message": "Se il server è attualmente disattivato, potrebbero volerci 10-15 minuti per configurarlo."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "Richiesta di riabbonamento inviata"
|
||||
|
||||
@@ -3155,6 +3155,12 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "월간"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "분기"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "분기별"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "년"
|
||||
},
|
||||
@@ -3269,6 +3275,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "재구독 오류"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "서버가 현재 취소된 상태라면, 서버를 다시 설정하는 데 10-15분 정도 소요될 수 있습니다."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "재구독 요청 접수됨"
|
||||
},
|
||||
|
||||
@@ -1692,7 +1692,7 @@
|
||||
"message": "Cari"
|
||||
},
|
||||
"layout.nav.upgrade-to-modrinth-plus": {
|
||||
"message": "Naik Taraf ke Modrinth+"
|
||||
"message": "Naik taraf ke Modrinth+"
|
||||
},
|
||||
"moderation.moderate": {
|
||||
"message": "Sederhanakan"
|
||||
@@ -3155,6 +3155,12 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "bulanan"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "3 bulan"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "setiap tiga bulan"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "tahun"
|
||||
},
|
||||
@@ -3269,6 +3275,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "Ralat semasa melanggan semula"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "Jika pelayan dibatalkan pada masa ini, ia mungkin mengambil masa 10-15 minit untuk menyediakan pelayan."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "Permintaan langganan semula telah dihantar"
|
||||
},
|
||||
@@ -3327,7 +3336,7 @@
|
||||
"message": "Kaedah kemas kini"
|
||||
},
|
||||
"settings.billing.upgrade": {
|
||||
"message": "Naik Taraf"
|
||||
"message": "Naik taraf"
|
||||
},
|
||||
"settings.display.banner.developer-mode.button": {
|
||||
"message": "Nyahaktifkan mod pembangun"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"message": "Gebruiker niet gevonden"
|
||||
},
|
||||
"app-marketing.download.description": {
|
||||
"message": "Ons bureaublad-app is overal verkrijgbaar, kies de gewenste versie."
|
||||
"message": "Ons bureaublad app is overal verkrijgbaar, kies de gewenste versie."
|
||||
},
|
||||
"app-marketing.download.download-appimage": {
|
||||
"message": "Download de AppImage"
|
||||
@@ -54,7 +54,7 @@
|
||||
"message": "Volg projecten"
|
||||
},
|
||||
"app-marketing.features.importing.description": {
|
||||
"message": "Importeer al je favorieten profielen van je vorige launcher, en begin binnen enkele seconden met de Modrinth-app!"
|
||||
"message": "Importeer al je favoriete profielen vanuit het opstartprogramma dat je eerder gebruikte, en ga binnen enkele seconden aan de slag met de Modrinth app!"
|
||||
},
|
||||
"app-marketing.features.importing.gdlauncher-alt": {
|
||||
"message": "GDLauncher"
|
||||
@@ -2816,12 +2816,36 @@
|
||||
"settings.account.button.complete-setup": {
|
||||
"message": "Vervolledig setup"
|
||||
},
|
||||
"settings.account.data-export.action.download": {
|
||||
"message": "Exporteren downloaden"
|
||||
},
|
||||
"settings.account.data-export.action.generate": {
|
||||
"message": "Export genereren"
|
||||
},
|
||||
"settings.account.data-export.action.generating": {
|
||||
"message": "Export wordt gegenereerd..."
|
||||
},
|
||||
"settings.account.data-export.description": {
|
||||
"message": "Vraag een kopie aan van al uw persoonsgegevens die u naar Modrinth hebt geüpload. Dit kan enkele minuten duren."
|
||||
},
|
||||
"settings.account.data-export.title": {
|
||||
"message": "Gegevens exporteren"
|
||||
},
|
||||
"settings.account.delete.confirm.description": {
|
||||
"message": "Hierdoor worden **onmiddellijk al je gebruikersgegevens en volgers verwijderd**. Je projecten worden hierdoor niet verwijderd. Het verwijderen van je account kan niet ongedaan worden gemaakt.<br><br>Als je hulp nodig hebt met je account, kun je ondersteuning krijgen op de [Modrinth Discord](https://discord.modrinth.com)."
|
||||
},
|
||||
"settings.account.delete.confirm.proceed": {
|
||||
"message": "Je account verwijderen"
|
||||
},
|
||||
"settings.account.delete.confirm.title": {
|
||||
"message": "Weet u zeker dat u uw account wilt verwijderen?"
|
||||
},
|
||||
"settings.account.delete.section.action": {
|
||||
"message": "Verwijder account"
|
||||
},
|
||||
"settings.account.delete.section.description": {
|
||||
"message": "Zodra je je account verwijdert, kun je dit niet meer ongedaan maken. Als je je account verwijdert, worden alle bijbehorende gegevens, met uitzondering van projecten, van onze servers verwijderd."
|
||||
},
|
||||
"settings.account.delete.section.title": {
|
||||
"message": "Verwijder account"
|
||||
},
|
||||
@@ -2849,6 +2873,24 @@
|
||||
"settings.account.password.action.save": {
|
||||
"message": "Wachtwoord opslaan"
|
||||
},
|
||||
"settings.account.password.error.mismatch": {
|
||||
"message": "De ingevoerde wachtwoorden komen niet overeen!"
|
||||
},
|
||||
"settings.account.password.field.confirm-current.description": {
|
||||
"message": "Voer uw wachtwoord in om door te gaan."
|
||||
},
|
||||
"settings.account.password.field.confirm-current.label": {
|
||||
"message": "Bevestig wachtwoord"
|
||||
},
|
||||
"settings.account.password.field.confirm-current.placeholder": {
|
||||
"message": "Bevestig wachtwoord"
|
||||
},
|
||||
"settings.account.password.field.confirm-new.label": {
|
||||
"message": "Bevestig het nieuwe wachtwoord"
|
||||
},
|
||||
"settings.account.password.field.confirm-new.placeholder": {
|
||||
"message": "Bevestig het nieuwe wachtwoord"
|
||||
},
|
||||
"settings.account.password.field.new.label": {
|
||||
"message": "Nieuw wachtwoord"
|
||||
},
|
||||
@@ -2873,18 +2915,102 @@
|
||||
"settings.account.providers.action.add": {
|
||||
"message": "Toevoegen"
|
||||
},
|
||||
"settings.account.providers.modal.header": {
|
||||
"message": "Authenticatieproviders"
|
||||
},
|
||||
"settings.account.providers.table.actions": {
|
||||
"message": "Acties"
|
||||
},
|
||||
"settings.account.providers.table.provider": {
|
||||
"message": "Aanbieder"
|
||||
},
|
||||
"settings.account.security.email.action.add": {
|
||||
"message": "Voeg e-mailadres toe"
|
||||
},
|
||||
"settings.account.security.email.action.change": {
|
||||
"message": "Email veranderen"
|
||||
},
|
||||
"settings.account.security.email.description": {
|
||||
"message": "Hiermee wijzigt u het e-mailadres dat aan uw account is gekoppeld."
|
||||
},
|
||||
"settings.account.security.email.title": {
|
||||
"message": "E-mail"
|
||||
},
|
||||
"settings.account.security.password.action.add": {
|
||||
"message": "Wachtwoord toevoegen"
|
||||
},
|
||||
"settings.account.security.password.action.change": {
|
||||
"message": "Wachtwoord wijzigen"
|
||||
},
|
||||
"settings.account.security.password.description.change": {
|
||||
"message": "Wijzig het wachtwoord dat u gebruikt om in te loggen op uw account."
|
||||
},
|
||||
"settings.account.security.password.description.change-or-remove": {
|
||||
"message": "Wijzig of verwijder het wachtwoord dat u gebruikt om in te loggen op uw account."
|
||||
},
|
||||
"settings.account.security.password.description.set": {
|
||||
"message": "Stel een vast wachtwoord in om in te loggen op uw account."
|
||||
},
|
||||
"settings.account.security.password.title": {
|
||||
"message": "Wachtwoord"
|
||||
},
|
||||
"settings.account.security.providers.action.manage": {
|
||||
"message": "Leveranciers beheren"
|
||||
},
|
||||
"settings.account.security.providers.description": {
|
||||
"message": "Voeg aanmeldingsmethoden toe aan je account of verwijder ze, zoals GitHub, GitLab, Microsoft, Discord, Steam en Google."
|
||||
},
|
||||
"settings.account.security.providers.title": {
|
||||
"message": "Authenticatie providers beheren"
|
||||
},
|
||||
"settings.account.security.title": {
|
||||
"message": "Accountbeveiliging"
|
||||
},
|
||||
"settings.account.security.two-factor.action.remove": {
|
||||
"message": "2FA uitschakelen"
|
||||
},
|
||||
"settings.account.security.two-factor.action.setup": {
|
||||
"message": "2FA instellen"
|
||||
},
|
||||
"settings.account.security.two-factor.description": {
|
||||
"message": "Voeg een extra beveiligingslaag toe aan uw account tijdens het inloggen."
|
||||
},
|
||||
"settings.account.security.two-factor.title": {
|
||||
"message": "Tweefactorauthenticatie"
|
||||
},
|
||||
"settings.account.two-factor.backup.intro": {
|
||||
"message": "Download deze reservecodes en bewaar ze op een veilige plek. Je kunt deze gebruiken in plaats van een 2FA code als je ooit de toegang tot je apparaat kwijtraakt! Je moet deze codes net zo goed beveiligen als je wachtwoord."
|
||||
},
|
||||
"settings.account.two-factor.backup.single-use": {
|
||||
"message": "Back-upcodes kunnen slechts één keer worden gebruikt."
|
||||
},
|
||||
"settings.account.two-factor.error.incorrect-code": {
|
||||
"message": "De ingevoerde code is onjuist!"
|
||||
},
|
||||
"settings.account.two-factor.field.code.description": {
|
||||
"message": "Voer een twee factor authenticatie code in om door te gaan."
|
||||
},
|
||||
"settings.account.two-factor.field.code.label": {
|
||||
"message": "Voer de twee factor authenticatie code in"
|
||||
},
|
||||
"settings.account.two-factor.field.code.placeholder": {
|
||||
"message": "Voer de code in..."
|
||||
},
|
||||
"settings.account.two-factor.setup.intro": {
|
||||
"message": "2FA authenticatie zorgt ervoor dat je account veilig blijft, omdat je een tweede apparaat nodig hebt om in te loggen."
|
||||
},
|
||||
"settings.account.two-factor.setup.manual-secret": {
|
||||
"message": "Als de QR-code niet kan worden gescand, kun je de code handmatig invoeren:"
|
||||
},
|
||||
"settings.account.two-factor.setup.scan": {
|
||||
"message": "Scan de QR-code met <authy-link>Authy</authy-link>, <microsoft-authenticator-link>Microsoft Authenticator</microsoft-authenticator-link> of een andere 2FA app om te beginnen."
|
||||
},
|
||||
"settings.account.two-factor.verify.description": {
|
||||
"message": "Voer de eenmalige code uit de authenticator in om de toegang te verifiëren."
|
||||
},
|
||||
"settings.account.two-factor.verify.label": {
|
||||
"message": "Code verifiëren"
|
||||
},
|
||||
"settings.applications.about": {
|
||||
"message": "Over"
|
||||
},
|
||||
@@ -2966,6 +3092,9 @@
|
||||
"settings.applications.field.url.placeholder": {
|
||||
"message": "https://example.com"
|
||||
},
|
||||
"settings.applications.head-title": {
|
||||
"message": "Toepassingen"
|
||||
},
|
||||
"settings.applications.modal.header": {
|
||||
"message": "Project informatie"
|
||||
},
|
||||
@@ -2978,6 +3107,99 @@
|
||||
"settings.applications.secret.disclaimer": {
|
||||
"message": "Bewaar je geheim nu, het blijft verborgen nadat je deze pagina verlaat!"
|
||||
},
|
||||
"settings.authorizations.about-this-app": {
|
||||
"message": "Over deze app"
|
||||
},
|
||||
"settings.authorizations.by": {
|
||||
"message": "door"
|
||||
},
|
||||
"settings.authorizations.description": {
|
||||
"message": "Wanneer u een app machtigt via uw Modrinth account, verleent u deze apptoegang tot uw account. U kunt de toegang tot uw account hier op elk moment beheren en bekijken."
|
||||
},
|
||||
"settings.authorizations.empty-state": {
|
||||
"message": "We kunnen je geautoriseerde apps momenteel niet weergeven, maar we werken eraan om dit te verhelpen. Kom later nog eens terug op deze pagina!"
|
||||
},
|
||||
"settings.authorizations.head-title": {
|
||||
"message": "Toestemmingen"
|
||||
},
|
||||
"settings.authorizations.revoke.action": {
|
||||
"message": "Intrekken"
|
||||
},
|
||||
"settings.authorizations.revoke.confirm.description": {
|
||||
"message": "Hierdoor wordt de toegang van de app tot je account ingetrokken. Je kunt de app later altijd weer autoriseren."
|
||||
},
|
||||
"settings.authorizations.revoke.confirm.title": {
|
||||
"message": "Weet u zeker dat u deze aanvraag wilt intrekken?"
|
||||
},
|
||||
"settings.billing.charges.description": {
|
||||
"message": "Hier vindt u een overzicht van alle eerdere afschrijvingen op uw Modrinth rekening:"
|
||||
},
|
||||
"settings.billing.charges.product.medal-trial": {
|
||||
"message": "Proefversie van Medal Server"
|
||||
},
|
||||
"settings.billing.charges.product.midas": {
|
||||
"message": "Modrinth Plus"
|
||||
},
|
||||
"settings.billing.charges.product.pyro": {
|
||||
"message": "Modrinth Hosting"
|
||||
},
|
||||
"settings.billing.charges.title": {
|
||||
"message": "Eerdere aanklachten"
|
||||
},
|
||||
"settings.billing.expires": {
|
||||
"message": "Vervalt op {datum}"
|
||||
},
|
||||
"settings.billing.interval.month": {
|
||||
"message": "maand"
|
||||
},
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "maandelijks"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "kwartaal"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "Per kwartaal"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "jaar"
|
||||
},
|
||||
"settings.billing.interval.yearly": {
|
||||
"message": "jaarlijks"
|
||||
},
|
||||
"settings.billing.midas.benefits.ad-free": {
|
||||
"message": "Reclamevrij surfen op modrinth.com en in de Modrinth app"
|
||||
},
|
||||
"settings.billing.midas.benefits.badge": {
|
||||
"message": "Modrinth+ badge op je profiel"
|
||||
},
|
||||
"settings.billing.midas.benefits.support": {
|
||||
"message": "Steun Modrinth en de makers rechtstreeks"
|
||||
},
|
||||
"settings.billing.midas.benefits.title": {
|
||||
"message": "Voordelen"
|
||||
},
|
||||
"settings.billing.midas.save-per-year": {
|
||||
"message": "Bespaar {bedrag} per jaar door over te stappen op jaarfacturering!"
|
||||
},
|
||||
"settings.billing.midas.status.cancelled.line1": {
|
||||
"message": "Je hebt je abonnement opgezegd."
|
||||
},
|
||||
"settings.billing.midas.status.cancelled.line2": {
|
||||
"message": "U behoudt uw voordelen tot het einde van de huidige factureringsperiode."
|
||||
},
|
||||
"settings.billing.midas.status.failed": {
|
||||
"message": "De betaling van je abonnement is mislukt. Werk je betaalmethode bij."
|
||||
},
|
||||
"settings.billing.midas.status.open": {
|
||||
"message": "Je bent momenteel geabonneerd op:"
|
||||
},
|
||||
"settings.billing.midas.status.processing": {
|
||||
"message": "Je betaling wordt verwerkt. De extraatjes worden geactiveerd zodra de betaling is voltooid."
|
||||
},
|
||||
"settings.billing.midas.upsell": {
|
||||
"message": "Word abonnee van Modrinth Plus!"
|
||||
},
|
||||
"settings.billing.modal.cancel.action": {
|
||||
"message": "Annuleer abbonement"
|
||||
},
|
||||
@@ -2996,6 +3218,12 @@
|
||||
"settings.billing.modal.delete.title": {
|
||||
"message": "Weet je zeker dat je deze betalings-methode wil verwijderen?"
|
||||
},
|
||||
"settings.billing.next": {
|
||||
"message": "Volgende:"
|
||||
},
|
||||
"settings.billing.or-yearly-save": {
|
||||
"message": "Of {prijs} per jaar (bespaar {percent}%)!"
|
||||
},
|
||||
"settings.billing.payment_method.action.add": {
|
||||
"message": "Voeg betalings-methode toe"
|
||||
},
|
||||
@@ -3017,18 +3245,99 @@
|
||||
"settings.billing.payment_method.title": {
|
||||
"message": "Betaalmethodes"
|
||||
},
|
||||
"settings.billing.plan.title": {
|
||||
"message": "{size} Plan"
|
||||
},
|
||||
"settings.billing.price.per-interval": {
|
||||
"message": "{price} / {interval}"
|
||||
},
|
||||
"settings.billing.price.slash-interval": {
|
||||
"message": "/{interval}"
|
||||
},
|
||||
"settings.billing.pyro.cpu": {
|
||||
"message": "{shared} Gedeelde CPU's (pieken tot {bursts} CPU's)"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.not-found": {
|
||||
"message": "Er kon geen gekoppelde server worden gevonden voor dit abonnement. Hiervoor zijn een aantal mogelijke verklaringen. Als u uw server net hebt aangeschaft, is dit normaal. Het kan tot een uur duren voordat uw server is ingesteld. Als u deze server daarentegen al enige tijd geleden hebt aangeschaft, is deze waarschijnlijk inmiddels opgeschort. Als dit niet is wat u had verwacht, neem dan contact op met de Modrinth-helpdesk met de volgende informatie:"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.server-id": {
|
||||
"message": "Server ID: {id}"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.stripe-id": {
|
||||
"message": "Stripe ID: {id}"
|
||||
},
|
||||
"settings.billing.pyro.ram": {
|
||||
"message": "{gb} GB RAM"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.error.text": {
|
||||
"message": "Er is een fout opgetreden bij het opnieuw abonneren op uw Modrinth server."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "Fout bij het opnieuw abonneren"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "Als de server momenteel is geannuleerd, kan het 10 tot 15 minuten duren om de server in te stellen."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "Verzoek tot verlenging van het abonnement ingediend"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.success.text": {
|
||||
"message": "Het serverabonnement is opnieuw geactiveerd"
|
||||
},
|
||||
"settings.billing.pyro.status.failed": {
|
||||
"message": "De betaling voor je abonnement is mislukt. Werk je betaalmethode bij en meld je vervolgens opnieuw aan."
|
||||
},
|
||||
"settings.billing.pyro.status.processing": {
|
||||
"message": "Uw betaling wordt verwerkt. Uw server wordt geactiveerd zodra de betaling is voltooid."
|
||||
},
|
||||
"settings.billing.pyro.storage": {
|
||||
"message": "{gb} GB SSD"
|
||||
},
|
||||
"settings.billing.pyro.swap": {
|
||||
"message": "{gb} GB Swap"
|
||||
},
|
||||
"settings.billing.pyro_subscription.description": {
|
||||
"message": "Beheer jouw Modrinth Server abonnement."
|
||||
},
|
||||
"settings.billing.pyro_subscription.title": {
|
||||
"message": "Modrinth Server Abonnementen"
|
||||
},
|
||||
"settings.billing.renews": {
|
||||
"message": "Vernieuwt {datum}"
|
||||
},
|
||||
"settings.billing.resubscribe": {
|
||||
"message": "Opnieuw abonneren"
|
||||
},
|
||||
"settings.billing.since": {
|
||||
"message": "Sinds {datum}"
|
||||
},
|
||||
"settings.billing.subscribe": {
|
||||
"message": "Abonneren"
|
||||
},
|
||||
"settings.billing.subscription.description": {
|
||||
"message": ".."
|
||||
},
|
||||
"settings.billing.subscription.title": {
|
||||
"message": "Abonnementen"
|
||||
},
|
||||
"settings.billing.switch.switching-to-interval": {
|
||||
"message": "Overstappen naar {interval}"
|
||||
},
|
||||
"settings.billing.switch.to-interval": {
|
||||
"message": "Schakel over naar {interval}"
|
||||
},
|
||||
"settings.billing.switch.tooltip.monthly-additional-per-year": {
|
||||
"message": "Bij maandelijkse facturering betaalt u {bedrag} extra per jaar"
|
||||
},
|
||||
"settings.billing.switches-to-billing-on": {
|
||||
"message": "Gaat op {datum} over op facturering per {interval}"
|
||||
},
|
||||
"settings.billing.update-method": {
|
||||
"message": "Update methode"
|
||||
},
|
||||
"settings.billing.upgrade": {
|
||||
"message": "Upgraden"
|
||||
},
|
||||
"settings.display.banner.developer-mode.button": {
|
||||
"message": "Ontwikkelaarsmodus deactiveren"
|
||||
},
|
||||
@@ -3041,6 +3350,12 @@
|
||||
"settings.display.flags.title": {
|
||||
"message": "Functies in en uitschakelen"
|
||||
},
|
||||
"settings.display.notification.developer-mode-deactivated.text": {
|
||||
"message": "De ontwikkelaarsmodus is uitgeschakeld"
|
||||
},
|
||||
"settings.display.notification.developer-mode-deactivated.title": {
|
||||
"message": "Ontwikkelaarsmodus uitgeschakeld"
|
||||
},
|
||||
"settings.display.project-list-layouts.datapack": {
|
||||
"message": "Data Packs pagina"
|
||||
},
|
||||
@@ -3050,6 +3365,15 @@
|
||||
"settings.display.project-list-layouts.mod": {
|
||||
"message": "Mods pagina"
|
||||
},
|
||||
"settings.display.project-list-layouts.mode.gallery": {
|
||||
"message": "Gallerij"
|
||||
},
|
||||
"settings.display.project-list-layouts.mode.grid": {
|
||||
"message": "Raster"
|
||||
},
|
||||
"settings.display.project-list-layouts.mode.rows": {
|
||||
"message": "Rijen"
|
||||
},
|
||||
"settings.display.project-list-layouts.modpack": {
|
||||
"message": "Modpacks pagina"
|
||||
},
|
||||
@@ -3110,6 +3434,9 @@
|
||||
"settings.display.theme.title": {
|
||||
"message": "Kleurschema"
|
||||
},
|
||||
"settings.head-title": {
|
||||
"message": "Weergave instellingen"
|
||||
},
|
||||
"settings.pats.action.create": {
|
||||
"message": "Creëer een PAT"
|
||||
},
|
||||
@@ -3170,6 +3497,9 @@
|
||||
"settings.profile.description": {
|
||||
"message": "Je profielgegevens zijn openbaar op Modrinth en zichtbaar via de <docs-link>Modrinth API</docs-link>."
|
||||
},
|
||||
"settings.profile.head-title": {
|
||||
"message": "Profielinstellingen"
|
||||
},
|
||||
"settings.profile.profile-info": {
|
||||
"message": "Profielgegevens"
|
||||
},
|
||||
@@ -3200,12 +3530,30 @@
|
||||
"settings.sessions.unknown-platform": {
|
||||
"message": "Onbekend platform"
|
||||
},
|
||||
"settings.sidebar.label.account": {
|
||||
"message": "Account"
|
||||
},
|
||||
"settings.sidebar.label.developer": {
|
||||
"message": "Ontwikkelaar"
|
||||
},
|
||||
"settings.sidebar.label.display": {
|
||||
"message": "Weergave "
|
||||
},
|
||||
"ui.latest-news-row.latest-news": {
|
||||
"message": "Laatste nieuws van Modrinth"
|
||||
},
|
||||
"ui.latest-news-row.view-all": {
|
||||
"message": "Zie all het nieuws"
|
||||
},
|
||||
"ui.newsletter-button.subscribe": {
|
||||
"message": "Abonneren"
|
||||
},
|
||||
"ui.newsletter-button.subscribed": {
|
||||
"message": "Aangemeld!"
|
||||
},
|
||||
"ui.newsletter-button.tooltip": {
|
||||
"message": "Abonneer je op de nieuwsbrief van Modrinth"
|
||||
},
|
||||
"version.environment.none.description": {
|
||||
"message": "De omgeving voor deze versie is niet gespecificeerd."
|
||||
},
|
||||
|
||||
@@ -3155,6 +3155,9 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "miesięcznie"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "kwartał"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "rok"
|
||||
},
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
"message": "Делитесь сборками"
|
||||
},
|
||||
"app-marketing.features.unlike-any-launcher": {
|
||||
"message": "Непохоже ни на один лаунчер,"
|
||||
"message": "Лаунчер, непохожий"
|
||||
},
|
||||
"app-marketing.features.website.description": {
|
||||
"message": "Приложение полностью интегрировано с сайтом — открывайте любимые проекты прямо в Modrinth App!"
|
||||
@@ -174,7 +174,7 @@
|
||||
"message": "Интеграция с сайтом"
|
||||
},
|
||||
"app-marketing.features.youve-used-before": {
|
||||
"message": "что вы использовали раньше"
|
||||
"message": "на все остальные"
|
||||
},
|
||||
"app-marketing.hero.app-screenshot-alt": {
|
||||
"message": "Скриншот Modrinth App со страницей содержимого сборки Cobblemon."
|
||||
@@ -192,7 +192,7 @@
|
||||
"message": "Скачать Modrinth App для {os}"
|
||||
},
|
||||
"app-marketing.hero.minecraft-screenshot-alt": {
|
||||
"message": "Скриншот сборки Cobblemon на главном меню."
|
||||
"message": "Скриншот главного меню сборки Cobblemon."
|
||||
},
|
||||
"app-marketing.hero.more-download-options": {
|
||||
"message": "Больше вариантов установки"
|
||||
@@ -507,7 +507,7 @@
|
||||
"message": "Введите название проекта..."
|
||||
},
|
||||
"create.project.owner-description": {
|
||||
"message": "Укажите себя или организацию, в которой состоите."
|
||||
"message": "Укажите в качестве владельца проекта себя или организацию, членом которой вы являетесь."
|
||||
},
|
||||
"create.project.owner-label": {
|
||||
"message": "Владелец"
|
||||
@@ -576,7 +576,7 @@
|
||||
"message": "Создать новую"
|
||||
},
|
||||
"dashboard.collections.empty.get-started-hint": {
|
||||
"message": "Создайте свою первую коллекцию!"
|
||||
"message": "Создайте свою первую коллекцию, чтобы начать работу!"
|
||||
},
|
||||
"dashboard.collections.empty.no-collections": {
|
||||
"message": "Коллекций пока нет"
|
||||
@@ -657,7 +657,7 @@
|
||||
"message": "Чистая сумма"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.fee-breakdown-usd-equivalent": {
|
||||
"message": "Долларов США"
|
||||
"message": "Эквивалент в долларах США"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.kyc.business-entity": {
|
||||
"message": "Юридическое лицо"
|
||||
@@ -1119,10 +1119,10 @@
|
||||
"message": "На год"
|
||||
},
|
||||
"hosting-marketing.faq.burst-threads": {
|
||||
"message": "Как работают с высокой нагрузкой на CPU?"
|
||||
"message": "Как работает рост потоков CPU?"
|
||||
},
|
||||
"hosting-marketing.faq.burst-threads.answer": {
|
||||
"message": "Когда ваш сервер испытывает высокую нагрузку, мы временно предоставляем ему доступ к дополнительным потокам CPU, чтобы уменьшить лаги и нестабильность. Это помогает предотвратить падение TPS ниже 20 и обеспечить максимально плавную работу. Поскольку эти дополнительные потоки доступны только на короткое время во время периодов высокой нагрузки, они могут не отображаться в отчётах Spark или других инструментах профилирования."
|
||||
"message": "Когда сервер испытывает высокую нагрузку, ему временно выделяются дополнительные потоки CPU. Это помогает показателю TPS не упасть ниже 20 и обеспечивает максимально плавный игровой процесс. Из‑за своей кратковременности такие потоки могут не отображаться в отчётах Spark и других инструментах профилирования."
|
||||
},
|
||||
"hosting-marketing.faq.cpu-kind": {
|
||||
"message": "Какие процессоры используются на серверах Modrinth Hosting?"
|
||||
@@ -1155,10 +1155,10 @@
|
||||
"message": "Чаще всего проблемы с производительностью вызваны плохой оптимизацией сборок, модов, плагинов и наборов данных. Наши серверы рассчитаны на высокие нагрузки, поэтому при выборе подходящего плана серьёзных проблем возникнуть не должно."
|
||||
},
|
||||
"hosting-marketing.faq.increase-storage": {
|
||||
"message": "Можно ли увеличить объём хранилища на сервере?"
|
||||
"message": "Можно ли увеличить объём диска на сервере?"
|
||||
},
|
||||
"hosting-marketing.faq.increase-storage.answer": {
|
||||
"message": "Да. Объём хранилища на сервере можно бесплатно увеличить: достаточно обратиться в поддержку Modrinth."
|
||||
"message": "Да. Объём диска на сервере можно бесплатно увеличить: достаточно обратиться в поддержку Modrinth."
|
||||
},
|
||||
"hosting-marketing.faq.location": {
|
||||
"message": "Где расположены серверы Modrinth Hosting? Можно ли выбрать регион?"
|
||||
@@ -1227,7 +1227,7 @@
|
||||
"message": "Поддержка в нужный момент"
|
||||
},
|
||||
"hosting-marketing.included.help.description": {
|
||||
"message": "Обращайтесь в команду Modrinth за помощью по вопросам, связанным с вашим сервером, в любое время."
|
||||
"message": "Команда Modrinth готова помочь с сервером в любое время."
|
||||
},
|
||||
"hosting-marketing.included.powerful-console": {
|
||||
"message": "Мощная консоль, удобная настройка и многое другое"
|
||||
@@ -1266,7 +1266,7 @@
|
||||
"message": "Управляйте всем на Modrinth"
|
||||
},
|
||||
"hosting-marketing.why.all-on-modrinth.description": {
|
||||
"message": "Сервера, моды, игроки — всё уже есть на Modrinth. Не нужно переключаться между платформами."
|
||||
"message": "Сервер, моды, игроки — всё уже есть на Modrinth. Не нужно переключаться между платформами."
|
||||
},
|
||||
"hosting-marketing.why.consistently-fast": {
|
||||
"message": "Всегда высокая скорость"
|
||||
@@ -1275,7 +1275,7 @@
|
||||
"message": "Из‑за отсутствия перегрузок в инфраструктуре каждый сервер работает на полной мощности."
|
||||
},
|
||||
"hosting-marketing.why.description": {
|
||||
"message": "Выберите любую из тысяч сборок на Modrinth или создайте свою, пригласите друзей и начинайте игру."
|
||||
"message": "Выберите любую из тысяч сборок на Modrinth или создайте свою. Пригласите друзей и начинайте игру."
|
||||
},
|
||||
"hosting-marketing.why.heading": {
|
||||
"message": "Найдите сборку. Сервер готов."
|
||||
@@ -1290,13 +1290,13 @@
|
||||
"message": "Играйте сразу с модами"
|
||||
},
|
||||
"hosting-marketing.why.where-mods-are.description": {
|
||||
"message": "Установка модов и сборок встроена в Modrinth Hosting и работает прямо на сервере."
|
||||
"message": "Установка серверных модов и сборок встроена в Modrinth Hosting."
|
||||
},
|
||||
"hosting-marketing.why.why-modrinth-hosting": {
|
||||
"message": "Почему именно Modrinth Hosting?"
|
||||
"message": "Преимущества Modrinth Hosting"
|
||||
},
|
||||
"hosting-marketing.why.your-favorite-mods": {
|
||||
"message": "Все ваши любимые моды"
|
||||
"message": "Любые моды на ваш вкус"
|
||||
},
|
||||
"hosting-marketing.why.your-favorite-mods.description": {
|
||||
"message": "Выбирайте Fabric, Forge, Quilt, NeoForge или сервер без модов. Всё, что есть на Modrinth, работает на сервере."
|
||||
@@ -1314,7 +1314,7 @@
|
||||
"message": "Перейти в панель управления"
|
||||
},
|
||||
"landing.creator.feature.constantly-evolving.description": {
|
||||
"message": "Получайте лучший опыт моддинга с постоянными обновлениями от команды Modrinth"
|
||||
"message": "Получайте лучший опыт в создании модов с постоянными обновлениями от команды Modrinth"
|
||||
},
|
||||
"landing.creator.feature.constantly-evolving.title": {
|
||||
"message": "Постоянное развитие"
|
||||
@@ -2805,13 +2805,13 @@
|
||||
"message": "Нет оповещений"
|
||||
},
|
||||
"servers.plan.large.description": {
|
||||
"message": "Подходит для 15–25 игроков, сборок, либо требовательных модов."
|
||||
"message": "Для 15–25 игроков, сборок и тяжёлых модов."
|
||||
},
|
||||
"servers.plan.medium.description": {
|
||||
"message": "Подходит для 6–15 игроков и нескольких модов."
|
||||
"message": "Для 6–15 игроков и нескольких модов."
|
||||
},
|
||||
"servers.plan.small.description": {
|
||||
"message": "Подходит для 1–5 игроков и нескольких лёгких модов."
|
||||
"message": "Для 1–5 друзей и пары лёгких модов."
|
||||
},
|
||||
"settings.account.button.complete-setup": {
|
||||
"message": "Завершить настройку"
|
||||
@@ -3255,7 +3255,7 @@
|
||||
"message": "/{interval}"
|
||||
},
|
||||
"settings.billing.pyro.cpu": {
|
||||
"message": "{shared, plural, one {# разделяемый} other {# разделяемых}} CPU (временный рост до {bursts} CPU)"
|
||||
"message": "{shared, plural, one {# разделяемый} other {# разделяемых}} CPU (рост до {bursts} CPU)"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.not-found": {
|
||||
"message": "Привязанный сервер не найден. Возможных причин несколько: сервер куплен только что — подождите, подготовка занимает до часа; сервер куплен давно — вероятно, его уже приостановили. Если что‑то не так, свяжитесь с поддержкой Modrinth, указав следующие данные:"
|
||||
@@ -3275,6 +3275,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "Не удалось возобновить подписку"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "Если сервер в данный момент отключен, настройка сервера может занять 10-15 минут."
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "Подписка возобновляется"
|
||||
},
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
"message": "Modrinth App är helt integrerad med webbsidan, så att du kan använda alla dina favoritprojekt i appen!"
|
||||
},
|
||||
"app-marketing.features.website.title": {
|
||||
"message": "Webbsidaintegrering"
|
||||
"message": "Webbsidointegrering"
|
||||
},
|
||||
"app-marketing.features.youve-used-before": {
|
||||
"message": "du använt förut"
|
||||
@@ -785,6 +785,12 @@
|
||||
"dashboard.creator-withdraw-modal.stage.tax-form": {
|
||||
"message": "Skatteformulär"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.tax-form-required.body": {
|
||||
"message": "För att ta ut hela ditt <b>{available}</b> tillgängliga saldo, vänligen fyll i formuläret nedan. Detta krävs för skattedeklarationen och behöver endast göras en gång."
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.tax-form-required.body-with-limit": {
|
||||
"message": "Du måste fylla i en W-9- eller W-8-blankett för Modrinths skatteregister så att vi kan följa skattereglerna."
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.tax-form-required.header": {
|
||||
"message": "Skatteformulär krävs"
|
||||
},
|
||||
@@ -911,6 +917,9 @@
|
||||
"dashboard.withdraw.error.account-not-linked.title": {
|
||||
"message": "Konto inte länkat"
|
||||
},
|
||||
"dashboard.withdraw.error.email-verification.title": {
|
||||
"message": "E-post verifiering krävs"
|
||||
},
|
||||
"dashboard.withdraw.error.insufficient-balance.title": {
|
||||
"message": "Otillräcklig saldo"
|
||||
},
|
||||
@@ -1055,6 +1064,9 @@
|
||||
"hosting-marketing.faq.burst-threads": {
|
||||
"message": "Hur fungerar CPU burst-trådar?"
|
||||
},
|
||||
"hosting-marketing.faq.burst-threads.answer": {
|
||||
"message": "När din server har hög belastning ger vi den tillfälligt tillgång till ytterligare CPU-trådar för att minska plötsliga fördröjningar och instabilitet. Detta bidrar till att förhindra att TPS sjunker under 20, vilket garanterar en så smidig upplevelse som möjligt. Eftersom dessa extra CPU-trådar endast är tillgängliga under korta perioder vid hög belastning, kanske de inte syns i Spark-rapporter eller andra profilverktyg."
|
||||
},
|
||||
"hosting-marketing.faq.cpu-kind": {
|
||||
"message": "Vilka typer av CPU:er kör Modrinth Hostings servrar på?"
|
||||
},
|
||||
@@ -1124,6 +1136,9 @@
|
||||
"hosting-marketing.hero.hosting-description": {
|
||||
"message": "Modrinth Hosting är det lättaste sättet att hosta din egna Minecraft: Java Edition server. Installera och spela dina favoritmoddar och modpaket smidigt, allt inom Modrinth-plattformen."
|
||||
},
|
||||
"hosting-marketing.included.advanced-networking": {
|
||||
"message": "Avancerad nätverkshantering"
|
||||
},
|
||||
"hosting-marketing.included.advanced-networking.description": {
|
||||
"message": "Lägg till din egen domän till din server, reservera upp till 15 portar för moddar som kräver dem och mycket mer."
|
||||
},
|
||||
@@ -1139,6 +1154,9 @@
|
||||
"hosting-marketing.included.custom-url.description": {
|
||||
"message": "Dela din server med en anpassad <contrast>modrinth.gg</contrast> URL."
|
||||
},
|
||||
"hosting-marketing.included.description": {
|
||||
"message": "Varje server inkluderar en rad funktioner som är designade för att ge en hostingupplevelse som endast Modrinth kan erbjuda."
|
||||
},
|
||||
"hosting-marketing.included.file-manager": {
|
||||
"message": "Filhanterare som är lätt att använda"
|
||||
},
|
||||
@@ -1787,6 +1805,9 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Samling"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {nedladdning} other {nedladdningar}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Gick med"
|
||||
},
|
||||
@@ -1886,6 +1907,9 @@
|
||||
"project-type.datapack.singular": {
|
||||
"message": "Datapaket"
|
||||
},
|
||||
"project-type.minecraft_java_server.plural": {
|
||||
"message": "Sevrar"
|
||||
},
|
||||
"project-type.minecraft_java_server.singular": {
|
||||
"message": "Server"
|
||||
},
|
||||
@@ -2085,7 +2109,7 @@
|
||||
"message": "Projektikon uppdaterad"
|
||||
},
|
||||
"project.notification.updated.message": {
|
||||
"message": "Ditt projekt har blivit uppdaterat."
|
||||
"message": "Ditt projekt har uppdaterats."
|
||||
},
|
||||
"project.notification.updated.title": {
|
||||
"message": "Projekt uppdaterat"
|
||||
@@ -2444,6 +2468,120 @@
|
||||
"servers.plan.small.description": {
|
||||
"message": "Perfekt för 1–5 vänner med några få lätta moddar."
|
||||
},
|
||||
"settings.account.delete.section.action": {
|
||||
"message": "Radera konto"
|
||||
},
|
||||
"settings.account.delete.section.description": {
|
||||
"message": "Efter att du har raderat ditt konto finns det ingen väg tillbaka. Att radera ditt konto tar bort all tillhörande data, exklusive projekt från våra servrar."
|
||||
},
|
||||
"settings.account.delete.section.title": {
|
||||
"message": "Radera konto"
|
||||
},
|
||||
"settings.account.email.action.save": {
|
||||
"message": "Spara e-post"
|
||||
},
|
||||
"settings.account.email.field.label": {
|
||||
"message": "E-postadress"
|
||||
},
|
||||
"settings.account.email.field.placeholder": {
|
||||
"message": "Ange e-postadress..."
|
||||
},
|
||||
"settings.account.email.modal.header.add": {
|
||||
"message": "Lägg till e-post"
|
||||
},
|
||||
"settings.account.email.modal.header.change": {
|
||||
"message": "Ändra e-post"
|
||||
},
|
||||
"settings.account.password.action.remove": {
|
||||
"message": "Radera lösenord"
|
||||
},
|
||||
"settings.account.password.action.save": {
|
||||
"message": "Spara lösenord"
|
||||
},
|
||||
"settings.account.password.error.mismatch": {
|
||||
"message": "Lösenorden stämmer inte överens!"
|
||||
},
|
||||
"settings.account.password.field.confirm-current.description": {
|
||||
"message": "Vänligen ange ditt lösenord för att fortsätta."
|
||||
},
|
||||
"settings.account.password.field.confirm-current.label": {
|
||||
"message": "Bekräfta lösenord"
|
||||
},
|
||||
"settings.account.password.field.confirm-current.placeholder": {
|
||||
"message": "Bekräfta lösenord"
|
||||
},
|
||||
"settings.account.password.field.confirm-new.label": {
|
||||
"message": "Bekräfta nytt lösenord"
|
||||
},
|
||||
"settings.account.password.field.confirm-new.placeholder": {
|
||||
"message": "Bekräfta nytt lösenord"
|
||||
},
|
||||
"settings.account.password.field.new.label": {
|
||||
"message": "Nytt lösenord"
|
||||
},
|
||||
"settings.account.password.field.new.placeholder": {
|
||||
"message": "Nytt lösenord"
|
||||
},
|
||||
"settings.account.password.field.old.label": {
|
||||
"message": "Nuvarande lösenord"
|
||||
},
|
||||
"settings.account.password.field.old.placeholder": {
|
||||
"message": "Nuvarande lösenord"
|
||||
},
|
||||
"settings.account.password.modal.header.add": {
|
||||
"message": "Lägg till lösenord"
|
||||
},
|
||||
"settings.account.password.modal.header.change": {
|
||||
"message": "Ändra lösenord"
|
||||
},
|
||||
"settings.account.password.modal.header.remove": {
|
||||
"message": "Radera lösenord"
|
||||
},
|
||||
"settings.account.providers.action.add": {
|
||||
"message": "Lägg till"
|
||||
},
|
||||
"settings.account.providers.modal.header": {
|
||||
"message": "Autentiseringsleverantörer"
|
||||
},
|
||||
"settings.account.providers.table.actions": {
|
||||
"message": "Åtgärder"
|
||||
},
|
||||
"settings.account.providers.table.provider": {
|
||||
"message": "Leverantör"
|
||||
},
|
||||
"settings.account.security.email.action.add": {
|
||||
"message": "Lägg till e-post"
|
||||
},
|
||||
"settings.account.security.email.action.change": {
|
||||
"message": "Ändra e-post"
|
||||
},
|
||||
"settings.account.security.email.description": {
|
||||
"message": "Ändrar e-posten som associeras med ditt konto."
|
||||
},
|
||||
"settings.account.security.email.title": {
|
||||
"message": "E-post"
|
||||
},
|
||||
"settings.account.security.password.action.add": {
|
||||
"message": "Lägg till lösenord"
|
||||
},
|
||||
"settings.account.security.password.action.change": {
|
||||
"message": "Ändra lösenord"
|
||||
},
|
||||
"settings.account.security.password.description.change": {
|
||||
"message": "Ändra lösenordet du använder för att logga in på ditt konto."
|
||||
},
|
||||
"settings.account.security.password.description.change-or-remove": {
|
||||
"message": "Ändra eller radera lösenordet du använder för att logga in på ditt konto."
|
||||
},
|
||||
"settings.account.security.password.description.set": {
|
||||
"message": "Sätt ett permanent lösenord att logga in på ditt konto med."
|
||||
},
|
||||
"settings.account.security.password.title": {
|
||||
"message": "Lösenord"
|
||||
},
|
||||
"settings.account.security.providers.description": {
|
||||
"message": "Lägg till eller ta bort inloggningsmetoder från ditt konto, inklusive GitHub, GitLab, Microsoft, Discord, Steam och Google."
|
||||
},
|
||||
"settings.applications.about": {
|
||||
"message": "Om"
|
||||
},
|
||||
@@ -2480,9 +2618,18 @@
|
||||
"settings.applications.created-on": {
|
||||
"message": "Skapad {date}"
|
||||
},
|
||||
"settings.applications.delete.confirm.button": {
|
||||
"message": "Radera applikationen"
|
||||
},
|
||||
"settings.applications.field.description": {
|
||||
"message": "Beskrivning"
|
||||
},
|
||||
"settings.applications.field.icon": {
|
||||
"message": "Ikon"
|
||||
},
|
||||
"settings.applications.field.name": {
|
||||
"message": "Namn"
|
||||
},
|
||||
"settings.applications.field.name.placeholder": {
|
||||
"message": "Ange applikationens namn..."
|
||||
},
|
||||
@@ -2498,21 +2645,114 @@
|
||||
"settings.applications.field.url.placeholder": {
|
||||
"message": "https://exempel.se"
|
||||
},
|
||||
"settings.applications.head-title": {
|
||||
"message": "Applikationer"
|
||||
},
|
||||
"settings.applications.modal.header": {
|
||||
"message": "Applikations-information"
|
||||
},
|
||||
"settings.applications.notification.icon-updated.description": {
|
||||
"message": "Ditt applikations ikon har blivit uppdaterad."
|
||||
},
|
||||
"settings.applications.notification.icon-updated.title": {
|
||||
"message": "Ikon uppdaterad"
|
||||
},
|
||||
"settings.applications.secret.disclaimer": {
|
||||
"message": "Spara din hemlighet nu, det kommer att döljas när du lämnar sidan!"
|
||||
},
|
||||
"settings.authorizations.about-this-app": {
|
||||
"message": "Om appen"
|
||||
},
|
||||
"settings.authorizations.by": {
|
||||
"message": "av"
|
||||
},
|
||||
"settings.billing.charges.product.medal-trial": {
|
||||
"message": "Medal provserver"
|
||||
},
|
||||
"settings.billing.charges.product.midas": {
|
||||
"message": "Modrinth Plus"
|
||||
},
|
||||
"settings.billing.charges.product.pyro": {
|
||||
"message": "Modrinth Hosting"
|
||||
},
|
||||
"settings.billing.expires": {
|
||||
"message": "Löper ut {date}"
|
||||
},
|
||||
"settings.billing.interval.month": {
|
||||
"message": "månad"
|
||||
},
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "månatligen"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "kvartal"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "kvartalsvis"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "år"
|
||||
},
|
||||
"settings.billing.interval.yearly": {
|
||||
"message": "årligen"
|
||||
},
|
||||
"settings.billing.midas.benefits.ad-free": {
|
||||
"message": "Annonsfritt surfande på modrinth.com och Modrinth App"
|
||||
},
|
||||
"settings.billing.midas.benefits.badge": {
|
||||
"message": "Modrinth+ märke på din profil"
|
||||
},
|
||||
"settings.billing.midas.benefits.support": {
|
||||
"message": "Stötta Modrinth och kreatörer direkt"
|
||||
},
|
||||
"settings.billing.midas.benefits.title": {
|
||||
"message": "Förmåner"
|
||||
},
|
||||
"settings.billing.midas.save-per-year": {
|
||||
"message": "Spara {amount}/år genom att byta till årlig betalning!"
|
||||
},
|
||||
"settings.billing.midas.status.cancelled.line1": {
|
||||
"message": "Du har avbrutit din prenumeration."
|
||||
},
|
||||
"settings.billing.midas.status.cancelled.line2": {
|
||||
"message": "Du kommer behålla dina förmåner tills slutet av faktureringscykeln."
|
||||
},
|
||||
"settings.billing.midas.status.failed": {
|
||||
"message": "Fakturering av din prenumeration misslyckades. Vänligen uppdatera din betalmetod."
|
||||
},
|
||||
"settings.billing.midas.status.open": {
|
||||
"message": "Du prenumererar just nu på:"
|
||||
},
|
||||
"settings.billing.midas.status.processing": {
|
||||
"message": "Din betalning behandlas just nu. Förmåner kommer att aktiveras när betalningen slutförs."
|
||||
},
|
||||
"settings.billing.midas.upsell": {
|
||||
"message": "Prenumerera på Modrinth Plus!"
|
||||
},
|
||||
"settings.billing.modal.cancel.action": {
|
||||
"message": "Avbryt prenumeration"
|
||||
},
|
||||
"settings.billing.modal.cancel.description": {
|
||||
"message": "Detta kommer avbryta din prenumeration. Du kommer behålla dina förmåner tills slutet av faktureringscykeln."
|
||||
},
|
||||
"settings.billing.modal.cancel.title": {
|
||||
"message": "Är du säker på att du vill avbryta din prenumeration?"
|
||||
},
|
||||
"settings.billing.modal.delete.action": {
|
||||
"message": "Ta bort denna betalningsmetod"
|
||||
},
|
||||
"settings.billing.modal.delete.description": {
|
||||
"message": "Detta kommer radera betalningsmetoden för evigt (asså för evigt)."
|
||||
},
|
||||
"settings.billing.modal.delete.title": {
|
||||
"message": "Är du säker på att du vill radera betalningsmetoden?"
|
||||
},
|
||||
"settings.billing.next": {
|
||||
"message": "Nästa:"
|
||||
},
|
||||
"settings.billing.or-yearly-save": {
|
||||
"message": "Eller {price} / år (spara {percent}%)!"
|
||||
},
|
||||
"settings.billing.payment_method.action.add": {
|
||||
"message": "Lägg till betalningsmetod"
|
||||
},
|
||||
@@ -2525,24 +2765,96 @@
|
||||
"settings.billing.payment_method.card_expiry": {
|
||||
"message": "Går ut {month}/{year}"
|
||||
},
|
||||
"settings.billing.payment_method.none": {
|
||||
"message": "Du har inte lagt till några betalningsmetoder."
|
||||
},
|
||||
"settings.billing.payment_method.primary": {
|
||||
"message": "Primär"
|
||||
},
|
||||
"settings.billing.payment_method.title": {
|
||||
"message": "Betalningsmetoder"
|
||||
},
|
||||
"settings.billing.plan.title": {
|
||||
"message": "{size} plan"
|
||||
},
|
||||
"settings.billing.price.per-interval": {
|
||||
"message": "{price} / {interval}"
|
||||
},
|
||||
"settings.billing.price.slash-interval": {
|
||||
"message": "/{interval}"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.server-id": {
|
||||
"message": "Server ID: {id}"
|
||||
},
|
||||
"settings.billing.pyro.ram": {
|
||||
"message": "{gb} GB RAM"
|
||||
},
|
||||
"settings.billing.pyro.storage": {
|
||||
"message": "{gb} GB SSD"
|
||||
},
|
||||
"settings.billing.pyro.swap": {
|
||||
"message": "{gb} GB utbytesminne"
|
||||
},
|
||||
"settings.billing.pyro_subscription.description": {
|
||||
"message": "Hantera dina Modrinth Server-prenumerationer."
|
||||
},
|
||||
"settings.billing.pyro_subscription.title": {
|
||||
"message": "Modrinth Server-prenumerationer"
|
||||
},
|
||||
"settings.billing.renews": {
|
||||
"message": "Förnyas {date}"
|
||||
},
|
||||
"settings.billing.resubscribe": {
|
||||
"message": "Prenumerera igen"
|
||||
},
|
||||
"settings.billing.since": {
|
||||
"message": "Sen {date}"
|
||||
},
|
||||
"settings.billing.subscribe": {
|
||||
"message": "Prenumerera"
|
||||
},
|
||||
"settings.billing.subscription.description": {
|
||||
"message": "Hantera dina Modrinth-prenumerationer."
|
||||
},
|
||||
"settings.billing.subscription.title": {
|
||||
"message": "Prenumerationer"
|
||||
},
|
||||
"settings.billing.switch.switching-to-interval": {
|
||||
"message": "Ändrar till {interval}"
|
||||
},
|
||||
"settings.billing.switch.to-interval": {
|
||||
"message": "Ändra till {interval}"
|
||||
},
|
||||
"settings.billing.switch.tooltip.monthly-additional-per-year": {
|
||||
"message": "Månatlig betalning kommer kosta dig ytterligare {amount} per år"
|
||||
},
|
||||
"settings.billing.switches-to-billing-on": {
|
||||
"message": "Ändrar till {interval} betalning på {date}"
|
||||
},
|
||||
"settings.billing.update-method": {
|
||||
"message": "Uppdatera metod"
|
||||
},
|
||||
"settings.billing.upgrade": {
|
||||
"message": "Uppgradera"
|
||||
},
|
||||
"settings.display.banner.developer-mode.button": {
|
||||
"message": "Inaktivera utvecklarläge"
|
||||
},
|
||||
"settings.display.banner.developer-mode.description": {
|
||||
"message": "<strong>Utvecklarläge</strong> är aktiverat. Detta tillåter dig att visa interna ID:n för olika saker genom Modrinth vilket kan vara användbart om du är en utvecklare och använder Modrinths API. Klicka på Modrinth-loggan längst ned fem gånger för att aktivera utvecklarläge."
|
||||
},
|
||||
"settings.display.flags.description": {
|
||||
"message": "Aktivera eller inaktivera vissa funktioner på denna enhet."
|
||||
},
|
||||
"settings.display.flags.title": {
|
||||
"message": "Växla funktioner"
|
||||
},
|
||||
"settings.display.notification.developer-mode-deactivated.text": {
|
||||
"message": "Utvecklarläge har inaktiverats"
|
||||
},
|
||||
"settings.display.notification.developer-mode-deactivated.title": {
|
||||
"message": "Utvecklarläge inaktiverat"
|
||||
},
|
||||
"settings.display.project-list-layouts.datapack": {
|
||||
"message": "Sidan för datapaket"
|
||||
},
|
||||
@@ -2585,6 +2897,12 @@
|
||||
"settings.display.project-list.layouts.collection": {
|
||||
"message": "Samling"
|
||||
},
|
||||
"settings.display.sidebar.advanced-rendering.description": {
|
||||
"message": "Aktiverar avancerad rendering som t.ex. oskärpa vilket kan leda till prestandaproblem utan hårdvaruaccelerande rendering."
|
||||
},
|
||||
"settings.display.sidebar.advanced-rendering.title": {
|
||||
"message": "Avancerad rendering"
|
||||
},
|
||||
"settings.display.sidebar.external-links-new-tab.description": {
|
||||
"message": "Får länkar som leder utanför Modrinth att öppna en ny flik. Oavsett vad denna inställning har ställts in på kommer länkar med samma domän och i Markdown-beskrivningar att öppnas i samma flik, och länkar på annonser och redigeringssidor öppnas i en ny flik."
|
||||
},
|
||||
@@ -2597,21 +2915,33 @@
|
||||
"settings.display.sidebar.hide-app-promos.title": {
|
||||
"message": "Dölj \"Installera Modrinth\" knapp"
|
||||
},
|
||||
"settings.display.sidebar.left-aligned-content-sidebar.title": {
|
||||
"message": "Vänsterjusterad sidofält på innehållssidor"
|
||||
},
|
||||
"settings.display.sidebar.right-aligned-content-sidebar.description": {
|
||||
"message": "Justerar sidofältet till vänster om sidans innehåll."
|
||||
},
|
||||
"settings.display.sidebar.right-aligned-filters-sidebar.description": {
|
||||
"message": "Justerar filtrets sidofält till höger om sökresultaten."
|
||||
},
|
||||
"settings.display.sidebar.right-aligned-filters-sidebar.title": {
|
||||
"message": "Högerjusterad sidofält på innehållssidor"
|
||||
},
|
||||
"settings.display.theme.description": {
|
||||
"message": "Välj färgtema för Modrinth på denna enhet."
|
||||
},
|
||||
"settings.display.theme.title": {
|
||||
"message": "Färgtema"
|
||||
},
|
||||
"settings.head-title": {
|
||||
"message": "Visningsinställningar"
|
||||
},
|
||||
"settings.pats.action.create": {
|
||||
"message": "Skapa en PAT"
|
||||
},
|
||||
"settings.pats.description": {
|
||||
"message": "PATs kan användas för att få åtkomst till Modrinths API. De kan skapas och spärras när som helst. För mer information, se <doc-link>Modrinths API dokumentation</doc-link>."
|
||||
},
|
||||
"settings.pats.modal.create.action": {
|
||||
"message": "Skapa PAT"
|
||||
},
|
||||
@@ -2621,12 +2951,21 @@
|
||||
"settings.pats.modal.create.name.label": {
|
||||
"message": "Namn"
|
||||
},
|
||||
"settings.pats.modal.create.name.placeholder": {
|
||||
"message": "Skriv in ett namn..."
|
||||
},
|
||||
"settings.pats.modal.create.title": {
|
||||
"message": "Skapa personlig åtkomstnyckel"
|
||||
},
|
||||
"settings.pats.modal.delete.action": {
|
||||
"message": "Ta bort denna token"
|
||||
},
|
||||
"settings.pats.modal.delete.description": {
|
||||
"message": "Detta kommer radera nyckeln förevigt (asså förevigt)."
|
||||
},
|
||||
"settings.pats.modal.delete.title": {
|
||||
"message": "Är du saker på att du vill radera nyckeln?"
|
||||
},
|
||||
"settings.pats.modal.edit.title": {
|
||||
"message": "Redigera personliga åtkomstnyckeln"
|
||||
},
|
||||
@@ -2657,6 +2996,9 @@
|
||||
"settings.profile.description": {
|
||||
"message": "Din profilinformation visas offentligt på Modrinth och genom <docs-link>Modrinth API:t</docs-link>."
|
||||
},
|
||||
"settings.profile.head-title": {
|
||||
"message": "Profilinställningar"
|
||||
},
|
||||
"settings.profile.profile-info": {
|
||||
"message": "Profilinformation"
|
||||
},
|
||||
@@ -2687,16 +3029,40 @@
|
||||
"settings.sessions.unknown-platform": {
|
||||
"message": "Okänd plattform"
|
||||
},
|
||||
"settings.sidebar.label.account": {
|
||||
"message": "Konto"
|
||||
},
|
||||
"settings.sidebar.label.developer": {
|
||||
"message": "Utvecklare"
|
||||
},
|
||||
"settings.sidebar.label.display": {
|
||||
"message": "Visning"
|
||||
},
|
||||
"ui.latest-news-row.latest-news": {
|
||||
"message": "Senaste nytt från Modrinth"
|
||||
},
|
||||
"ui.latest-news-row.view-all": {
|
||||
"message": "Visa alla nyheter"
|
||||
},
|
||||
"ui.newsletter-button.subscribe": {
|
||||
"message": "Prenumerera"
|
||||
},
|
||||
"ui.newsletter-button.subscribed": {
|
||||
"message": "Prenumererar!"
|
||||
},
|
||||
"ui.newsletter-button.tooltip": {
|
||||
"message": "Prenumerera på Modrinths nyhetsbrev"
|
||||
},
|
||||
"version.environment.none.description": {
|
||||
"message": "Miljön för denna version har inte specificerats."
|
||||
},
|
||||
"version.environment.none.title": {
|
||||
"message": "Ingen miljö angiven"
|
||||
},
|
||||
"version.environment.unknown.description": {
|
||||
"message": "Miljön: \"{environment}\" känns inte igen."
|
||||
},
|
||||
"version.environment.unknown.title": {
|
||||
"message": "Okänd miljö"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"message": "Linux"
|
||||
},
|
||||
"app-marketing.download.linux-disclaimer": {
|
||||
"message": ""
|
||||
"message": "เวอร์ชั่น Linux แอพ Modrinth <issues-link> อาจมีปัญหา</issues-link> ในบางเครื่องและการตั้งค่าบางอย่าง ถ้าแอพ Modrinth ไม่เสถียรในเครื่องของคุณ เราขอให้คุณลองใช้แอพอื่นๆเช่น <prism-link>Prism Launcher</prism-link> เพื่อติดตั้งเนื้อหาของ Modrinth"
|
||||
},
|
||||
"app-marketing.download.mac": {
|
||||
"message": "Mac"
|
||||
@@ -242,6 +242,9 @@
|
||||
"auth.sign-in.title": {
|
||||
"message": "ลงชื่อเข้าใช้"
|
||||
},
|
||||
"auth.sign-up.action.create-account": {
|
||||
"message": "สร้างบัญชี"
|
||||
},
|
||||
"auth.sign-up.legal-dislaimer": {
|
||||
"message": "ในการสร้างบัญชี คุณยอมรับ <terms-link> ข้อกำหนด </terms-link> และ <privacy-policy-link> นโยบายความเป็นส่วนตัว </privacy-policy-link> ของ Modrinth"
|
||||
},
|
||||
@@ -278,6 +281,9 @@
|
||||
"auth.verify-email.failed-verification.description": {
|
||||
"message": "เราไม่สามารถตรวจสอบอีเมลของคุณได้ ลองส่งอีเมลยืนยันอีกครั้งผ่านแดชบอร์ดของคุณโดยลงชื่อเข้าใช้"
|
||||
},
|
||||
"auth.verify-email.failed-verification.description.logged-in": {
|
||||
"message": "เราไม่สามารถยืนยันอีเมลของคุณได้ โปรดลองส่งอีเมลยืนยันอีกครั้งโดยใช้ปุ่มด้านล่าง"
|
||||
},
|
||||
"auth.verify-email.failed-verification.title": {
|
||||
"message": "การยืนยันอีเมลล้มเหลว"
|
||||
},
|
||||
@@ -386,6 +392,81 @@
|
||||
"create.organization.name-label": {
|
||||
"message": ""
|
||||
},
|
||||
"create.organization.ownership-info": {
|
||||
"message": "คุณจะเป็นเจ้าของในการจัดการต่างๆ และคุณก็สามารถเพิ่มสมาชิกได้ และ ย้ายเจ้าของตอนไหนก็ได้"
|
||||
},
|
||||
"create.organization.summary-label": {
|
||||
"message": "สรุป"
|
||||
},
|
||||
"create.organization.url-label": {
|
||||
"message": "URL"
|
||||
},
|
||||
"create.project.create-project": {
|
||||
"message": "สร้างโปรเจ็ก"
|
||||
},
|
||||
"create.project.name-label": {
|
||||
"message": "ชื่อ"
|
||||
},
|
||||
"create.project.name-placeholder": {
|
||||
"message": "ใส่ชื่อโปรเจ็ก"
|
||||
},
|
||||
"create.project.owner-label": {
|
||||
"message": "เจ้าของ"
|
||||
},
|
||||
"create.project.server-project-title": {
|
||||
"message": "สร้าง เซิร์ฟเวอร์โปรเจ็ก"
|
||||
},
|
||||
"create.project.summary-label": {
|
||||
"message": "สรุป"
|
||||
},
|
||||
"create.project.title": {
|
||||
"message": "สร้างโปรเจ็ก"
|
||||
},
|
||||
"create.project.type-label": {
|
||||
"message": "ประเภท"
|
||||
},
|
||||
"create.project.type-project": {
|
||||
"message": "โปรเจ็ก"
|
||||
},
|
||||
"create.project.type-server": {
|
||||
"message": "เซิร์ฟเวอร์"
|
||||
},
|
||||
"create.project.url-label": {
|
||||
"message": "URL"
|
||||
},
|
||||
"create.project.visibility-private": {
|
||||
"message": "ส่วนตัว"
|
||||
},
|
||||
"create.project.visibility-public": {
|
||||
"message": "สาธารณะ"
|
||||
},
|
||||
"dashboard.collections.button.create-new": {
|
||||
"message": "สร้างใหม่"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.download-button": {
|
||||
"message": "ดาวโหลด {formType}"
|
||||
},
|
||||
"dashboard.creator-tax-form-modal.confirmation.title": {
|
||||
"message": "คุณพร้อมแล้ว! 🎉"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.details-label": {
|
||||
"message": "รายละเอียด"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.fee-breakdown-amount": {
|
||||
"message": "ปริมาณ"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.method-selection.country-placeholder": {
|
||||
"message": "เลือกประเทศของคุณ"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.method-selection.country-search-placeholder": {
|
||||
"message": "ค้นหาประเทศ"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.method-selection.region": {
|
||||
"message": "ภูมิถาค"
|
||||
},
|
||||
"dashboard.creator-withdraw-modal.paypal-details.account": {
|
||||
"message": "บัญชี"
|
||||
},
|
||||
"error.generic.default.subtitle": {
|
||||
"message": ""
|
||||
},
|
||||
@@ -502,5 +583,11 @@
|
||||
},
|
||||
"landing.notifications.received-time": {
|
||||
"message": ""
|
||||
},
|
||||
"settings.applications.button.upload-icon": {
|
||||
"message": "อัปโหลดไอคอน"
|
||||
},
|
||||
"settings.applications.field.icon": {
|
||||
"message": "ไอคอน"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1433,6 +1433,9 @@
|
||||
"landing.section.for-players.label": {
|
||||
"message": "Oyuncular İçin"
|
||||
},
|
||||
"landing.section.for-players.tagline": {
|
||||
"message": "{count}'un üzerinde içerik keşfedin"
|
||||
},
|
||||
"landing.subheading": {
|
||||
"message": "Topluluk odaklı açık kaynak platformumuzda Minecraft içerikleri keşfedin, oynayın ve paylaşın."
|
||||
},
|
||||
@@ -2030,6 +2033,9 @@
|
||||
"profile.label.collection": {
|
||||
"message": "Koleksiyon"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {countPlural, plural, one {indirme} other {indirmeler}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Katılma:"
|
||||
},
|
||||
@@ -2048,6 +2054,9 @@
|
||||
"profile.label.organizations": {
|
||||
"message": "Organizasyonlar"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {countPlural, plural, one {proje} other {projeler}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Kaydediliyor..."
|
||||
},
|
||||
@@ -2804,6 +2813,27 @@
|
||||
"servers.plan.small.description": {
|
||||
"message": "1–5 arkadaş ve birkaç hafif mod için mükemmel."
|
||||
},
|
||||
"settings.account.button.complete-setup": {
|
||||
"message": "Kurulumu tamamla"
|
||||
},
|
||||
"settings.account.data-export.action.download": {
|
||||
"message": "Dışa aktarmayı indir"
|
||||
},
|
||||
"settings.account.data-export.action.generate": {
|
||||
"message": "Dışa akatma oluştur"
|
||||
},
|
||||
"settings.account.data-export.action.generating": {
|
||||
"message": "Dışa aktarma oluşturuluyor..."
|
||||
},
|
||||
"settings.account.data-export.description": {
|
||||
"message": "Modrinth'e yüklediğiniz tüm kişisel verilerinizin bir kopyasını talep edin. Bu işlemin tamamlanması birkaç dakika sürebilir."
|
||||
},
|
||||
"settings.account.data-export.title": {
|
||||
"message": "Veri dışa aktarma"
|
||||
},
|
||||
"settings.account.delete.confirm.description": {
|
||||
"message": "Bu işlem, **tüm kullanıcı verilerinizi ve takip ettiğiniz hesapları anında siler**. Projeleriniz silinmez. Hesabınızı silme işlemi geri alınamaz.<br><br>Hesabınızla ilgili yardıma ihtiyacınız varsa, [Modrinth Discord](https://discord.modrinth.com) üzerinden destek alabilirsiniz."
|
||||
},
|
||||
"settings.account.delete.confirm.proceed": {
|
||||
"message": "Hesabı sil"
|
||||
},
|
||||
@@ -2813,12 +2843,36 @@
|
||||
"settings.account.delete.section.action": {
|
||||
"message": "Hesabı sil"
|
||||
},
|
||||
"settings.account.delete.section.description": {
|
||||
"message": "Hesabınızı sildiğinizde geri dönüş yoktur. Hesabınızı silmek, projeler hariç olmak olmak üzere tüm ekli verilerin sunucularımızdan silinmesine neden olur."
|
||||
},
|
||||
"settings.account.delete.section.title": {
|
||||
"message": "Hesabı sil"
|
||||
},
|
||||
"settings.account.email.action.save": {
|
||||
"message": "Epostayı kaydet"
|
||||
},
|
||||
"settings.account.email.field.label": {
|
||||
"message": "Eposta adresi"
|
||||
},
|
||||
"settings.account.email.field.placeholder": {
|
||||
"message": "Eposta adresinizi girin..."
|
||||
},
|
||||
"settings.account.email.modal.header.add": {
|
||||
"message": "E-posta Ekle"
|
||||
},
|
||||
"settings.account.email.modal.header.change": {
|
||||
"message": "Eposta değiştirme"
|
||||
},
|
||||
"settings.account.email.modal.notice": {
|
||||
"message": "Hesap detaylarınız herkesse gösterilmez."
|
||||
},
|
||||
"settings.account.password.action.remove": {
|
||||
"message": "Parolayı kaldır"
|
||||
},
|
||||
"settings.account.password.action.save": {
|
||||
"message": "Parolayı kaydet"
|
||||
},
|
||||
"settings.account.password.error.mismatch": {
|
||||
"message": "Parolalar uyuşmuyor!"
|
||||
},
|
||||
@@ -2861,6 +2915,12 @@
|
||||
"settings.account.providers.action.add": {
|
||||
"message": "Ekle"
|
||||
},
|
||||
"settings.account.providers.modal.header": {
|
||||
"message": "Doğrulama sağlayıcıları"
|
||||
},
|
||||
"settings.account.providers.table.actions": {
|
||||
"message": "Eylemler"
|
||||
},
|
||||
"settings.account.providers.table.provider": {
|
||||
"message": "Sağlayıcı"
|
||||
},
|
||||
@@ -2897,6 +2957,9 @@
|
||||
"settings.account.two-factor.error.incorrect-code": {
|
||||
"message": "Girilen kod yanlış!"
|
||||
},
|
||||
"settings.account.two-factor.field.code.label": {
|
||||
"message": "Çift aşamalı kodu girin"
|
||||
},
|
||||
"settings.account.two-factor.field.code.placeholder": {
|
||||
"message": "Kodu girin..."
|
||||
},
|
||||
@@ -3002,6 +3065,33 @@
|
||||
"settings.authorizations.by": {
|
||||
"message": "tarafından"
|
||||
},
|
||||
"settings.authorizations.revoke.action": {
|
||||
"message": "Kaldır"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "çeyrek"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "çeyreklik"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "yıl"
|
||||
},
|
||||
"settings.billing.interval.yearly": {
|
||||
"message": "yıllık"
|
||||
},
|
||||
"settings.billing.midas.benefits.ad-free": {
|
||||
"message": "Modrinth App ve modrinth.com'da reklamsız gezinme"
|
||||
},
|
||||
"settings.billing.midas.benefits.badge": {
|
||||
"message": "Profilinde Modrinth+ rozeti"
|
||||
},
|
||||
"settings.billing.midas.benefits.support": {
|
||||
"message": "Modrinth ve üreticilerini direkt olarak destekleyin"
|
||||
},
|
||||
"settings.billing.midas.benefits.title": {
|
||||
"message": "Yararlar"
|
||||
},
|
||||
"settings.billing.modal.cancel.action": {
|
||||
"message": "Aboneliği iptal et"
|
||||
},
|
||||
@@ -3020,6 +3110,9 @@
|
||||
"settings.billing.modal.delete.title": {
|
||||
"message": "Bu ödeme yöntemini kaldırmak istediğinizden emin misiniz?"
|
||||
},
|
||||
"settings.billing.next": {
|
||||
"message": "Sonraki:"
|
||||
},
|
||||
"settings.billing.payment_method.action.add": {
|
||||
"message": "Ödeme yöntemi ekle"
|
||||
},
|
||||
@@ -3041,18 +3134,36 @@
|
||||
"settings.billing.payment_method.title": {
|
||||
"message": "Ödeme yöntemleri"
|
||||
},
|
||||
"settings.billing.plan.title": {
|
||||
"message": "{size} Plan"
|
||||
},
|
||||
"settings.billing.price.per-interval": {
|
||||
"message": "{price} / {interval}"
|
||||
},
|
||||
"settings.billing.price.slash-interval": {
|
||||
"message": "/{interval}"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.server-id": {
|
||||
"message": "Sunucu Kimliği:{id}"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.stripe-id": {
|
||||
"message": "Stripe Kimliği: {id}"
|
||||
},
|
||||
"settings.billing.pyro.ram": {
|
||||
"message": "{gb} GB RAM"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "Yeniden abone olurken hata"
|
||||
},
|
||||
"settings.billing.pyro.status.processing": {
|
||||
"message": "Ödemeniz işleniyor. Sunucunuz ödeme tamamlandığında aktifleşecektir."
|
||||
},
|
||||
"settings.billing.pyro.storage": {
|
||||
"message": "{gb} GB RAM"
|
||||
},
|
||||
"settings.billing.pyro.swap": {
|
||||
"message": "{gb} GB Değişim"
|
||||
},
|
||||
"settings.billing.pyro_subscription.description": {
|
||||
"message": "Modrinth Sunucu aboneliklerini yönet."
|
||||
},
|
||||
@@ -3068,11 +3179,20 @@
|
||||
"settings.billing.subscription.title": {
|
||||
"message": "Abonelikler"
|
||||
},
|
||||
"settings.billing.switch.tooltip.monthly-additional-per-year": {
|
||||
"message": "Aylık faturalandırma size yıllık ek olarak {amount} maliyet çıkaracaktır"
|
||||
},
|
||||
"settings.billing.switches-to-billing-on": {
|
||||
"message": "{date} tarihinde {interval} ödemeye geçer"
|
||||
},
|
||||
"settings.billing.update-method": {
|
||||
"message": "Güncelleme metodu"
|
||||
},
|
||||
"settings.billing.upgrade": {
|
||||
"message": "Güncelle"
|
||||
},
|
||||
"settings.display.banner.developer-mode.button": {
|
||||
"message": "Geliştiri modunu devre dışı bırak"
|
||||
"message": "Geliştirme modunu devre dışı bırak"
|
||||
},
|
||||
"settings.display.banner.developer-mode.description": {
|
||||
"message": "<strong>Geliştirme modu</strong> aktif. Modrinth'te bulunan çeşitli şeylerin dahilî kimliklerini gösterir, Modrinth API kullanan bir geliştiriciyseniz kullanışlı olabilir. Sayfanın en altındaki Modrinth logosuna 5 kere tıklayarak açılır ve kapanır."
|
||||
@@ -3083,6 +3203,12 @@
|
||||
"settings.display.flags.title": {
|
||||
"message": "Özellik aç/kapa"
|
||||
},
|
||||
"settings.display.notification.developer-mode-deactivated.text": {
|
||||
"message": "Geliştirme modu kapatıldı"
|
||||
},
|
||||
"settings.display.notification.developer-mode-deactivated.title": {
|
||||
"message": "Geliştirme modu kapalı"
|
||||
},
|
||||
"settings.display.project-list-layouts.datapack": {
|
||||
"message": "Veri Paketleri sayfası"
|
||||
},
|
||||
@@ -3266,6 +3392,9 @@
|
||||
"ui.newsletter-button.subscribed": {
|
||||
"message": "Abone Olundu!"
|
||||
},
|
||||
"ui.newsletter-button.tooltip": {
|
||||
"message": "Modrinth haber mektuplarına abone ol"
|
||||
},
|
||||
"version.environment.none.description": {
|
||||
"message": "Bu sürüm için ortam belirtilmemiş."
|
||||
},
|
||||
|
||||
@@ -3155,6 +3155,12 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "每月"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "季度"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "季度"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "年"
|
||||
},
|
||||
@@ -3252,7 +3258,7 @@
|
||||
"message": "{shared} 共享 CPU(突发高达 {bursts} 个 CPU)"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.not-found": {
|
||||
"message": "我们未能找到与此订阅关联的服务器。可能存在以下几种情况:若你刚刚购买服务器,此为正常现象,服务器配置可能需要一小时左右;若你已购买一段时间,服务器可能已被暂停使用。如有不符预期的情况,请提供以下信息联系 Modrinth 支持团队:"
|
||||
"message": "我们未能找到与此订阅关联的服务器。可能存在以下几种情况:若你刚刚购买服务器,此为正常现象,服务器配置可能需要一小时左右;若你已购买一段时间,服务器可能已被停用。如有不符预期的情况,请提供以下信息联系 Modrinth 支持团队:"
|
||||
},
|
||||
"settings.billing.pyro.linked-server.server-id": {
|
||||
"message": "服务器ID:{id}"
|
||||
@@ -3269,6 +3275,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "重新订阅时出错"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "如果服务器当前已被取消,设置服务器可能需要10-15分钟。"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "重新订阅请求已提交"
|
||||
},
|
||||
|
||||
@@ -3155,6 +3155,12 @@
|
||||
"settings.billing.interval.monthly": {
|
||||
"message": "每月"
|
||||
},
|
||||
"settings.billing.interval.quarter": {
|
||||
"message": "季"
|
||||
},
|
||||
"settings.billing.interval.quarterly.adjective": {
|
||||
"message": "每季"
|
||||
},
|
||||
"settings.billing.interval.year": {
|
||||
"message": "年"
|
||||
},
|
||||
@@ -3269,6 +3275,9 @@
|
||||
"settings.billing.pyro.resubscribe.error.title": {
|
||||
"message": "重新訂閱時發生錯誤"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.text": {
|
||||
"message": "若伺服器目前處於取消狀態,可能需要 10 至 15 分鐘來完成伺服器設定。"
|
||||
},
|
||||
"settings.billing.pyro.resubscribe.request-submitted.title": {
|
||||
"message": "已送出重新訂閱請求"
|
||||
},
|
||||
|
||||
@@ -443,8 +443,8 @@
|
||||
<ModerationProjectNags
|
||||
v-if="
|
||||
projectV3 &&
|
||||
((currentMember && project.status === 'draft') ||
|
||||
tags.rejectedStatuses.includes(project.status))
|
||||
currentMember &&
|
||||
(project.status === 'draft' || tags.rejectedStatuses.includes(project.status))
|
||||
"
|
||||
:project="project"
|
||||
:project-v3="projectV3"
|
||||
|
||||
@@ -3,26 +3,47 @@
|
||||
<div class="normal-page__sidebar">
|
||||
<NavStack
|
||||
:items="[
|
||||
{ type: 'heading', label: 'Dashboard' },
|
||||
{ link: '/dashboard', label: 'Overview', icon: DashboardIcon },
|
||||
{ link: '/dashboard/notifications', label: 'Notifications', icon: NotificationsIcon },
|
||||
{ link: '/dashboard/reports', label: 'Active reports', icon: ReportIcon },
|
||||
{ type: 'heading', label: formatMessage(messages.dashboard) },
|
||||
{ link: '/dashboard', label: formatMessage(messages.overview), icon: DashboardIcon },
|
||||
{
|
||||
link: '/dashboard/notifications',
|
||||
label: formatMessage(messages.notifications),
|
||||
icon: NotificationsIcon,
|
||||
},
|
||||
{
|
||||
link: '/dashboard/reports',
|
||||
label: formatMessage(messages.activeReports),
|
||||
icon: ReportIcon,
|
||||
},
|
||||
{
|
||||
link: '/dashboard/collections',
|
||||
label: formatMessage(commonMessages.collectionsLabel),
|
||||
icon: LibraryIcon,
|
||||
},
|
||||
{ type: 'heading', label: 'Creators' },
|
||||
{ link: '/dashboard/projects', label: 'Projects', icon: ListIcon },
|
||||
{ link: '/dashboard/organizations', label: 'Organizations', icon: OrganizationIcon },
|
||||
{ link: '/dashboard/analytics', label: 'Analytics', icon: ChartIcon },
|
||||
{ type: 'heading', label: formatMessage(messages.creators) },
|
||||
{ link: '/dashboard/projects', label: formatMessage(messages.projects), icon: ListIcon },
|
||||
{
|
||||
link: '/dashboard/organizations',
|
||||
label: formatMessage(messages.organizations),
|
||||
icon: OrganizationIcon,
|
||||
},
|
||||
{
|
||||
link: '/dashboard/analytics',
|
||||
label: formatMessage(messages.analytics),
|
||||
icon: ChartIcon,
|
||||
},
|
||||
{
|
||||
link: '/dashboard/affiliate-links',
|
||||
label: formatMessage(commonMessages.affiliateLinksButton),
|
||||
icon: AffiliateIcon,
|
||||
shown: !!isAffiliate,
|
||||
},
|
||||
{ link: '/dashboard/revenue', label: 'Revenue', icon: CurrencyIcon, matchNested: true },
|
||||
{
|
||||
link: '/dashboard/revenue',
|
||||
label: formatMessage(messages.revenue),
|
||||
icon: CurrencyIcon,
|
||||
matchNested: true,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
@@ -43,7 +64,7 @@ import {
|
||||
OrganizationIcon,
|
||||
ReportIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { commonMessages, useVIntl } from '@modrinth/ui'
|
||||
import { commonMessages, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { type User, UserBadge } from '@modrinth/utils'
|
||||
|
||||
import NavStack from '~/components/ui/NavStack.vue'
|
||||
@@ -56,6 +77,45 @@ const isAffiliate = computed(() => {
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
dashboard: {
|
||||
id: 'dashboard.sidebar.label.dashboard',
|
||||
defaultMessage: 'Dashboard',
|
||||
},
|
||||
overview: {
|
||||
id: 'dashboard.sidebar.label.overview',
|
||||
defaultMessage: 'Overview',
|
||||
},
|
||||
notifications: {
|
||||
id: 'dashboard.sidebar.label.notifications',
|
||||
defaultMessage: 'Notifications',
|
||||
},
|
||||
activeReports: {
|
||||
id: 'dashboard.sidebar.label.activeReports',
|
||||
defaultMessage: 'Active reports',
|
||||
},
|
||||
creators: {
|
||||
id: 'dashboard.sidebar.label.creators',
|
||||
defaultMessage: 'Creators',
|
||||
},
|
||||
projects: {
|
||||
id: 'dashboard.sidebar.label.projects',
|
||||
defaultMessage: 'Projects',
|
||||
},
|
||||
organizations: {
|
||||
id: 'dashboard.sidebar.label.organizations',
|
||||
defaultMessage: 'Organizations',
|
||||
},
|
||||
analytics: {
|
||||
id: 'dashboard.sidebar.label.analytics',
|
||||
defaultMessage: 'Analytics',
|
||||
},
|
||||
revenue: {
|
||||
id: 'dashboard.sidebar.label.revenue',
|
||||
defaultMessage: 'Revenue',
|
||||
},
|
||||
})
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
})
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
v-else-if="!filteredAffiliates || filteredAffiliates.length === 0"
|
||||
class="py-8 text-center"
|
||||
>
|
||||
<p class="text-secondary">No affiliate codes found.</p>
|
||||
<p class="text-secondary">{{ formatMessage(messages.noAffiliateCodesFound) }}</p>
|
||||
</div>
|
||||
<div v-else class="space-y-3">
|
||||
<AffiliateLinkCard
|
||||
@@ -166,6 +166,10 @@ const messages = defineMessages({
|
||||
id: 'dashboard.affiliate-links.error.title',
|
||||
defaultMessage: 'Error loading affiliate links',
|
||||
},
|
||||
noAffiliateCodesFound: {
|
||||
id: 'dashboard.affiliate-links.empty.no-codes',
|
||||
defaultMessage: 'No affiliate codes found.',
|
||||
},
|
||||
revokeConfirmButton: {
|
||||
id: 'dashboard.affiliate-links.revoke-confirm.button',
|
||||
defaultMessage: 'Revoke',
|
||||
|
||||
@@ -12,10 +12,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { injectModrinthClient, useDebugLogger } from '@modrinth/ui'
|
||||
import {
|
||||
commonProjectSettingsMessages,
|
||||
injectModrinthClient,
|
||||
useDebugLogger,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
import ChartDisplay from '~/components/ui/charts/ChartDisplay.vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const debug = useDebugLogger('analytics.vue')
|
||||
|
||||
definePageMeta({
|
||||
@@ -23,7 +30,7 @@ definePageMeta({
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Analytics - Modrinth',
|
||||
title: () => `${formatMessage(commonProjectSettingsMessages.analytics)} - Modrinth`,
|
||||
})
|
||||
|
||||
const auth = await useAuth()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:icon="SearchIcon"
|
||||
type="text"
|
||||
clearable
|
||||
placeholder="Search collections..."
|
||||
:placeholder="formatMessage(messages.searchCollectionsPlaceholder)"
|
||||
wrapper-class="w-full"
|
||||
input-class="!h-12"
|
||||
/>
|
||||
@@ -20,18 +20,13 @@
|
||||
v-slot="{ selected }"
|
||||
v-model="sortBy"
|
||||
class="!w-auto flex-grow md:flex-grow-0"
|
||||
name="Sort by"
|
||||
:name="formatMessage(commonMessages.sortByLabel)"
|
||||
:options="['updated', 'created', 'name']"
|
||||
:display-name="
|
||||
(option) =>
|
||||
option === 'updated'
|
||||
? 'Recently Updated'
|
||||
: option === 'created'
|
||||
? 'Recently Created'
|
||||
: 'Name (A-Z)'
|
||||
"
|
||||
:display-name="formatCollectionSortOption"
|
||||
>
|
||||
<span class="font-semibold text-primary">Sort by: </span>
|
||||
<span class="font-semibold text-primary">{{
|
||||
formatMessage(commonMessages.sortByLabel)
|
||||
}}</span>
|
||||
<span class="font-semibold text-secondary">{{ selected }}</span>
|
||||
</DropdownSelect>
|
||||
|
||||
@@ -188,6 +183,22 @@ const messages = defineMessages({
|
||||
id: 'dashboard.collections.label.search-input',
|
||||
defaultMessage: 'Search your collections',
|
||||
},
|
||||
searchCollectionsPlaceholder: {
|
||||
id: 'dashboard.collections.placeholder.search',
|
||||
defaultMessage: 'Search collections...',
|
||||
},
|
||||
sortRecentlyUpdated: {
|
||||
id: 'dashboard.collections.sort.recently-updated',
|
||||
defaultMessage: 'Recently Updated',
|
||||
},
|
||||
sortRecentlyCreated: {
|
||||
id: 'dashboard.collections.sort.recently-created',
|
||||
defaultMessage: 'Recently Created',
|
||||
},
|
||||
sortNameAscending: {
|
||||
id: 'dashboard.collections.sort.name-ascending',
|
||||
defaultMessage: 'Name (A-Z)',
|
||||
},
|
||||
emptyNoMatch: {
|
||||
id: 'dashboard.collections.empty.no-match',
|
||||
defaultMessage: 'No collections match your search',
|
||||
@@ -234,6 +245,18 @@ const router = useNativeRouter()
|
||||
const validSortOptions = ['updated', 'created', 'name']
|
||||
const sortBy = ref(validSortOptions.includes(route.query.s) ? route.query.s : 'updated')
|
||||
|
||||
function formatCollectionSortOption(option) {
|
||||
if (option === 'updated') {
|
||||
return formatMessage(messages.sortRecentlyUpdated)
|
||||
}
|
||||
|
||||
if (option === 'created') {
|
||||
return formatMessage(messages.sortRecentlyCreated)
|
||||
}
|
||||
|
||||
return formatMessage(messages.sortNameAscending)
|
||||
}
|
||||
|
||||
const orderedCollections = computed(() => {
|
||||
if (!collections.value) return []
|
||||
return [...collections.value]
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{{ auth.user.username }}
|
||||
</h1>
|
||||
<NuxtLink class="goto-link" :to="`/user/${auth.user.username}`">
|
||||
Visit your profile
|
||||
{{ formatMessage(commonMessages.visitYourProfile) }}
|
||||
<ChevronRightIcon class="featured-header-chevron" aria-hidden="true" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
@@ -15,13 +15,15 @@
|
||||
<div class="dashboard-notifications">
|
||||
<section class="universal-card">
|
||||
<div class="header__row">
|
||||
<h2 class="header__title text-2xl">Notifications</h2>
|
||||
<h2 class="header__title text-2xl">
|
||||
{{ formatMessage(commonMessages.notificationsLabel) }}
|
||||
</h2>
|
||||
<nuxt-link
|
||||
v-if="notifications.length > 0"
|
||||
class="goto-link"
|
||||
to="/dashboard/notifications"
|
||||
>
|
||||
See all
|
||||
{{ formatMessage(messages.seeAll) }}
|
||||
<ChevronRightIcon />
|
||||
</nuxt-link>
|
||||
</div>
|
||||
@@ -42,15 +44,15 @@
|
||||
class="goto-link view-more-notifs mt-4"
|
||||
to="/dashboard/notifications"
|
||||
>
|
||||
View {{ extraNotifs }} more notification{{ extraNotifs === 1 ? '' : 's' }}
|
||||
{{ formatMessage(messages.viewMore, { extraNotifs: extraNotifs }) }}
|
||||
<ChevronRightIcon />
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<div v-else class="universal-body">
|
||||
<p>You have no unread notifications.</p>
|
||||
<p>{{ formatMessage(messages.noUnreadNotifications) }}</p>
|
||||
<nuxt-link class="iconified-button !mt-4" to="/dashboard/notifications/history">
|
||||
<HistoryIcon />
|
||||
View notification history
|
||||
{{ formatMessage(messages.viewNotificationHistory) }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</section>
|
||||
@@ -58,18 +60,16 @@
|
||||
|
||||
<div class="dashboard-analytics">
|
||||
<section class="universal-card">
|
||||
<h2>Analytics</h2>
|
||||
<h2>{{ formatMessage(commonMessages.analyticsButton) }}</h2>
|
||||
<div class="grid-display">
|
||||
<div class="grid-display__item">
|
||||
<div class="label">Total downloads</div>
|
||||
<div class="label">{{ formatMessage(messages.totalDownloads) }}</div>
|
||||
<div class="value">
|
||||
{{ $formatNumber(projects.reduce((agg, x) => agg + x.downloads, 0)) }}
|
||||
</div>
|
||||
<span
|
||||
>from
|
||||
{{ downloadsProjectCount }}
|
||||
project{{ downloadsProjectCount === 1 ? '' : 's' }}</span
|
||||
>
|
||||
<span>{{
|
||||
formatMessage(messages.fromProjects, { count: downloadsProjectCount })
|
||||
}}</span>
|
||||
<!-- <NuxtLink class="goto-link" to="/dashboard/analytics"-->
|
||||
<!-- >View breakdown-->
|
||||
<!-- <ChevronRightIcon-->
|
||||
@@ -78,16 +78,15 @@
|
||||
<!-- /></NuxtLink>-->
|
||||
</div>
|
||||
<div class="grid-display__item">
|
||||
<div class="label">Total followers</div>
|
||||
<div class="label">{{ formatMessage(messages.totalFollowers) }}</div>
|
||||
<div class="value">
|
||||
{{ $formatNumber(projects.reduce((agg, x) => agg + x.followers, 0)) }}
|
||||
</div>
|
||||
<span>
|
||||
<span
|
||||
>from {{ followersProjectCount }} project{{
|
||||
followersProjectCount === 1 ? '' : 's'
|
||||
}}</span
|
||||
></span
|
||||
<span>{{
|
||||
formatMessage(messages.fromProjects, { count: followersProjectCount })
|
||||
}}</span></span
|
||||
>
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,14 +96,58 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { ChevronRightIcon, HistoryIcon } from '@modrinth/assets'
|
||||
import { Avatar, injectModrinthClient } from '@modrinth/ui'
|
||||
import {
|
||||
Avatar,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
import NotificationItem from '~/components/ui/NotificationItem.vue'
|
||||
import { fetchExtraNotificationData, groupNotifications } from '~/helpers/platform-notifications.ts'
|
||||
|
||||
useHead({
|
||||
title: 'Dashboard - Modrinth',
|
||||
title: () => `${formatMessage(messages.headTitle)} - Modrinth`,
|
||||
})
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
headTitle: {
|
||||
id: 'dashboard.head-title',
|
||||
defaultMessage: 'Dashboard',
|
||||
},
|
||||
seeAll: {
|
||||
id: 'dashboard.notifications.link.see-all',
|
||||
defaultMessage: 'See all',
|
||||
},
|
||||
viewMore: {
|
||||
id: 'dashboard.notifications.link.view-more',
|
||||
defaultMessage:
|
||||
'View {extraNotifs} more {extraNotifs, plural, one {notification} other {notifications}}',
|
||||
},
|
||||
noUnreadNotifications: {
|
||||
id: 'dashboard.notifications.empty.no-unread',
|
||||
defaultMessage: 'You have no unread notifications.',
|
||||
},
|
||||
viewNotificationHistory: {
|
||||
id: 'dashboard.notifications.link.view-history',
|
||||
defaultMessage: 'View notification history',
|
||||
},
|
||||
totalDownloads: {
|
||||
id: 'dashboard.analytics.total-downloads',
|
||||
defaultMessage: 'Total downloads',
|
||||
},
|
||||
totalFollowers: {
|
||||
id: 'dashboard.analytics.total-followers',
|
||||
defaultMessage: 'Total followers',
|
||||
},
|
||||
fromProjects: {
|
||||
id: 'dashboard.analytics.from-projects',
|
||||
defaultMessage: 'from {count} {count, plural, one {project} other {projects}}',
|
||||
},
|
||||
})
|
||||
|
||||
const auth = await useAuth()
|
||||
|
||||
@@ -3,22 +3,31 @@
|
||||
<section class="universal-card">
|
||||
<Breadcrumbs
|
||||
v-if="history"
|
||||
current-title="History"
|
||||
:link-stack="[{ href: `/dashboard/notifications`, label: 'Notifications' }]"
|
||||
:current-title="formatMessage(messages.historyLabel)"
|
||||
:link-stack="[
|
||||
{
|
||||
href: `/dashboard/notifications`,
|
||||
label: formatMessage(commonMessages.notificationsLabel),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
<div class="header__row">
|
||||
<div class="header__title">
|
||||
<h2 v-if="history" class="text-2xl">Notification history</h2>
|
||||
<h2 v-else class="text-2xl">Notifications</h2>
|
||||
<h2 v-if="history" class="text-2xl">
|
||||
{{ formatMessage(messages.notificationHistoryTitle) }}
|
||||
</h2>
|
||||
<h2 v-else class="text-2xl">
|
||||
{{ formatMessage(commonMessages.notificationsLabel) }}
|
||||
</h2>
|
||||
</div>
|
||||
<template v-if="!history">
|
||||
<Button v-if="data.hasRead" @click="updateRoute()">
|
||||
<HistoryIcon />
|
||||
View history
|
||||
{{ formatMessage(messages.viewHistory) }}
|
||||
</Button>
|
||||
<Button v-if="notifications.length > 0" color="danger" @click="readAll()">
|
||||
<CheckCheckIcon />
|
||||
Mark all as read
|
||||
{{ formatMessage(messages.markAllAsRead) }}
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
@@ -29,9 +38,9 @@
|
||||
:format-label="(x) => (x === 'all' ? 'All' : formatProjectType(x).replace('_', ' ') + 's')"
|
||||
:capitalize="false"
|
||||
/>
|
||||
<p v-if="isPending">Loading notifications...</p>
|
||||
<p v-if="isPending">{{ formatMessage(messages.loadingNotifications) }}</p>
|
||||
<template v-else-if="error">
|
||||
<p>Error loading notifications:</p>
|
||||
<p>{{ formatMessage(messages.errorLoadingNotifications) }}</p>
|
||||
<pre>
|
||||
{{ error }}
|
||||
</pre>
|
||||
@@ -48,7 +57,7 @@
|
||||
@update:notifications="() => refetch()"
|
||||
/>
|
||||
</template>
|
||||
<p v-else>You don't have any unread notifications.</p>
|
||||
<p v-else>{{ formatMessage(messages.noUnreadNotifications) }}</p>
|
||||
<div class="flex justify-end">
|
||||
<Pagination :page="page" :count="pages" @switch-page="changePage" />
|
||||
</div>
|
||||
@@ -57,7 +66,15 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { CheckCheckIcon, HistoryIcon } from '@modrinth/assets'
|
||||
import { Button, Chips, injectModrinthClient, Pagination } from '@modrinth/ui'
|
||||
import {
|
||||
Button,
|
||||
Chips,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
Pagination,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { formatProjectType } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
@@ -69,8 +86,37 @@ import {
|
||||
markAsRead,
|
||||
} from '~/helpers/platform-notifications.ts'
|
||||
|
||||
useHead({
|
||||
title: 'Notifications - Modrinth',
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
historyLabel: {
|
||||
id: 'dashboard.notifications.history.label',
|
||||
defaultMessage: 'History',
|
||||
},
|
||||
notificationHistoryTitle: {
|
||||
id: 'dashboard.notifications.history.title',
|
||||
defaultMessage: 'Notification history',
|
||||
},
|
||||
viewHistory: {
|
||||
id: 'dashboard.notifications.button.view-history',
|
||||
defaultMessage: 'View history',
|
||||
},
|
||||
markAllAsRead: {
|
||||
id: 'dashboard.notifications.button.mark-all-as-read',
|
||||
defaultMessage: 'Mark all as read',
|
||||
},
|
||||
loadingNotifications: {
|
||||
id: 'dashboard.notifications.loading',
|
||||
defaultMessage: 'Loading notifications...',
|
||||
},
|
||||
errorLoadingNotifications: {
|
||||
id: 'dashboard.notifications.error.loading',
|
||||
defaultMessage: 'Error loading notifications:',
|
||||
},
|
||||
noUnreadNotifications: {
|
||||
id: 'dashboard.notifications.empty.no-unread',
|
||||
defaultMessage: "You don't have any unread notifications.",
|
||||
},
|
||||
})
|
||||
|
||||
const client = injectModrinthClient()
|
||||
@@ -79,6 +125,12 @@ const route = useNativeRoute()
|
||||
const router = useNativeRouter()
|
||||
|
||||
const history = computed(() => route.name === 'dashboard-notifications-history')
|
||||
|
||||
useHead({
|
||||
title: () =>
|
||||
`${formatMessage(history.value ? messages.notificationHistoryTitle : commonMessages.notificationsLabel)} - Modrinth`,
|
||||
})
|
||||
|
||||
const selectedType = ref('all')
|
||||
const page = ref(1)
|
||||
const perPage = ref(50)
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<OrganizationCreateModal ref="createOrgModal" />
|
||||
<section class="universal-card">
|
||||
<div class="header__row">
|
||||
<h2 class="header__title text-2xl">Organizations</h2>
|
||||
<h2 class="header__title text-2xl">{{ formatMessage(messages.organizationsTitle) }}</h2>
|
||||
<div class="input-group">
|
||||
<button class="iconified-button brand-button" @click="openCreateOrgModal">
|
||||
<PlusIcon aria-hidden="true" />
|
||||
Create organization
|
||||
{{ formatMessage(messages.createOrganization) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,10 +32,11 @@
|
||||
<div class="stats">
|
||||
<UsersIcon aria-hidden="true" />
|
||||
<span>
|
||||
{{ onlyAcceptedMembers(org.members).length }}
|
||||
member<template v-if="onlyAcceptedMembers(org.members).length !== 1"
|
||||
>s</template
|
||||
>
|
||||
{{
|
||||
formatMessage(messages.memberCount, {
|
||||
count: onlyAcceptedMembers(org.members).length,
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
@@ -43,19 +44,44 @@
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else> Make an organization! </template>
|
||||
<template v-else> {{ formatMessage(messages.makeOrganization) }} </template>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { PlusIcon, UsersIcon } from '@modrinth/assets'
|
||||
import { Avatar, injectModrinthClient } from '@modrinth/ui'
|
||||
import { Avatar, defineMessages, injectModrinthClient, useVIntl } from '@modrinth/ui'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
import OrganizationCreateModal from '~/components/ui/create/OrganizationCreateModal.vue'
|
||||
import { useAuth } from '~/composables/auth.js'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
organizationsTitle: {
|
||||
id: 'dashboard.organizations.title',
|
||||
defaultMessage: 'Organizations',
|
||||
},
|
||||
createOrganization: {
|
||||
id: 'dashboard.organizations.button.create',
|
||||
defaultMessage: 'Create organization',
|
||||
},
|
||||
memberCount: {
|
||||
id: 'dashboard.organizations.member-count',
|
||||
defaultMessage: '{count} {count, plural, one {member} other {members}}',
|
||||
},
|
||||
makeOrganization: {
|
||||
id: 'dashboard.organizations.empty.cta',
|
||||
defaultMessage: 'Make an organization!',
|
||||
},
|
||||
fetchOrganizationsFailed: {
|
||||
id: 'dashboard.organizations.error.fetch',
|
||||
defaultMessage: 'Failed to fetch organizations',
|
||||
},
|
||||
})
|
||||
|
||||
const createOrgModal = ref(null)
|
||||
|
||||
const auth = await useAuth()
|
||||
@@ -77,7 +103,7 @@ const onlyAcceptedMembers = (members) => members.filter((member) => member?.acce
|
||||
if (error.value) {
|
||||
createError({
|
||||
statusCode: 500,
|
||||
message: 'Failed to fetch organizations',
|
||||
message: formatMessage(messages.fetchOrganizationsFailed),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<NewModal ref="editLinksModal" header="Edit links">
|
||||
<NewModal ref="editLinksModal" :header="formatMessage(messages.editLinksButton)">
|
||||
<div class="universal-modal links-modal !p-0">
|
||||
<p>
|
||||
Any links you specify below will be overwritten on each of the selected projects. Any you
|
||||
leave blank will be ignored. You can clear a link from all selected projects using the
|
||||
trash can button.
|
||||
</p>
|
||||
<p>{{ formatMessage(messages.editLinksDescription) }}</p>
|
||||
<section class="links">
|
||||
<label
|
||||
for="issue-tracker-input"
|
||||
title="A place for users to report bugs, issues, and concerns about your project."
|
||||
>
|
||||
<span class="label__title">Issue tracker</span>
|
||||
<label for="issue-tracker-input" :title="formatMessage(messages.issueTrackerDescription)">
|
||||
<span class="label__title">{{ formatMessage(messages.issueTrackerLabel) }}</span>
|
||||
</label>
|
||||
<div class="input-group shrink-first">
|
||||
<StyledInput
|
||||
@@ -20,14 +13,12 @@
|
||||
v-model="editLinks.issues.val"
|
||||
:disabled="editLinks.issues.clear"
|
||||
type="url"
|
||||
:placeholder="
|
||||
editLinks.issues.clear ? 'Existing link will be cleared' : 'Enter a valid URL'
|
||||
"
|
||||
:placeholder="getLinkInputPlaceholder(editLinks.issues.clear)"
|
||||
:maxlength="2048"
|
||||
/>
|
||||
<button
|
||||
v-tooltip="'Clear link'"
|
||||
aria-label="Clear link"
|
||||
v-tooltip="formatMessage(messages.clearLinkLabel)"
|
||||
:aria-label="formatMessage(messages.clearLinkLabel)"
|
||||
class="square-button label-button"
|
||||
:data-active="editLinks.issues.clear"
|
||||
@click="editLinks.issues.clear = !editLinks.issues.clear"
|
||||
@@ -35,11 +26,8 @@
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</div>
|
||||
<label
|
||||
for="source-code-input"
|
||||
title="A page/repository containing the source code for your project"
|
||||
>
|
||||
<span class="label__title">Source code</span>
|
||||
<label for="source-code-input" :title="formatMessage(messages.sourceCodeDescription)">
|
||||
<span class="label__title">{{ formatMessage(messages.sourceCodeLabel) }}</span>
|
||||
</label>
|
||||
<div class="input-group shrink-first">
|
||||
<StyledInput
|
||||
@@ -48,13 +36,11 @@
|
||||
:disabled="editLinks.source.clear"
|
||||
type="url"
|
||||
:maxlength="2048"
|
||||
:placeholder="
|
||||
editLinks.source.clear ? 'Existing link will be cleared' : 'Enter a valid URL'
|
||||
"
|
||||
:placeholder="getLinkInputPlaceholder(editLinks.source.clear)"
|
||||
/>
|
||||
<button
|
||||
v-tooltip="'Clear link'"
|
||||
aria-label="Clear link"
|
||||
v-tooltip="formatMessage(messages.clearLinkLabel)"
|
||||
:aria-label="formatMessage(messages.clearLinkLabel)"
|
||||
class="square-button label-button"
|
||||
:data-active="editLinks.source.clear"
|
||||
@click="editLinks.source.clear = !editLinks.source.clear"
|
||||
@@ -62,11 +48,8 @@
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</div>
|
||||
<label
|
||||
for="wiki-page-input"
|
||||
title="A page containing information, documentation, and help for the project."
|
||||
>
|
||||
<span class="label__title">Wiki page</span>
|
||||
<label for="wiki-page-input" :title="formatMessage(messages.wikiPageDescription)">
|
||||
<span class="label__title">{{ formatMessage(messages.wikiPageLabel) }}</span>
|
||||
</label>
|
||||
<div class="input-group shrink-first">
|
||||
<StyledInput
|
||||
@@ -75,13 +58,11 @@
|
||||
:disabled="editLinks.wiki.clear"
|
||||
type="url"
|
||||
:maxlength="2048"
|
||||
:placeholder="
|
||||
editLinks.wiki.clear ? 'Existing link will be cleared' : 'Enter a valid URL'
|
||||
"
|
||||
:placeholder="getLinkInputPlaceholder(editLinks.wiki.clear)"
|
||||
/>
|
||||
<button
|
||||
v-tooltip="'Clear link'"
|
||||
aria-label="Clear link"
|
||||
v-tooltip="formatMessage(messages.clearLinkLabel)"
|
||||
:aria-label="formatMessage(messages.clearLinkLabel)"
|
||||
class="square-button label-button"
|
||||
:data-active="editLinks.wiki.clear"
|
||||
@click="editLinks.wiki.clear = !editLinks.wiki.clear"
|
||||
@@ -89,8 +70,11 @@
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</div>
|
||||
<label for="discord-invite-input" title="An invitation link to your Discord server.">
|
||||
<span class="label__title">Discord invite</span>
|
||||
<label
|
||||
for="discord-invite-input"
|
||||
:title="formatMessage(messages.discordInviteDescription)"
|
||||
>
|
||||
<span class="label__title">{{ formatMessage(messages.discordInviteLabel) }}</span>
|
||||
</label>
|
||||
<div class="input-group shrink-first">
|
||||
<StyledInput
|
||||
@@ -99,15 +83,11 @@
|
||||
:disabled="editLinks.discord.clear"
|
||||
type="url"
|
||||
:maxlength="2048"
|
||||
:placeholder="
|
||||
editLinks.discord.clear
|
||||
? 'Existing link will be cleared'
|
||||
: 'Enter a valid Discord invite URL'
|
||||
"
|
||||
:placeholder="getLinkInputPlaceholder(editLinks.discord.clear, true)"
|
||||
/>
|
||||
<button
|
||||
v-tooltip="'Clear link'"
|
||||
aria-label="Clear link"
|
||||
v-tooltip="formatMessage(messages.clearLinkLabel)"
|
||||
:aria-label="formatMessage(messages.clearLinkLabel)"
|
||||
class="square-button label-button"
|
||||
:data-active="editLinks.discord.clear"
|
||||
@click="editLinks.discord.clear = !editLinks.discord.clear"
|
||||
@@ -117,10 +97,14 @@
|
||||
</div>
|
||||
</section>
|
||||
<p>
|
||||
Changes will be applied to
|
||||
<strong>{{ selectedProjects.length }}</strong> project{{
|
||||
selectedProjects.length > 1 ? 's' : ''
|
||||
}}.
|
||||
<IntlFormatted
|
||||
:message-id="messages.changesAppliedTo"
|
||||
:values="{ count: selectedProjects.length }"
|
||||
>
|
||||
<template #strong="{ children }">
|
||||
<strong><component :is="() => children" /></strong>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</p>
|
||||
<ul>
|
||||
<li
|
||||
@@ -133,23 +117,25 @@
|
||||
{{ project.title }}
|
||||
</li>
|
||||
<li v-if="!editLinks.showAffected && selectedProjects.length > 3">
|
||||
<strong>and {{ selectedProjects.length - 3 }} more...</strong>
|
||||
<strong>{{
|
||||
formatMessage(messages.andMore, { count: selectedProjects.length - 3 })
|
||||
}}</strong>
|
||||
</li>
|
||||
</ul>
|
||||
<Checkbox
|
||||
v-if="selectedProjects.length > 3"
|
||||
v-model="editLinks.showAffected"
|
||||
label="Show all projects"
|
||||
description="Show all projects"
|
||||
:label="formatMessage(messages.showAllProjects)"
|
||||
:description="formatMessage(messages.showAllProjects)"
|
||||
/>
|
||||
<div class="push-right input-group">
|
||||
<button class="iconified-button" @click="$refs.editLinksModal.hide()">
|
||||
<XIcon />
|
||||
Cancel
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
<button class="iconified-button brand-button" @click="bulkEditLinks()">
|
||||
<SaveIcon />
|
||||
Save changes
|
||||
{{ formatMessage(commonMessages.saveChangesButton) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -157,7 +143,7 @@
|
||||
<ModalCreation ref="modal_creation" />
|
||||
<section class="universal-card">
|
||||
<div class="header__row">
|
||||
<h2 class="header__title text-2xl">Projects</h2>
|
||||
<h2 class="header__title text-2xl">{{ formatMessage(messages.headTitle) }}</h2>
|
||||
<div class="input-group">
|
||||
<button class="iconified-button brand-button" @click="$refs.modal_creation.show($event)">
|
||||
<PlusIcon />
|
||||
@@ -166,10 +152,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="projects.length < 1">
|
||||
You don't have any projects yet. Click the green button above to begin.
|
||||
{{ formatMessage(messages.noProjectsYet) }}
|
||||
</p>
|
||||
<template v-else>
|
||||
<p>You can edit multiple projects at once by selecting them below.</p>
|
||||
<p>{{ formatMessage(messages.bulkEditHint) }}</p>
|
||||
<div class="input-group">
|
||||
<button
|
||||
class="iconified-button"
|
||||
@@ -177,11 +163,11 @@
|
||||
@click="$refs.editLinksModal.show()"
|
||||
>
|
||||
<EditIcon />
|
||||
Edit links
|
||||
{{ formatMessage(messages.editLinksButton) }}
|
||||
</button>
|
||||
<div class="push-right">
|
||||
<div class="labeled-control-row">
|
||||
Sort by
|
||||
{{ formatMessage(commonMessages.sortByLabel) }}
|
||||
<Combobox
|
||||
v-model="sortBy"
|
||||
:searchable="false"
|
||||
@@ -190,7 +176,7 @@
|
||||
@update:model-value="projects = updateSort(projects, sortBy, descending)"
|
||||
/>
|
||||
<button
|
||||
v-tooltip="descending ? 'Descending' : 'Ascending'"
|
||||
v-tooltip="formatMessage(descending ? messages.descending : messages.ascending)"
|
||||
class="square-button"
|
||||
@click="updateDescending()"
|
||||
>
|
||||
@@ -208,11 +194,11 @@
|
||||
@update:model-value="toggleAllBulkEditableProjects()"
|
||||
/>
|
||||
</div>
|
||||
<div>Icon</div>
|
||||
<div>Name</div>
|
||||
<div>ID</div>
|
||||
<div>Type</div>
|
||||
<div>Status</div>
|
||||
<div>{{ formatMessage(messages.iconHeader) }}</div>
|
||||
<div>{{ formatMessage(messages.nameHeader) }}</div>
|
||||
<div>{{ formatMessage(messages.idHeader) }}</div>
|
||||
<div>{{ formatMessage(messages.typeHeader) }}</div>
|
||||
<div>{{ formatMessage(messages.statusHeader) }}</div>
|
||||
<div />
|
||||
</div>
|
||||
<div v-for="project in projects" :key="`project-${project.id}`" class="grid-table__row">
|
||||
@@ -234,7 +220,7 @@
|
||||
<Avatar
|
||||
:src="project.icon_url"
|
||||
aria-hidden="true"
|
||||
:alt="'Icon for ' + project.title"
|
||||
:alt="formatMessage(messages.projectIconAlt, { title: project.title })"
|
||||
no-shadow
|
||||
/>
|
||||
</nuxt-link>
|
||||
@@ -244,7 +230,7 @@
|
||||
<span class="project-title">
|
||||
<IssuesIcon
|
||||
v-if="project.moderator_message"
|
||||
aria-label="Project has a message from the moderators. View the project to see more."
|
||||
:aria-label="formatMessage(messages.projectModeratorMessageAriaLabel)"
|
||||
/>
|
||||
|
||||
<nuxt-link
|
||||
@@ -277,7 +263,7 @@
|
||||
color="orange"
|
||||
>
|
||||
<nuxt-link
|
||||
v-tooltip="'Please review environment metadata'"
|
||||
v-tooltip="formatMessage(messages.reviewEnvironmentMetadata)"
|
||||
:to="`/${getProjectTypeForUrl(project.project_type, project.loaders)}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}?showEnvironmentMigrationWarning=true`"
|
||||
@@ -323,7 +309,9 @@ import {
|
||||
Combobox,
|
||||
commonMessages,
|
||||
CopyCode,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
IntlFormatted,
|
||||
NewModal,
|
||||
ProjectStatusBadge,
|
||||
StyledInput,
|
||||
@@ -334,8 +322,6 @@ import { formatProjectType } from '@modrinth/utils'
|
||||
import ModalCreation from '~/components/ui/create/ProjectCreateModal.vue'
|
||||
import { getProjectTypeForUrl } from '~/helpers/projects.js'
|
||||
|
||||
useHead({ title: 'Projects - Modrinth' })
|
||||
|
||||
// const UPLOAD_VERSION = 1 << 0
|
||||
// const DELETE_VERSION = 1 << 1
|
||||
const EDIT_DETAILS = 1 << 2
|
||||
@@ -348,16 +334,163 @@ const EDIT_DETAILS = 1 << 2
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
headTitle: {
|
||||
id: 'dashboard.projects.head-title',
|
||||
defaultMessage: 'Projects',
|
||||
},
|
||||
editLinksButton: {
|
||||
id: 'dashboard.projects.links.button.edit',
|
||||
defaultMessage: 'Edit links',
|
||||
},
|
||||
editLinksDescription: {
|
||||
id: 'dashboard.projects.links.description',
|
||||
defaultMessage:
|
||||
'Any links you specify below will be overwritten on each of the selected projects. Any you leave blank will be ignored. You can clear a link from all selected projects using the trash can button.',
|
||||
},
|
||||
issueTrackerLabel: {
|
||||
id: 'dashboard.projects.links.issue-tracker.label',
|
||||
defaultMessage: 'Issue tracker',
|
||||
},
|
||||
issueTrackerDescription: {
|
||||
id: 'dashboard.projects.links.issue-tracker.description',
|
||||
defaultMessage: 'A place for users to report bugs, issues, and concerns about your project.',
|
||||
},
|
||||
sourceCodeLabel: {
|
||||
id: 'dashboard.projects.links.source-code.label',
|
||||
defaultMessage: 'Source code',
|
||||
},
|
||||
sourceCodeDescription: {
|
||||
id: 'dashboard.projects.links.source-code.description',
|
||||
defaultMessage: 'A page/repository containing the source code for your project',
|
||||
},
|
||||
wikiPageLabel: {
|
||||
id: 'dashboard.projects.links.wiki-page.label',
|
||||
defaultMessage: 'Wiki page',
|
||||
},
|
||||
wikiPageDescription: {
|
||||
id: 'dashboard.projects.links.wiki-page.description',
|
||||
defaultMessage: 'A page containing information, documentation, and help for the project.',
|
||||
},
|
||||
discordInviteLabel: {
|
||||
id: 'dashboard.projects.links.discord-invite.label',
|
||||
defaultMessage: 'Discord invite',
|
||||
},
|
||||
discordInviteDescription: {
|
||||
id: 'dashboard.projects.links.discord-invite.description',
|
||||
defaultMessage: 'An invitation link to your Discord server.',
|
||||
},
|
||||
existingLinkWillBeCleared: {
|
||||
id: 'dashboard.projects.links.placeholder.cleared',
|
||||
defaultMessage: 'Existing link will be cleared',
|
||||
},
|
||||
enterValidUrl: {
|
||||
id: 'dashboard.projects.links.placeholder.valid-url',
|
||||
defaultMessage: 'Enter a valid URL',
|
||||
},
|
||||
enterValidDiscordInviteUrl: {
|
||||
id: 'dashboard.projects.links.placeholder.valid-discord-url',
|
||||
defaultMessage: 'Enter a valid Discord invite URL',
|
||||
},
|
||||
clearLinkLabel: {
|
||||
id: 'dashboard.projects.links.button.clear-link',
|
||||
defaultMessage: 'Clear link',
|
||||
},
|
||||
changesAppliedTo: {
|
||||
id: 'dashboard.projects.links.changes-applied',
|
||||
defaultMessage:
|
||||
'Changes will be applied to <strong>{count}</strong> {count, plural, one {project} other {projects}}.',
|
||||
},
|
||||
andMore: {
|
||||
id: 'dashboard.projects.links.and-more',
|
||||
defaultMessage: 'and {count} more...',
|
||||
},
|
||||
showAllProjects: {
|
||||
id: 'dashboard.projects.links.show-all-projects',
|
||||
defaultMessage: 'Show all projects',
|
||||
},
|
||||
noProjectsYet: {
|
||||
id: 'dashboard.projects.empty',
|
||||
defaultMessage: "You don't have any projects yet. Click the green button above to begin.",
|
||||
},
|
||||
bulkEditHint: {
|
||||
id: 'dashboard.projects.bulk-edit-hint',
|
||||
defaultMessage: 'You can edit multiple projects at once by selecting them below.',
|
||||
},
|
||||
ascending: {
|
||||
id: 'dashboard.projects.sort.ascending',
|
||||
defaultMessage: 'Ascending',
|
||||
},
|
||||
descending: {
|
||||
id: 'dashboard.projects.sort.descending',
|
||||
defaultMessage: 'Descending',
|
||||
},
|
||||
sortOptionName: {
|
||||
id: 'dashboard.projects.sort.option.name',
|
||||
defaultMessage: 'Name',
|
||||
},
|
||||
sortOptionStatus: {
|
||||
id: 'dashboard.projects.sort.option.status',
|
||||
defaultMessage: 'Status',
|
||||
},
|
||||
sortOptionType: {
|
||||
id: 'dashboard.projects.sort.option.type',
|
||||
defaultMessage: 'Type',
|
||||
},
|
||||
iconHeader: {
|
||||
id: 'dashboard.projects.table.icon',
|
||||
defaultMessage: 'Icon',
|
||||
},
|
||||
nameHeader: {
|
||||
id: 'dashboard.projects.table.name',
|
||||
defaultMessage: 'Name',
|
||||
},
|
||||
idHeader: {
|
||||
id: 'dashboard.projects.table.id',
|
||||
defaultMessage: 'ID',
|
||||
},
|
||||
typeHeader: {
|
||||
id: 'dashboard.projects.table.type',
|
||||
defaultMessage: 'Type',
|
||||
},
|
||||
statusHeader: {
|
||||
id: 'dashboard.projects.table.status',
|
||||
defaultMessage: 'Status',
|
||||
},
|
||||
projectIconAlt: {
|
||||
id: 'dashboard.projects.project.icon-alt',
|
||||
defaultMessage: 'Icon for {title}',
|
||||
},
|
||||
projectModeratorMessageAriaLabel: {
|
||||
id: 'dashboard.projects.project.moderator-message-aria',
|
||||
defaultMessage: 'Project has a message from the moderators. View the project to see more.',
|
||||
},
|
||||
reviewEnvironmentMetadata: {
|
||||
id: 'dashboard.projects.project.review-environment-metadata',
|
||||
defaultMessage: 'Please review environment metadata',
|
||||
},
|
||||
serverBulkEditDisabled: {
|
||||
id: 'dashboard.projects.bulk-edit.server-disabled',
|
||||
defaultMessage: 'Server projects do not support bulk editing',
|
||||
},
|
||||
bulkEditSuccessText: {
|
||||
id: 'dashboard.projects.notification.bulk-edit-success',
|
||||
defaultMessage: "Bulk edited selected project's links.",
|
||||
},
|
||||
})
|
||||
|
||||
useHead({ title: () => `${formatMessage(messages.headTitle)} - Modrinth` })
|
||||
|
||||
const user = await useUser()
|
||||
const projects = ref([])
|
||||
const projectsWithMigrationWarning = ref([])
|
||||
const selectedProjects = ref([])
|
||||
const sortBy = ref('Name')
|
||||
const sortOptions = [
|
||||
{ value: 'Name', label: 'Name' },
|
||||
{ value: 'Status', label: 'Status' },
|
||||
{ value: 'Type', label: 'Type' },
|
||||
]
|
||||
const sortOptions = computed(() => [
|
||||
{ value: 'Name', label: formatMessage(messages.sortOptionName) },
|
||||
{ value: 'Status', label: formatMessage(messages.sortOptionStatus) },
|
||||
{ value: 'Type', label: formatMessage(messages.sortOptionType) },
|
||||
])
|
||||
const descending = ref(false)
|
||||
const editLinks = reactive({
|
||||
showAffected: false,
|
||||
@@ -370,6 +503,16 @@ const editLinks = reactive({
|
||||
const editLinksModal = ref(null)
|
||||
const modal_creation = ref(null)
|
||||
|
||||
function getLinkInputPlaceholder(clearLink, isDiscord = false) {
|
||||
if (clearLink) {
|
||||
return formatMessage(messages.existingLinkWillBeCleared)
|
||||
}
|
||||
|
||||
return isDiscord
|
||||
? formatMessage(messages.enterValidDiscordInviteUrl)
|
||||
: formatMessage(messages.enterValidUrl)
|
||||
}
|
||||
|
||||
function isProjectBulkEditDisabled(project) {
|
||||
return (
|
||||
(project.permissions & EDIT_DETAILS) === EDIT_DETAILS ||
|
||||
@@ -408,7 +551,7 @@ function toggleProjectSelection(project) {
|
||||
|
||||
function getBulkEditDisabledTooltip(project) {
|
||||
if (project.project_type === 'minecraft_java_server') {
|
||||
return 'Server projects do not support bulk editing'
|
||||
return formatMessage(messages.serverBulkEditDisabled)
|
||||
}
|
||||
|
||||
return ''
|
||||
@@ -467,8 +610,8 @@ async function bulkEditLinks() {
|
||||
|
||||
editLinksModal.value?.hide()
|
||||
addNotification({
|
||||
title: 'Success',
|
||||
text: "Bulk edited selected project's links.",
|
||||
title: formatMessage(commonMessages.successLabel),
|
||||
text: formatMessage(messages.bulkEditSuccessText),
|
||||
type: 'success',
|
||||
})
|
||||
selectedProjects.value = []
|
||||
@@ -483,7 +626,7 @@ async function bulkEditLinks() {
|
||||
editLinks.discord.clear = false
|
||||
} catch (e) {
|
||||
addNotification({
|
||||
title: 'An error occurred',
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: e,
|
||||
type: 'error',
|
||||
})
|
||||
|
||||
@@ -2,16 +2,33 @@
|
||||
<ReportView
|
||||
:auth="auth"
|
||||
:report-id="route.params.id"
|
||||
:breadcrumbs-stack="[{ href: '/dashboard/reports', label: 'Active reports' }]"
|
||||
:breadcrumbs-stack="[
|
||||
{ href: '/dashboard/reports', label: formatMessage(messages.activeReportsTitle) },
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { defineMessages, useVIntl } from '@modrinth/ui'
|
||||
|
||||
import ReportView from '~/components/ui/report/ReportView.vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
activeReportsTitle: {
|
||||
id: 'dashboard.reports.active-title',
|
||||
defaultMessage: 'Active reports',
|
||||
},
|
||||
reportTitle: {
|
||||
id: 'dashboard.report.title',
|
||||
defaultMessage: 'Report {id}',
|
||||
},
|
||||
})
|
||||
|
||||
const route = useNativeRoute()
|
||||
const auth = await useAuth()
|
||||
|
||||
useHead({
|
||||
title: `Report ${route.params.id} - Modrinth`,
|
||||
title: () => `${formatMessage(messages.reportTitle, { id: route.params.id })} - Modrinth`,
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<section class="universal-card">
|
||||
<h2 class="text-2xl">Reports</h2>
|
||||
<h2 class="text-2xl">{{ formatMessage(messages.reportsTitle) }}</h2>
|
||||
<ReportsList :auth="auth" />
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { defineMessages, useVIntl } from '@modrinth/ui'
|
||||
|
||||
import ReportsList from '~/components/ui/report/ReportsList.vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const messages = defineMessages({
|
||||
reportsTitle: {
|
||||
id: 'dashboard.reports.title',
|
||||
defaultMessage: 'Reports',
|
||||
},
|
||||
activeReportsTitle: {
|
||||
id: 'dashboard.reports.active-title',
|
||||
defaultMessage: 'Active reports',
|
||||
},
|
||||
})
|
||||
|
||||
const auth = await useAuth()
|
||||
useHead({
|
||||
title: 'Active reports - Modrinth',
|
||||
title: () => `${formatMessage(messages.activeReportsTitle)} - Modrinth`,
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -298,8 +298,14 @@ async function openWithdrawModal() {
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
balanceLabel: { id: 'dashboard.revenue.balance', defaultMessage: 'Balance' },
|
||||
availableNow: { id: 'dashboard.revenue.available-now', defaultMessage: 'Available now' },
|
||||
balanceLabel: {
|
||||
id: 'dashboard.revenue.balance',
|
||||
defaultMessage: 'Balance',
|
||||
},
|
||||
availableNow: {
|
||||
id: 'dashboard.revenue.available-now',
|
||||
defaultMessage: 'Available now',
|
||||
},
|
||||
estimatedWithDate: {
|
||||
id: 'dashboard.revenue.estimated-with-date',
|
||||
defaultMessage: 'Estimated {date}',
|
||||
@@ -312,14 +318,23 @@ const messages = defineMessages({
|
||||
id: 'dashboard.revenue.estimated-tooltip.msg2',
|
||||
defaultMessage: 'Click to read about how Modrinth handles your revenue.',
|
||||
},
|
||||
processing: { id: 'dashboard.revenue.processing', defaultMessage: 'Processing' },
|
||||
processing: {
|
||||
id: 'dashboard.revenue.processing',
|
||||
defaultMessage: 'Processing',
|
||||
},
|
||||
processingTooltip: {
|
||||
id: 'dashboard.revenue.processing.tooltip',
|
||||
defaultMessage:
|
||||
'Revenue stays in processing until the end of the month, then becomes available 60 days later.',
|
||||
},
|
||||
withdrawHeader: { id: 'dashboard.revenue.withdraw.header', defaultMessage: 'Withdraw' },
|
||||
withdrawCardTitle: { id: 'dashboard.revenue.withdraw.card.title', defaultMessage: 'Withdraw' },
|
||||
withdrawHeader: {
|
||||
id: 'dashboard.revenue.withdraw.header',
|
||||
defaultMessage: 'Withdraw',
|
||||
},
|
||||
withdrawCardTitle: {
|
||||
id: 'dashboard.revenue.withdraw.card.title',
|
||||
defaultMessage: 'Withdraw',
|
||||
},
|
||||
withdrawCardDescription: {
|
||||
id: 'dashboard.revenue.withdraw.card.description',
|
||||
defaultMessage: 'Withdraw from your available balance to any payout method.',
|
||||
@@ -338,7 +353,10 @@ const messages = defineMessages({
|
||||
id: 'dashboard.revenue.transactions.header',
|
||||
defaultMessage: 'Transactions',
|
||||
},
|
||||
seeAll: { id: 'dashboard.revenue.transactions.see-all', defaultMessage: 'See all' },
|
||||
seeAll: {
|
||||
id: 'dashboard.revenue.transactions.see-all',
|
||||
defaultMessage: 'See all',
|
||||
},
|
||||
noTransactions: {
|
||||
id: 'dashboard.revenue.transactions.none',
|
||||
defaultMessage: 'No transactions',
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
<Combobox
|
||||
v-model="selectedYear"
|
||||
:options="yearOptions"
|
||||
:display-value="selectedYear === 'all' ? 'All years' : String(selectedYear)"
|
||||
:display-value="
|
||||
selectedYear === 'all' ? formatMessage(messages.allYears) : String(selectedYear)
|
||||
"
|
||||
listbox
|
||||
/>
|
||||
<ButtonStyled circular>
|
||||
@@ -116,7 +118,7 @@ const client = injectModrinthClient()
|
||||
const generatedState = useGeneratedState()
|
||||
|
||||
useHead({
|
||||
title: 'Transaction history - Modrinth',
|
||||
title: () => `${formatMessage(messages.headTitle)} - Modrinth`,
|
||||
})
|
||||
|
||||
const { data: transactions, refetch } = useQuery({
|
||||
@@ -143,7 +145,7 @@ const yearOptions = computed(() => {
|
||||
|
||||
return yearValues.map((year) => ({
|
||||
value: year,
|
||||
label: year === 'all' ? 'All years' : String(year),
|
||||
label: year === 'all' ? formatMessage(messages.allYears) : String(year),
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -161,9 +163,9 @@ function getPeriodLabel(date) {
|
||||
const now = dayjs()
|
||||
|
||||
if (txnDate.isSame(now, 'month')) {
|
||||
return 'This month'
|
||||
return formatMessage(messages.thisMonth)
|
||||
} else if (txnDate.isSame(now.subtract(1, 'month'), 'month')) {
|
||||
return 'Last month'
|
||||
return formatMessage(messages.lastMonth)
|
||||
} else {
|
||||
return capitalizeString(formatMonth(txnDate.toDate()))
|
||||
}
|
||||
@@ -322,6 +324,10 @@ const messages = defineMessages({
|
||||
id: 'dashboard.revenue.transactions.header',
|
||||
defaultMessage: 'Transactions',
|
||||
},
|
||||
headTitle: {
|
||||
id: 'dashboard.revenue.transactions.head-title',
|
||||
defaultMessage: 'Transaction history',
|
||||
},
|
||||
received: {
|
||||
id: 'dashboard.revenue.stats.received',
|
||||
defaultMessage: 'Received',
|
||||
@@ -346,5 +352,17 @@ const messages = defineMessages({
|
||||
id: 'dashboard.revenue.transactions.btn.download-csv',
|
||||
defaultMessage: 'Download as CSV',
|
||||
},
|
||||
allYears: {
|
||||
id: 'dashboard.revenue.transactions.year.all',
|
||||
defaultMessage: 'All years',
|
||||
},
|
||||
thisMonth: {
|
||||
id: 'dashboard.revenue.transactions.period.this-month',
|
||||
defaultMessage: 'This month',
|
||||
},
|
||||
lastMonth: {
|
||||
id: 'dashboard.revenue.transactions.period.last-month',
|
||||
defaultMessage: 'Last month',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
"vars": {
|
||||
"ENVIRONMENT": "staging",
|
||||
"SENTRY_ENVIRONMENT": "staging",
|
||||
"BASE_URL": "https://api.modrinth.com/v2/",
|
||||
"BROWSER_BASE_URL": "https://api.modrinth.com/v2/",
|
||||
"BASE_URL": "https://staging-api.modrinth.com/v2/",
|
||||
"BROWSER_BASE_URL": "https://staging-api.modrinth.com/v2/",
|
||||
"PYRO_BASE_URL": "https://staging-archon.modrinth.com/",
|
||||
"STRIPE_PUBLISHABLE_KEY": "pk_test_51JbFxJJygY5LJFfKV50mnXzz3YLvBVe2Gd1jn7ljWAkaBlRz3VQdxN9mXcPSrFbSqxwAb0svte9yhnsmm7qHfcWn00R611Ce7b"
|
||||
},
|
||||
|
||||
Generated
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT m.id mod_id, u.username, u.id uid\n FROM mods m\n INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = m.team_id\n INNER JOIN users u ON u.id = tm.user_id\n WHERE m.id = ANY($1)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "mod_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "uid",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "654ca85d28573de9b532d0768e5250baa3a5f200fcf9ae990575073d0a1043e0"
|
||||
}
|
||||
Generated
-28
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT m.id mod_id, u.username\n FROM mods m\n INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = m.team_id\n INNER JOIN users u ON u.id = tm.user_id\n WHERE m.id = ANY($1)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "mod_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "80734c33c16aeacca980cf40070bac035931a0bab8c0d0cf63888c8e5616f847"
|
||||
}
|
||||
Generated
+46
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT m.id mod_id, u.username, u.id uid, o.name orgname, o.id oid\n FROM mods m\n INNER JOIN organizations o ON o.id = m.organization_id\n INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = o.team_id\n INNER JOIN users u ON u.id = tm.user_id\n WHERE m.id = ANY($1)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "mod_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "uid",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "orgname",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "oid",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "81b65298ddf4c58b884b15e64f1f4f5aa006944c05980a5b5da366135d17c912"
|
||||
}
|
||||
Generated
-28
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT m.id mod_id, u.username\n FROM mods m\n INNER JOIN organizations o ON o.id = m.organization_id\n INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = o.team_id\n INNER JOIN users u ON u.id = tm.user_id\n WHERE m.id = ANY($1)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "mod_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e50e308826d1e7fa54cade7daf8120b4ae4068bd086dc08f572b33cfc2476354"
|
||||
}
|
||||
@@ -80,6 +80,7 @@ murmur2 = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
path-util = { workspace = true }
|
||||
prometheus = { workspace = true }
|
||||
quick-xml = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
redis = { workspace = true, features = ["ahash", "r2d2", "tokio-comp"] }
|
||||
|
||||
@@ -9,6 +9,9 @@ use crate::env::ENV;
|
||||
use crate::queue::server_ping;
|
||||
use crate::routes::analytics::MINECRAFT_SERVER_PLAYS;
|
||||
|
||||
pub const DOWNLOADS: &str = "downloads";
|
||||
pub const PLAYTIME: &str = "playtime";
|
||||
|
||||
pub async fn init_client() -> clickhouse::error::Result<clickhouse::Client> {
|
||||
init_client_with_database(&ENV.CLICKHOUSE_DATABASE).await
|
||||
}
|
||||
@@ -90,7 +93,7 @@ pub async fn init_client_with_database(
|
||||
client
|
||||
.query(&format!(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS {database}.downloads {cluster_line}
|
||||
CREATE TABLE IF NOT EXISTS {database}.{DOWNLOADS} {cluster_line}
|
||||
(
|
||||
recorded DateTime64(4),
|
||||
domain String,
|
||||
@@ -117,7 +120,7 @@ pub async fn init_client_with_database(
|
||||
client
|
||||
.query(&format!(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS {database}.playtime {cluster_line}
|
||||
CREATE TABLE IF NOT EXISTS {database}.{PLAYTIME} {cluster_line}
|
||||
(
|
||||
recorded DateTime64(4),
|
||||
seconds UInt64,
|
||||
@@ -238,5 +241,27 @@ pub async fn init_client_with_database(
|
||||
.execute()
|
||||
.await?;
|
||||
|
||||
client
|
||||
.query(&format!(
|
||||
"
|
||||
ALTER TABLE {database}.{DOWNLOADS} {cluster_line}
|
||||
ADD COLUMN IF NOT EXISTS reason String,
|
||||
ADD COLUMN IF NOT EXISTS game_version String,
|
||||
ADD COLUMN IF NOT EXISTS loader String
|
||||
"
|
||||
))
|
||||
.execute()
|
||||
.await?;
|
||||
|
||||
client
|
||||
.query(&format!(
|
||||
"
|
||||
ALTER TABLE {database}.{PLAYTIME} {cluster_line}
|
||||
ADD COLUMN IF NOT EXISTS country String
|
||||
"
|
||||
))
|
||||
.execute()
|
||||
.await?;
|
||||
|
||||
Ok(client.with_database(database))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ pub struct LegacyResultSearchProject {
|
||||
pub project_type: String,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
#[serde(default)]
|
||||
pub author_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub organization: Option<String>,
|
||||
#[serde(default)]
|
||||
pub organization_id: Option<String>,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub categories: Vec<String>,
|
||||
@@ -139,6 +145,9 @@ impl LegacyResultSearchProject {
|
||||
project_id: result_search_project.project_id,
|
||||
slug: result_search_project.slug,
|
||||
author: result_search_project.author,
|
||||
author_id: result_search_project.author_id,
|
||||
organization: result_search_project.organization,
|
||||
organization_id: result_search_project.organization_id,
|
||||
title: result_search_project.name,
|
||||
description: result_search_project.summary,
|
||||
display_categories,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use clickhouse::Row;
|
||||
use derive_more::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::hash::Hash;
|
||||
use std::net::Ipv6Addr;
|
||||
@@ -23,6 +24,27 @@ pub struct Download {
|
||||
pub country: String,
|
||||
pub user_agent: String,
|
||||
pub headers: Vec<(String, String)>,
|
||||
|
||||
// added retroactively - may be empty
|
||||
pub reason: String,
|
||||
pub game_version: String,
|
||||
pub loader: String,
|
||||
}
|
||||
|
||||
/// Why a project was downloaded.
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[display(rename_all = "snake_case")]
|
||||
pub enum DownloadReason {
|
||||
/// Project was downloaded directly by the user.
|
||||
Standalone,
|
||||
/// Project was downloaded as a dependency, possibly transitive, of another
|
||||
/// project.
|
||||
Dependency,
|
||||
/// Project was downloaded as part of a modpack.
|
||||
Modpack,
|
||||
}
|
||||
|
||||
#[derive(Debug, Row, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
|
||||
@@ -77,6 +99,9 @@ pub struct Playtime {
|
||||
pub game_version: String,
|
||||
/// Parent modpack this playtime was recorded in
|
||||
pub parent: u64,
|
||||
|
||||
// added retroactively - may be empty
|
||||
pub country: String,
|
||||
}
|
||||
|
||||
#[derive(Row, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::routes::analytics::MINECRAFT_SERVER_PLAYS;
|
||||
use dashmap::{DashMap, DashSet};
|
||||
use redis::cmd;
|
||||
use std::collections::HashMap;
|
||||
use tracing::trace;
|
||||
|
||||
pub mod cache;
|
||||
|
||||
@@ -262,6 +263,7 @@ impl AnalyticsQueue {
|
||||
}
|
||||
|
||||
if !downloads_queue.is_empty() {
|
||||
let downloads_count = downloads_queue.len();
|
||||
let mut downloads_keys = Vec::new();
|
||||
let raw_downloads = DashMap::new();
|
||||
|
||||
@@ -319,6 +321,11 @@ impl AnalyticsQueue {
|
||||
let mut version_downloads: HashMap<i64, i32> = HashMap::new();
|
||||
let mut project_downloads: HashMap<i64, i32> = HashMap::new();
|
||||
|
||||
trace!(
|
||||
"inserting {} raw downloads out of {downloads_count} downloads",
|
||||
raw_downloads.len()
|
||||
);
|
||||
|
||||
for (_, download) in raw_downloads {
|
||||
*version_downloads
|
||||
.entry(download.version_id as i64)
|
||||
@@ -327,6 +334,8 @@ impl AnalyticsQueue {
|
||||
.entry(download.project_id as i64)
|
||||
.or_default() += 1;
|
||||
|
||||
trace!("writing download {download:?}");
|
||||
|
||||
downloads.write(&download).await?;
|
||||
}
|
||||
|
||||
|
||||
@@ -214,6 +214,8 @@ async fn playtime_ingest(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let headers = req.headers();
|
||||
|
||||
for (id, playtime) in playtimes {
|
||||
if playtime.seconds > 300 {
|
||||
continue;
|
||||
@@ -230,6 +232,10 @@ async fn playtime_ingest(
|
||||
loader: playtime.loader,
|
||||
game_version: playtime.game_version,
|
||||
parent: playtime.parent.map_or(0, |x| x.0),
|
||||
country: headers
|
||||
.get("cf-ipcountry")
|
||||
.and_then(|c| c.to_str().map(|s| s.to_string()).ok())
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::auth::validate::get_user_record_from_bearer_token;
|
||||
use crate::database::PgPool;
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::models::analytics::Download;
|
||||
use crate::models::analytics::{Download, DownloadReason};
|
||||
use crate::models::ids::ProjectId;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::queue::analytics::AnalyticsQueue;
|
||||
@@ -16,6 +16,7 @@ use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use tracing::trace;
|
||||
|
||||
pub fn config(cfg: &mut utoipa_actix_web::service_config::ServiceConfig) {
|
||||
cfg.service(
|
||||
@@ -35,6 +36,17 @@ pub struct DownloadBody {
|
||||
pub headers: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// Extra data attached to each download request, transmitted through the
|
||||
/// [`DOWNLOAD_META_HEADER`] header.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct DownloadMeta {
|
||||
pub reason: DownloadReason,
|
||||
pub game_version: String,
|
||||
pub loader: String,
|
||||
}
|
||||
|
||||
pub const DOWNLOAD_META_HEADER: &str = "modrinth-download-meta";
|
||||
|
||||
// This is an internal route, cannot be used without key
|
||||
#[utoipa::path(
|
||||
patch,
|
||||
@@ -118,7 +130,16 @@ pub async fn count_download(
|
||||
let ip = crate::util::ip::convert_to_ip_v6(&download_body.ip)
|
||||
.unwrap_or_else(|_| Ipv4Addr::new(127, 0, 0, 1).to_ipv6_mapped());
|
||||
|
||||
analytics_queue.add_download(Download {
|
||||
let meta =
|
||||
if let Some(meta) = download_body.headers.get(DOWNLOAD_META_HEADER) {
|
||||
serde_json::from_str::<DownloadMeta>(meta)
|
||||
.map(Some)
|
||||
.wrap_request_err("invalid download meta")?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let download = Download {
|
||||
recorded: get_current_tenths_of_ms(),
|
||||
domain: url.host_str().unwrap_or_default().to_string(),
|
||||
site_path: url.path().to_string(),
|
||||
@@ -153,7 +174,19 @@ pub async fn count_download(
|
||||
.contains(&&*x.0.to_lowercase())
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
reason: meta
|
||||
.as_ref()
|
||||
.map(|m| m.reason.to_string())
|
||||
.unwrap_or_default(),
|
||||
game_version: meta
|
||||
.as_ref()
|
||||
.map(|m| m.game_version.clone())
|
||||
.unwrap_or_default(),
|
||||
loader: meta.as_ref().map(|m| m.loader.clone()).unwrap_or_default(),
|
||||
};
|
||||
trace!("added download {download:#?}");
|
||||
|
||||
analytics_queue.add_download(download);
|
||||
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub(crate) mod admin;
|
||||
pub mod admin;
|
||||
pub mod affiliate;
|
||||
pub mod billing;
|
||||
pub mod delphi;
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use crate::{auth::get_user_from_headers, database};
|
||||
use actix_web::{HttpRequest, HttpResponse, get, route, web};
|
||||
use quick_xml::escape::escape;
|
||||
use std::collections::HashSet;
|
||||
use yaserde::YaSerialize;
|
||||
|
||||
@@ -329,7 +330,7 @@ pub async fn version_file(
|
||||
artifact_id: project_id,
|
||||
version: vnum,
|
||||
name: project.inner.name,
|
||||
description: project.inner.description,
|
||||
description: escape(project.inner.summary).into_owned(),
|
||||
};
|
||||
return Ok(HttpResponse::Ok()
|
||||
.content_type("text/xml")
|
||||
|
||||
@@ -296,7 +296,7 @@ pub struct EditTeamMember {
|
||||
#[patch("/{id}/members/{user_id}")]
|
||||
pub async fn edit_team_member(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(TeamId, UserId)>,
|
||||
info: web::Path<(TeamId, String)>,
|
||||
pool: web::Data<PgPool>,
|
||||
edit_member: web::Json<EditTeamMember>,
|
||||
redis: web::Data<RedisPool>,
|
||||
|
||||
@@ -54,7 +54,13 @@ pub struct GetRequest {
|
||||
/// What time range to return statistics for.
|
||||
pub time_range: TimeRange,
|
||||
/// What analytics metrics to return data for.
|
||||
#[serde(default)]
|
||||
pub return_metrics: ReturnMetrics,
|
||||
/// What project IDs to return data for.
|
||||
///
|
||||
/// If this is empty, all of the user's projects will be included.
|
||||
#[serde(default)]
|
||||
pub project_ids: Vec<ProjectId>,
|
||||
}
|
||||
|
||||
/// Time range for fetching analytics.
|
||||
@@ -108,10 +114,6 @@ pub struct ReturnMetrics {
|
||||
pub affiliate_code_revenue: Option<Metrics<AffiliateCodeRevenueField>>,
|
||||
}
|
||||
|
||||
/// Replacement for `()` because of a `utoipa` limitation.
|
||||
#[derive(Debug, Default, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct Unit {}
|
||||
|
||||
/// See [`ReturnMetrics`].
|
||||
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct Metrics<F> {
|
||||
@@ -612,9 +614,16 @@ pub async fn fetch_analytics(
|
||||
|
||||
let mut time_slices = vec![TimeSlice::default(); num_time_slices];
|
||||
|
||||
// TODO fetch from req
|
||||
let project_ids =
|
||||
DBUser::get_projects(user.id.into(), &**pool, &redis).await?;
|
||||
let project_ids = {
|
||||
if req.project_ids.is_empty() {
|
||||
DBUser::get_projects(user.id.into(), &**pool, &redis).await?
|
||||
} else {
|
||||
req.project_ids
|
||||
.iter()
|
||||
.map(|id| DBProjectId::from(*id))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
};
|
||||
|
||||
let project_ids =
|
||||
filter_allowed_project_ids(&project_ids, &user, &pool, &redis).await?;
|
||||
|
||||
@@ -12,9 +12,9 @@ use crate::models::pats::Scopes;
|
||||
use crate::models::teams::{OrganizationPermissions, ProjectPermissions};
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use crate::util::error::Context;
|
||||
use actix_web::{HttpRequest, HttpResponse, get, web};
|
||||
use ariadne::ids::UserId;
|
||||
use eyre::eyre;
|
||||
use rust_decimal::Decimal;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -689,7 +689,7 @@ pub struct EditTeamMember {
|
||||
|
||||
pub async fn edit_team_member(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(TeamId, UserId)>,
|
||||
info: web::Path<(TeamId, String)>,
|
||||
pool: web::Data<PgPool>,
|
||||
edit_member: web::Json<EditTeamMember>,
|
||||
redis: web::Data<RedisPool>,
|
||||
@@ -697,7 +697,12 @@ pub async fn edit_team_member(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let ids = info.into_inner();
|
||||
let id = ids.0.into();
|
||||
let user_id = ids.1.into();
|
||||
|
||||
let user_id = DBUser::get(&ids.1, &**pool, &redis)
|
||||
.await
|
||||
.wrap_internal_err("failed to fetch the specified user")?
|
||||
.wrap_request_err("the specified user does not exist")?
|
||||
.id;
|
||||
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
@@ -709,24 +714,21 @@ pub async fn edit_team_member(
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let team_association =
|
||||
DBTeam::get_association(id, &**pool).await?.ok_or_else(|| {
|
||||
ApiError::InvalidInput(
|
||||
"The team specified does not exist".to_string(),
|
||||
)
|
||||
})?;
|
||||
let team_association = DBTeam::get_association(id, &**pool)
|
||||
.await
|
||||
.wrap_internal_err("failed to fetch the specified team")?
|
||||
.wrap_request_err("the specified team does not exist")?;
|
||||
let member =
|
||||
DBTeamMember::get_from_user_id(id, current_user.id.into(), &**pool)
|
||||
.await?;
|
||||
let edit_member_db =
|
||||
DBTeamMember::get_from_user_id_pending(id, user_id, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::Request(eyre!(
|
||||
"This member does not exist in this team - \
|
||||
the member must first be created via `POST`"
|
||||
))
|
||||
})?;
|
||||
.await
|
||||
.wrap_internal_err("failed to fetch team member")?
|
||||
.wrap_request_err(
|
||||
"this member does not exist in this team - \
|
||||
the member must first be created via `POST`",
|
||||
)?;
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ use crate::database::models::loader_fields::{
|
||||
VersionField,
|
||||
};
|
||||
use crate::database::models::{
|
||||
DBProjectId, DBVersionId, LoaderFieldEnumId, LoaderFieldEnumValueId,
|
||||
LoaderFieldId,
|
||||
DBOrganizationId, DBProjectId, DBUserId, DBVersionId, LoaderFieldEnumId,
|
||||
LoaderFieldEnumValueId, LoaderFieldId,
|
||||
};
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::models::exp;
|
||||
@@ -34,6 +34,13 @@ fn normalize_for_search(s: &str) -> String {
|
||||
SPECIAL_CHARS_RE.replace_all(s, "").to_kebab_case()
|
||||
}
|
||||
|
||||
struct ProjectOwner {
|
||||
username: String,
|
||||
user_id: DBUserId,
|
||||
org_name: Option<String>,
|
||||
org_id: Option<DBOrganizationId>,
|
||||
}
|
||||
|
||||
pub async fn index_local(
|
||||
pool: &PgPool,
|
||||
redis: &RedisPool,
|
||||
@@ -171,9 +178,9 @@ pub async fn index_local(
|
||||
|
||||
info!("Indexing local org owners!");
|
||||
|
||||
let mods_org_owners: DashMap<DBProjectId, String> = sqlx::query!(
|
||||
let mods_org_owners: DashMap<DBProjectId, ProjectOwner> = sqlx::query!(
|
||||
"
|
||||
SELECT m.id mod_id, u.username
|
||||
SELECT m.id mod_id, u.username, u.id uid, o.name orgname, o.id oid
|
||||
FROM mods m
|
||||
INNER JOIN organizations o ON o.id = m.organization_id
|
||||
INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = o.team_id
|
||||
@@ -183,17 +190,22 @@ pub async fn index_local(
|
||||
&*project_ids,
|
||||
)
|
||||
.fetch(pool)
|
||||
.try_fold(DashMap::new(), |acc: DashMap<DBProjectId, String>, m| {
|
||||
acc.insert(DBProjectId(m.mod_id), m.username);
|
||||
.try_fold(DashMap::new(), |acc: DashMap<DBProjectId, ProjectOwner>, m| {
|
||||
acc.insert(DBProjectId(m.mod_id), ProjectOwner {
|
||||
username: m.username,
|
||||
user_id: DBUserId(m.uid),
|
||||
org_name: Some(m.orgname),
|
||||
org_id: Some(DBOrganizationId(m.oid)),
|
||||
});
|
||||
async move { Ok(acc) }
|
||||
})
|
||||
.await?;
|
||||
|
||||
info!("Indexing local team owners!");
|
||||
|
||||
let mods_team_owners: DashMap<DBProjectId, String> = sqlx::query!(
|
||||
let mods_team_owners: DashMap<DBProjectId, ProjectOwner> = sqlx::query!(
|
||||
"
|
||||
SELECT m.id mod_id, u.username
|
||||
SELECT m.id mod_id, u.username, u.id uid
|
||||
FROM mods m
|
||||
INNER JOIN team_members tm ON tm.is_owner = TRUE and tm.team_id = m.team_id
|
||||
INNER JOIN users u ON u.id = tm.user_id
|
||||
@@ -202,8 +214,13 @@ pub async fn index_local(
|
||||
&project_ids,
|
||||
)
|
||||
.fetch(pool)
|
||||
.try_fold(DashMap::new(), |acc: DashMap<DBProjectId, String>, m| {
|
||||
acc.insert(DBProjectId(m.mod_id), m.username);
|
||||
.try_fold(DashMap::new(), |acc: DashMap<DBProjectId, ProjectOwner>, m| {
|
||||
acc.insert(DBProjectId(m.mod_id), ProjectOwner {
|
||||
username: m.username,
|
||||
user_id: DBUserId(m.uid),
|
||||
org_name: None,
|
||||
org_id: None,
|
||||
});
|
||||
async move { Ok(acc) }
|
||||
})
|
||||
.await?;
|
||||
@@ -262,21 +279,24 @@ pub async fn index_local(
|
||||
if count % 1000 == 0 {
|
||||
info!("projects index prog: {count}/{total_len}");
|
||||
}
|
||||
|
||||
let owner =
|
||||
if let Some((_, org_owner)) = mods_org_owners.remove(&project.id) {
|
||||
org_owner
|
||||
} else if let Some((_, team_owner)) =
|
||||
mods_team_owners.remove(&project.id)
|
||||
{
|
||||
team_owner
|
||||
} else {
|
||||
warn!(
|
||||
"org owner not found for project {} id: {}!",
|
||||
project.name, project.id.0
|
||||
);
|
||||
continue;
|
||||
};
|
||||
let Some((
|
||||
_,
|
||||
ProjectOwner {
|
||||
username,
|
||||
user_id,
|
||||
org_name,
|
||||
org_id,
|
||||
},
|
||||
)) = mods_org_owners
|
||||
.remove(&project.id)
|
||||
.or_else(|| mods_team_owners.remove(&project.id))
|
||||
else {
|
||||
warn!(
|
||||
"org owner not found for project {} id: {}!",
|
||||
project.name, project.id.0
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let license = match project.license.split(' ').next() {
|
||||
Some(license) => license.to_string(),
|
||||
@@ -444,8 +464,13 @@ pub async fn index_local(
|
||||
downloads: project.downloads,
|
||||
log_downloads: (project.downloads.max(1) as f64).ln(),
|
||||
icon_url: project.icon_url.clone(),
|
||||
author: owner.clone(),
|
||||
indexed_author: normalize_for_search(&owner),
|
||||
author: username.clone(),
|
||||
author_id: ariadne::ids::UserId::from(user_id).to_string(),
|
||||
organization: org_name.clone(),
|
||||
organization_id: org_id.map(|e| {
|
||||
crate::models::ids::OrganizationId::from(e).to_string()
|
||||
}),
|
||||
indexed_author: normalize_for_search(&username),
|
||||
date_created: project.approved,
|
||||
created_timestamp: project.approved.timestamp(),
|
||||
date_modified: project.updated,
|
||||
|
||||
@@ -228,6 +228,9 @@ pub struct UploadSearchProject {
|
||||
pub project_types: Vec<String>,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
pub author_id: String,
|
||||
pub organization: Option<String>,
|
||||
pub organization_id: Option<String>,
|
||||
pub indexed_author: String,
|
||||
pub name: String,
|
||||
pub indexed_name: String,
|
||||
@@ -279,6 +282,12 @@ pub struct ResultSearchProject {
|
||||
pub project_types: Vec<String>,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
#[serde(default)]
|
||||
pub author_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub organization: Option<String>,
|
||||
#[serde(default)]
|
||||
pub organization_id: Option<String>,
|
||||
pub name: String,
|
||||
pub summary: String,
|
||||
pub categories: Vec<String>,
|
||||
@@ -315,6 +324,9 @@ impl From<UploadSearchProject> for ResultSearchProject {
|
||||
project_types: source.project_types,
|
||||
slug: source.slug,
|
||||
author: source.author,
|
||||
author_id: Some(source.author_id),
|
||||
organization: source.organization,
|
||||
organization_id: source.organization_id,
|
||||
name: source.name,
|
||||
summary: source.summary,
|
||||
categories: source.categories,
|
||||
|
||||
@@ -361,15 +361,15 @@ async fn test_lock_acquire_concurrent() {
|
||||
let pool1 = test_env.db.pool.clone();
|
||||
let pool2 = test_env.db.pool.clone();
|
||||
let barrier = Arc::new(Barrier::new(2));
|
||||
let ba = barrier.clone();
|
||||
let bb = barrier.clone();
|
||||
let barrier_a = barrier.clone();
|
||||
let barrier_b = barrier.clone();
|
||||
|
||||
let h1 = tokio::spawn(async move {
|
||||
ba.wait().await;
|
||||
barrier_a.wait().await;
|
||||
DBModerationLock::acquire(project_id, mod1, &pool1).await
|
||||
});
|
||||
let h2 = tokio::spawn(async move {
|
||||
bb.wait().await;
|
||||
barrier_b.wait().await;
|
||||
DBModerationLock::acquire(project_id, mod2, &pool2).await
|
||||
});
|
||||
let (o1, o2) = tokio::join!(h1, h2);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"build-storybook": "pnpm --filter @modrinth/ui build-storybook",
|
||||
"icons:add": "pnpm --filter @modrinth/assets icons:add",
|
||||
"changelog:collect": "node scripts/run.mjs collect-changelog",
|
||||
"changelog:combine-for-app": "node scripts/run.mjs build-theseus-release-notes",
|
||||
"scripts": "node scripts/run.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -31,3 +31,4 @@ export { XHRUploadClient } from './platform/xhr-upload-client'
|
||||
export { clearNodeAuthState, nodeAuthState, setNodeAuthState } from './state/node-auth'
|
||||
export * from './types'
|
||||
export { withJWTRetry } from './utils/jwt-retry'
|
||||
export type { Override, RawDecimal } from './utils/types'
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
import { AbstractModule } from '../../../core/abstract-module'
|
||||
import type { Override, RawDecimal } from '../../../utils/types'
|
||||
import type { Labrinth } from '../types'
|
||||
|
||||
type RawPayoutBalance = Override<
|
||||
Labrinth.Payout.v3.PayoutBalance,
|
||||
{
|
||||
available: RawDecimal
|
||||
withdrawn_lifetime: RawDecimal
|
||||
withdrawn_ytd: RawDecimal
|
||||
pending: RawDecimal
|
||||
dates: Record<string, RawDecimal>
|
||||
}
|
||||
>
|
||||
|
||||
type RawTransactionItem =
|
||||
| Override<
|
||||
Extract<Labrinth.Payout.v3.TransactionItem, { type: 'withdrawal' }>,
|
||||
{
|
||||
amount: RawDecimal
|
||||
fee: RawDecimal | null
|
||||
}
|
||||
>
|
||||
| Override<
|
||||
Extract<Labrinth.Payout.v3.TransactionItem, { type: 'payout_available' }>,
|
||||
{
|
||||
amount: RawDecimal
|
||||
}
|
||||
>
|
||||
|
||||
export class LabrinthPayoutV3Module extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
return 'labrinth_payout_v3'
|
||||
@@ -12,11 +39,22 @@ export class LabrinthPayoutV3Module extends AbstractModule {
|
||||
* @returns Promise resolving to the user's payout balance
|
||||
*/
|
||||
public async getBalance(): Promise<Labrinth.Payout.v3.PayoutBalance> {
|
||||
return this.client.request<Labrinth.Payout.v3.PayoutBalance>('/payout/balance', {
|
||||
const balance = await this.client.request<RawPayoutBalance>('/payout/balance', {
|
||||
api: 'labrinth',
|
||||
version: 3,
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
return {
|
||||
...balance,
|
||||
available: Number(balance.available),
|
||||
withdrawn_lifetime: Number(balance.withdrawn_lifetime),
|
||||
withdrawn_ytd: Number(balance.withdrawn_ytd),
|
||||
pending: Number(balance.pending),
|
||||
dates: Object.fromEntries(
|
||||
Object.entries(balance.dates).map(([date, amount]) => [date, Number(amount)]),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,11 +63,26 @@ export class LabrinthPayoutV3Module extends AbstractModule {
|
||||
* @returns Promise resolving to an array of transaction items
|
||||
*/
|
||||
public async getHistory(): Promise<Labrinth.Payout.v3.TransactionItem[]> {
|
||||
return this.client.request<Labrinth.Payout.v3.TransactionItem[]>('/payout/history', {
|
||||
const history = await this.client.request<RawTransactionItem[]>('/payout/history', {
|
||||
api: 'labrinth',
|
||||
version: 3,
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
return history.map((transaction) => {
|
||||
if (transaction.type === 'withdrawal') {
|
||||
return {
|
||||
...transaction,
|
||||
amount: Number(transaction.amount),
|
||||
fee: transaction.fee === null ? null : Number(transaction.fee),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...transaction,
|
||||
amount: Number(transaction.amount),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1133,6 +1133,9 @@ export namespace Labrinth {
|
||||
project_type: string
|
||||
slug: string | null
|
||||
author: string
|
||||
author_id: string | null
|
||||
organization: string | null
|
||||
organization_id: string | null
|
||||
title: string
|
||||
description: string
|
||||
categories: string[]
|
||||
@@ -1167,6 +1170,9 @@ export namespace Labrinth {
|
||||
project_types: string[]
|
||||
slug: string | null
|
||||
author: string
|
||||
author_id: string | null
|
||||
organization: string | null
|
||||
organization_id: string | null
|
||||
name: string
|
||||
summary: string
|
||||
categories: string[]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export type Override<T, R> = Omit<T, keyof R> & R
|
||||
export type RawDecimal = string | number
|
||||
@@ -4,11 +4,11 @@ use reqwest::StatusCode;
|
||||
|
||||
use crate::State;
|
||||
use crate::state::{Credentials, MinecraftLoginFlow};
|
||||
use crate::util::fetch::REQWEST_CLIENT;
|
||||
use crate::util::fetch::INSECURE_REQWEST_CLIENT;
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn check_reachable() -> crate::Result<()> {
|
||||
let resp = REQWEST_CLIENT
|
||||
let resp = INSECURE_REQWEST_CLIENT
|
||||
.get("https://sessionserver.mojang.com/session/minecraft/hasJoined")
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
@@ -14,7 +14,7 @@ use tokio_util::compat::FuturesAsyncReadCompatExt;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
ErrorKind, minecraft_skins::UrlOrBlob, util::fetch::REQWEST_CLIENT,
|
||||
ErrorKind, minecraft_skins::UrlOrBlob, util::fetch::INSECURE_REQWEST_CLIENT,
|
||||
};
|
||||
|
||||
pub async fn url_to_data_stream(
|
||||
@@ -25,7 +25,7 @@ pub async fn url_to_data_stream(
|
||||
|
||||
Ok(Either::Left(stream::once(async { Ok(data) })))
|
||||
} else {
|
||||
let response = REQWEST_CLIENT
|
||||
let response = INSECURE_REQWEST_CLIENT
|
||||
.get(url.as_str())
|
||||
.header("Accept", "image/png")
|
||||
.send()
|
||||
|
||||
@@ -863,7 +863,7 @@ async fn run_credentials(
|
||||
if !project_id.trim().is_empty() {
|
||||
let server_id = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let join_result = fetch::REQWEST_CLIENT
|
||||
let join_result = fetch::INSECURE_REQWEST_CLIENT
|
||||
.post("https://sessionserver.mojang.com/session/minecraft/join")
|
||||
.json(&json!({
|
||||
"accessToken": &credentials.access_token,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::ErrorKind;
|
||||
use crate::util::fetch::REQWEST_CLIENT;
|
||||
use crate::util::fetch::INSECURE_REQWEST_CLIENT;
|
||||
use base64::Engine;
|
||||
use base64::prelude::{BASE64_STANDARD, BASE64_URL_SAFE_NO_PAD};
|
||||
use chrono::{DateTime, Duration, TimeZone, Utc};
|
||||
@@ -855,7 +855,7 @@ async fn oauth_token(
|
||||
query.insert("scope", REQUESTED_SCOPE);
|
||||
|
||||
let res = auth_retry(|| {
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.post("https://login.live.com/oauth20_token.srf")
|
||||
.header("Accept", "application/json")
|
||||
.form(&query)
|
||||
@@ -903,7 +903,7 @@ async fn oauth_refresh(
|
||||
query.insert("scope", REQUESTED_SCOPE);
|
||||
|
||||
let res = auth_retry(|| {
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.post("https://login.live.com/oauth20_token.srf")
|
||||
.header("Accept", "application/json")
|
||||
.form(&query)
|
||||
@@ -1048,7 +1048,7 @@ async fn minecraft_token(
|
||||
let token = token.token;
|
||||
|
||||
let res = auth_retry(|| {
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.post("https://api.minecraftservices.com/launcher/login")
|
||||
.header("Accept", "application/json")
|
||||
.json(&json!({
|
||||
@@ -1276,7 +1276,7 @@ async fn minecraft_profile(
|
||||
token: &str,
|
||||
) -> Result<MinecraftProfile, MinecraftAuthenticationError> {
|
||||
let res = auth_retry(|| {
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.get("https://api.minecraftservices.com/minecraft/profile")
|
||||
.header("Accept", "application/json")
|
||||
.bearer_auth(token)
|
||||
@@ -1327,7 +1327,7 @@ async fn minecraft_entitlements(
|
||||
token: &str,
|
||||
) -> Result<MinecraftEntitlements, MinecraftAuthenticationError> {
|
||||
let res = auth_retry(|| {
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.get(format!("https://api.minecraftservices.com/entitlements/license?requestId={}", Uuid::new_v4()))
|
||||
.header("Accept", "application/json")
|
||||
.bearer_auth(token)
|
||||
@@ -1471,7 +1471,7 @@ async fn send_signed_request<T: DeserializeOwned>(
|
||||
let signature = BASE64_STANDARD.encode(&sig_buffer);
|
||||
|
||||
let res = auth_retry(|| {
|
||||
let mut request = REQWEST_CLIENT
|
||||
let mut request = INSECURE_REQWEST_CLIENT
|
||||
.post(url)
|
||||
.header("Content-Type", "application/json; charset=utf-8")
|
||||
.header("Accept", "application/json")
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
ErrorKind,
|
||||
data::Credentials,
|
||||
state::{MinecraftProfile, PROFILE_CACHE, ProfileCacheEntry},
|
||||
util::fetch::REQWEST_CLIENT,
|
||||
util::fetch::INSECURE_REQWEST_CLIENT,
|
||||
};
|
||||
|
||||
/// Provides operations for interacting with capes on a Minecraft player profile.
|
||||
@@ -23,7 +23,7 @@ impl MinecraftCapeOperation {
|
||||
cape_id: Uuid,
|
||||
) -> crate::Result<()> {
|
||||
update_profile_cache_from_response(
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.put("https://api.minecraftservices.com/minecraft/profile/capes/active")
|
||||
.header("Content-Type", "application/json; charset=utf-8")
|
||||
.header("Accept", "application/json")
|
||||
@@ -42,7 +42,7 @@ impl MinecraftCapeOperation {
|
||||
|
||||
pub async fn unequip_any(credentials: &Credentials) -> crate::Result<()> {
|
||||
update_profile_cache_from_response(
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.delete("https://api.minecraftservices.com/minecraft/profile/capes/active")
|
||||
.header("Accept", "application/json")
|
||||
.bearer_auth(&credentials.access_token)
|
||||
@@ -92,7 +92,7 @@ impl MinecraftSkinOperation {
|
||||
);
|
||||
|
||||
update_profile_cache_from_response(
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.post(
|
||||
"https://api.minecraftservices.com/minecraft/profile/skins",
|
||||
)
|
||||
@@ -110,7 +110,7 @@ impl MinecraftSkinOperation {
|
||||
|
||||
pub async fn unequip_any(credentials: &Credentials) -> crate::Result<()> {
|
||||
update_profile_cache_from_response(
|
||||
REQWEST_CLIENT
|
||||
INSECURE_REQWEST_CLIENT
|
||||
.delete("https://api.minecraftservices.com/minecraft/profile/skins/active")
|
||||
.header("Accept", "application/json")
|
||||
.bearer_auth(&credentials.access_token)
|
||||
|
||||
@@ -130,18 +130,24 @@ static GLOBAL_FETCH_FENCE: LazyLock<FetchFence> =
|
||||
inner: Mutex::new(FenceInner::new()),
|
||||
});
|
||||
|
||||
pub static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
|
||||
let header =
|
||||
reqwest::header::HeaderValue::from_str(&crate::launcher_user_agent())
|
||||
.unwrap();
|
||||
headers.insert(reqwest::header::USER_AGENT, header);
|
||||
fn reqwest_client_builder() -> reqwest::ClientBuilder {
|
||||
reqwest::Client::builder()
|
||||
.tcp_keepalive(Some(time::Duration::from_secs(10)))
|
||||
.default_headers(headers)
|
||||
.user_agent(crate::launcher_user_agent())
|
||||
}
|
||||
|
||||
pub static INSECURE_REQWEST_CLIENT: LazyLock<reqwest::Client> =
|
||||
LazyLock::new(|| {
|
||||
reqwest_client_builder()
|
||||
.build()
|
||||
.expect("client configuration should be valid")
|
||||
});
|
||||
|
||||
pub static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||
reqwest_client_builder()
|
||||
.https_only(true)
|
||||
.build()
|
||||
.expect("Reqwest Client Building Failed")
|
||||
.expect("client configuration should be valid")
|
||||
});
|
||||
|
||||
const FETCH_ATTEMPTS: usize = 2;
|
||||
@@ -157,6 +163,28 @@ pub async fn fetch(
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(semaphore))]
|
||||
pub async fn fetch_with_client(
|
||||
url: &str,
|
||||
sha1: Option<&str>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
client: &reqwest::Client,
|
||||
) -> crate::Result<Bytes> {
|
||||
fetch_advanced_with_client(
|
||||
Method::GET,
|
||||
url,
|
||||
sha1,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
semaphore,
|
||||
exec,
|
||||
client,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(json_body, semaphore))]
|
||||
pub async fn fetch_json<T>(
|
||||
method: Method,
|
||||
@@ -177,7 +205,8 @@ where
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Downloads a file with retry and checksum functionality
|
||||
/// Downloads a file with retry and checksum functionality, and a specific
|
||||
/// [`reqwest::Client`].
|
||||
#[tracing::instrument(skip(json_body, semaphore))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn fetch_advanced(
|
||||
@@ -189,6 +218,34 @@ pub async fn fetch_advanced(
|
||||
loading_bar: Option<(&LoadingBarId, f64)>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
) -> crate::Result<Bytes> {
|
||||
fetch_advanced_with_client(
|
||||
method,
|
||||
url,
|
||||
sha1,
|
||||
json_body,
|
||||
header,
|
||||
loading_bar,
|
||||
semaphore,
|
||||
exec,
|
||||
&INSECURE_REQWEST_CLIENT,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Downloads a file with retry and checksum functionality
|
||||
#[tracing::instrument(skip(json_body, semaphore))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn fetch_advanced_with_client(
|
||||
method: Method,
|
||||
url: &str,
|
||||
sha1: Option<&str>,
|
||||
json_body: Option<serde_json::Value>,
|
||||
header: Option<(&str, &str)>,
|
||||
loading_bar: Option<(&LoadingBarId, f64)>,
|
||||
semaphore: &FetchSemaphore,
|
||||
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
||||
client: &reqwest::Client,
|
||||
) -> crate::Result<Bytes> {
|
||||
let _permit = semaphore.0.acquire().await?;
|
||||
|
||||
@@ -210,7 +267,7 @@ pub async fn fetch_advanced(
|
||||
return Err(ErrorKind::ApiIsDownError.into());
|
||||
}
|
||||
|
||||
let mut req = REQWEST_CLIENT.request(method.clone(), url);
|
||||
let mut req = INSECURE_REQWEST_CLIENT.request(method.clone(), url);
|
||||
|
||||
if let Some(body) = json_body.clone() {
|
||||
req = req.json(&body);
|
||||
@@ -328,7 +385,9 @@ pub async fn fetch_mirrors(
|
||||
}
|
||||
|
||||
for (index, mirror) in mirrors.iter().enumerate() {
|
||||
let result = fetch(mirror, sha1, semaphore, exec).await;
|
||||
let result =
|
||||
fetch_with_client(mirror, sha1, semaphore, exec, &REQWEST_CLIENT)
|
||||
.await;
|
||||
|
||||
if result.is_ok() || (result.is_err() && index == (mirrors.len() - 1)) {
|
||||
return result;
|
||||
@@ -348,7 +407,7 @@ pub async fn post_json(
|
||||
) -> crate::Result<()> {
|
||||
let _permit = semaphore.0.acquire().await?;
|
||||
|
||||
let mut req = REQWEST_CLIENT.post(url).json(&json_body);
|
||||
let mut req = INSECURE_REQWEST_CLIENT.post(url).json(&json_body);
|
||||
|
||||
if let Some(creds) =
|
||||
crate::state::ModrinthCredentials::get_active(exec).await?
|
||||
|
||||
@@ -10,6 +10,20 @@ export type VersionEntry = {
|
||||
}
|
||||
|
||||
const VERSIONS: VersionEntry[] = [
|
||||
{
|
||||
date: `2026-04-20T20:48:51+00:00`,
|
||||
product: 'app',
|
||||
version: '0.13.4',
|
||||
body: `## Fixed
|
||||
- Browse page not using relevanced as default search type.
|
||||
- Fixed problem with the sticky header on scroll in the Files tab of instance pages.`,
|
||||
},
|
||||
{
|
||||
date: `2026-04-20T20:48:51+00:00`,
|
||||
product: 'web',
|
||||
body: `## Fixed
|
||||
- Browse page not using relevanced as default search type.`,
|
||||
},
|
||||
{
|
||||
date: `2026-04-19T00:07:46+00:00`,
|
||||
product: 'app',
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"defaultMessage": "اختر علامة دقيقة"
|
||||
},
|
||||
"nags.description-too-short.description": {
|
||||
"defaultMessage": "وصفك هو {length, plural, one {# readable character} other {# readable characters}}. يُوصى بأن يكون على الأقل {minChars, plural, one {# character} other {# characters}} لإنشاء وصف واضح وغني بالمعلومات.\n"
|
||||
"defaultMessage": "وصفك هو {length, plural, one {# readable character} other {# readable characters}}. يُوصى بأن يكون على الأقل {minChars, plural, one {# character}other{#characters}} لإنشاء وصف واضح وغني بالمعلومات."
|
||||
},
|
||||
"nags.description-too-short.title": {
|
||||
"defaultMessage": "قم بتوسيع الوصف"
|
||||
@@ -57,16 +57,16 @@
|
||||
"defaultMessage": "تعديل العنوان"
|
||||
},
|
||||
"nags.feature-gallery-image.description": {
|
||||
"defaultMessage": "غالباً ما تكون صورة المعرض الرئيسية هي الطريقة التي يترك بها مشروعك انطباعه الأول."
|
||||
"defaultMessage": "غالباً ما تكون صورة المَعرض الرئيسية هي الطريقة التي يترك بها مشروعك انطباعه الأول."
|
||||
},
|
||||
"nags.feature-gallery-image.title": {
|
||||
"defaultMessage": ""
|
||||
"defaultMessage": "صورة عرض مميزة"
|
||||
},
|
||||
"nags.gallery.title": {
|
||||
"defaultMessage": "زيارة مَعْرِض الصور"
|
||||
},
|
||||
"nags.gpl-license-source-required.description": {
|
||||
"defaultMessage": "الـ {type, select, mod {mod} plugin {plugin} other {project}} الخاص بك يستخدم ترخيصًا يتطلب إتاحة الشيفرة المصدرية. يُرجى توفير رابط الشيفرة المصدرية أو ملف المصادر لكل إصدار إضافي، أو التفكير في استخدام ترخيص مختلف.\n"
|
||||
"defaultMessage": "الـ {type, select, mod {mod} plugin {plugin} other {project}} الخاص بك يستخدم ترخيصًا يتطلب إتاحة الشيفرة المصدرية. يُرجى توفير رابط الشيفرة المصدرية أو ملف المصادر لكل إصدار إضافي، أو التفكير في استخدام ترخيص مختلف.\n"
|
||||
},
|
||||
"nags.gpl-license-source-required.title": {
|
||||
"defaultMessage": "توفير كود مصدري"
|
||||
@@ -86,17 +86,167 @@
|
||||
"nags.invalid-license-url.description.default": {
|
||||
"defaultMessage": "رابط الرخصة غير صالح."
|
||||
},
|
||||
"nags.invalid-license-url.description.domain": {
|
||||
"defaultMessage": "رابط حقوقك يعود إلى {domain} هو بطبعه غير لائق بمعلومات حقوق. روابط الحقوق يجب أن تربط إلى مَلف حقوقك. ليس منصات التواصل أو منصات الألعاب."
|
||||
},
|
||||
"nags.invalid-license-url.description.malformed": {
|
||||
"defaultMessage": "رابط حقوقك يظهر على انه غير صحيح. يرجى توفير رابط حقوق صحيح."
|
||||
},
|
||||
"nags.invalid-license-url.title": {
|
||||
"defaultMessage": "اضف رابط حقوق صحيح"
|
||||
},
|
||||
"nags.link-shortener-usage.description": {
|
||||
"defaultMessage": "يُحظر استخدام مختصرات الروابط أو طرق أخرى لإخفاء الوجهة التي قد يؤدي إليها الرابط في روابطك الخارجية أو رابط الترخيص، يرجى استخدام الروابط الطويلة المناسبة فقط."
|
||||
},
|
||||
"nags.link-shortener-usage.title": {
|
||||
"defaultMessage": "لا تستخدم مقصر روابط"
|
||||
},
|
||||
"nags.long-headers.description": {
|
||||
"defaultMessage": "{count, plural, one {# header} other {# headers}} في وصفك {count, plural, one {طويل جدًا} other {طويلة جدًا}}. يجب أن تكون العناوين مختصرة وتعمل كعناوين للأقسام، وليست جملًا كاملة.\n"
|
||||
"defaultMessage": "{count, plural, one {# header} other {# headers}} في وصفك {count, plural, one {طويل جدًا} other {طويلة جدًا}}.يجب أن تكون العناوين مختصرة وتعمل كعناوين للأقسام،وليست جملًا كاملة.\n"
|
||||
},
|
||||
"nags.long-headers.title": {
|
||||
"defaultMessage": "قصر العناوين"
|
||||
},
|
||||
"nags.minecraft-title-clause.description": {
|
||||
"defaultMessage": "المشاريع يجب ان لا تستخدم العلامات التجارية الخاصة ب \"ماينكرفت\" اذا كانت جزءا كبيرا من اسم المشروع."
|
||||
"defaultMessage": "المشاريع يجب أن لا تستخدم العلامات التجارية الخاصة ب \"ماينكرفت\" إذا كانت جزءا كبيرا من اسم المشروع."
|
||||
},
|
||||
"nags.minecraft-title-clause.title": {
|
||||
"defaultMessage": "تجنب انتهاك العلامة التجارية"
|
||||
},
|
||||
"nags.missing-alt-text.description": {
|
||||
"defaultMessage": "بعض صورك تفتقد نصوصًا بديلة، وهذا مهم لإمكانية الوصول، خاصةً للمستخدمين ذوي الإعاقة البصرية."
|
||||
},
|
||||
"nags.missing-alt-text.title": {
|
||||
"defaultMessage": "اضف صورة نص"
|
||||
},
|
||||
"nags.misused-discord-link-description": {
|
||||
"defaultMessage": "لا يمكن استخدام دعوات ديسكورد لأنواع روابط أخرى. يرجى وضع رابط ديسكورد الخاص بك في حقل رابط دعوة ديسكورد فقط."
|
||||
},
|
||||
"nags.misused-discord-link.title": {
|
||||
"defaultMessage": "حرك دعوة ديسكورد"
|
||||
},
|
||||
"nags.moderation.title": {
|
||||
"defaultMessage": "زر صفحة الإشراف"
|
||||
},
|
||||
"nags.moderator-feedback.description": {
|
||||
"defaultMessage": "راجع وتناول جميع المخاوف من فريق الإشراف قبل إعادة التقديم."
|
||||
},
|
||||
"nags.moderator-feedback.title": {
|
||||
"defaultMessage": "مراجعة التعليقات."
|
||||
},
|
||||
"nags.multiple-resolution-tags.description": {
|
||||
"defaultMessage": "لقد اخترت {count, plural, one {# resolution tag} other {# resolution tags}} ({tags}) حزمة الموارد يجب ان تمتلك فقط علامة دقة واحدة تتطابق مع دقتهم الأساسية."
|
||||
},
|
||||
"nags.multiple-resolution-tags.title": {
|
||||
"defaultMessage": "أختر قرار صحيح"
|
||||
},
|
||||
"nags.select-compatibility.description": {
|
||||
"defaultMessage": "ماهي الإصدارات التي يدعمها خادمك. اختر مودباك, أو اضف من عندك."
|
||||
},
|
||||
"nags.select-compatibility.title": {
|
||||
"defaultMessage": "اختر التوافق."
|
||||
},
|
||||
"nags.select-country.description": {
|
||||
"defaultMessage": "دع لاعبي خادمك يعرفون منطقة الخادم."
|
||||
},
|
||||
"nags.select-country.title": {
|
||||
"defaultMessage": "اختر منطقة"
|
||||
},
|
||||
"nags.select-language.description": {
|
||||
"defaultMessage": "اختر لغات المدعومة من خادمك."
|
||||
},
|
||||
"nags.select-language.title": {
|
||||
"defaultMessage": "اختر لغة."
|
||||
},
|
||||
"nags.select-license.description": {
|
||||
"defaultMessage": "اختر حقوق لـ {type, select, mod {mod} modpack {modpack} resourcepack {resource pack} shader {shader} plugin {plugin} datapack {data pack} other {project}} تحت الازعاج."
|
||||
},
|
||||
"nags.select-license.title": {
|
||||
"defaultMessage": "اختر حقوقا"
|
||||
},
|
||||
"nags.select-tags.description": {
|
||||
"defaultMessage": "اختر العلامات التي تنطبق على مشروعك لتساعد المستخدمين على إيجاده."
|
||||
},
|
||||
"nags.select-tags.title": {
|
||||
"defaultMessage": "إختيار العلامات"
|
||||
},
|
||||
"nags.server.title": {
|
||||
"defaultMessage": "زيارة إعدادات الخادم"
|
||||
},
|
||||
"nags.settings.description.title": {
|
||||
"defaultMessage": "زيارة إعدادات الوصف"
|
||||
},
|
||||
"nags.settings.license.title": {
|
||||
"defaultMessage": "زيارة إعدادات الحقوق"
|
||||
},
|
||||
"nags.settings.links.title": {
|
||||
"defaultMessage": "زيارة إعدادات الروابط"
|
||||
},
|
||||
"nags.settings.tags.title": {
|
||||
"defaultMessage": "زيارة إعدادات العلامة"
|
||||
},
|
||||
"nags.settings.title": {
|
||||
"defaultMessage": "زيارة الإعدادات العامة"
|
||||
},
|
||||
"nags.summary-same-as-title.description": {
|
||||
"defaultMessage": "لا يمكن أن يكون ملخص مشروعك نفس اسمه. من المهم أن تصنع ملخصا واضحا وغني بالمعلومات."
|
||||
},
|
||||
"nags.summary-same-as-title.title": {
|
||||
"defaultMessage": "تاكد أن ملخصك فريد"
|
||||
},
|
||||
"nags.summary-special-formatting.description": {
|
||||
"defaultMessage": "لا يجب على الملخص الخاص بك أن يمتلك التنسيق، فواصل الأسطر، الأحرف الخاصة، أو الروابط، حيث أن الملخص سيعرض فقط النص العادي."
|
||||
},
|
||||
"nags.summary-special-formatting.title": {
|
||||
"defaultMessage": "نضف الملخص"
|
||||
},
|
||||
"nags.summary-too-short.description": {
|
||||
"defaultMessage": "الملخص الخاص بك {length, plural, one {# character} other {# characters}}. يفضل ان تكون على الاقل {minChars, plural, one {# character} other {# characters}} لإنشاء وصف واضح."
|
||||
},
|
||||
"nags.summary-too-short.title": {
|
||||
"defaultMessage": "وسع الملخص"
|
||||
},
|
||||
"nags.title-contains-technical-info.description": {
|
||||
"defaultMessage": "ترك اسم مشروعك نضيف يجعله قابلا للتذكر و سهلا للبحث.\nالتحديث و معلومات المحمل تعرض في مشروعك بطريقة أوتوماتيكية."
|
||||
},
|
||||
"nags.title-contains-technical-info.title": {
|
||||
"defaultMessage": "عدل اسم"
|
||||
},
|
||||
"nags.too-many-tags-server.description": {
|
||||
"defaultMessage": "لقد اخترت{tagCount, plural, one {# tag} other {# tags}}. \nفكر في تقليلها الى {maxTagCount} او اقل من اجل عرض خادمك في بحوثات متعلقة."
|
||||
},
|
||||
"nags.too-many-tags-server.title": {
|
||||
"defaultMessage": "حدد علامات دقيقة"
|
||||
},
|
||||
"nags.too-many-tags.description": {
|
||||
"defaultMessage": "لقد اخترت{tagCount, plural, one {# tag} other {# tags}}. \nفكر في تقليلها الى {maxTagCount} او اقل من اجل عرض مشروعك في بحوثات متعلقة."
|
||||
},
|
||||
"nags.too-many-tags.title": {
|
||||
"defaultMessage": "اختار علامات دقيقة"
|
||||
},
|
||||
"nags.upload-gallery-image.description": {
|
||||
"defaultMessage": "صورة واحدة على الاقل لازمةلعرض المحتوى الخاص بـ{type, select, resourcepack {resource pack, except for audio or localization packs. If this describes your pack, please select the appropriate tag} shader {shader} other {project}}."
|
||||
},
|
||||
"nags.upload-gallery-image.title": {
|
||||
"defaultMessage": "رفع صورة من المَعْرِض"
|
||||
},
|
||||
"nags.upload-version.description": {
|
||||
"defaultMessage": "إصدار واحد على الأقل لازم لتقديم المشروع للنشر."
|
||||
},
|
||||
"nags.upload-version.title": {
|
||||
"defaultMessage": "رفع النسخة"
|
||||
},
|
||||
"nags.verify-external-links.description": {
|
||||
"defaultMessage": "بعض روابطك الإضافية يمكن أن تكون تستخدم مجالات غير لائقة لهذا النوع من الروابط"
|
||||
},
|
||||
"nags.verify-external-links.title": {
|
||||
"defaultMessage": "إثبات روابط خارجية"
|
||||
},
|
||||
"nags.versions.title": {
|
||||
"defaultMessage": "زيارة صفحة الإصدار"
|
||||
},
|
||||
"nags.visit-links-settings.title": {
|
||||
"defaultMessage": "زيارة إعدادات الروابط"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
"defaultMessage": "Piliin kung anong mga bersiyon ang sinusupotahan ng iyong akawnt, o mag-upload nang sarili."
|
||||
},
|
||||
"nags.select-compatibility.title": {
|
||||
"defaultMessage": "Piliin ang kompatibilidad"
|
||||
"defaultMessage": "Piliin ang pagkakatugma"
|
||||
},
|
||||
"nags.select-country.description": {
|
||||
"defaultMessage": "Hayaang malaman ng mga manlalaro kung saang rehiyon nakalagay ang iyong server."
|
||||
|
||||
@@ -90,10 +90,10 @@
|
||||
"defaultMessage": "이 라이선스 URL {domain}은(는) 라이선스 정보로 적절하지 않습니다. 라이선스 URL은 소셜 미디어나 게임 플랫폼 등이 아닌, 라이선스 파일 자체로 연결되어야 합니다."
|
||||
},
|
||||
"nags.invalid-license-url.description.malformed": {
|
||||
"defaultMessage": "이 라이센스 URL은 잘못되었습니다. 라이선스 전문으로 연결되는 올바른 URL을 제공해 주세요."
|
||||
"defaultMessage": "이 라이센스 URL은 잘못되었습니다. URL을 라이선스 전문으로 연결해 주세요."
|
||||
},
|
||||
"nags.invalid-license-url.title": {
|
||||
"defaultMessage": "올바른 라이선스 링크를 추가해주세요"
|
||||
"defaultMessage": "올바른 라이선스 링크를 추가해 주세요"
|
||||
},
|
||||
"nags.link-shortener-usage.description": {
|
||||
"defaultMessage": "외부 링크나 라이선스 링크에서 링크 단축기 및 기타 방법의 사용은 금지되어 있습니다. 반드시 전체 길이의 링크만 사용해 주세요."
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '@modrinth/assets'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import type Stripe from 'stripe'
|
||||
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
|
||||
import { computed, nextTick, ref, toRef, useTemplateRef, watch } from 'vue'
|
||||
|
||||
import { injectNotificationManager } from '#ui/providers/web-notifications.ts'
|
||||
|
||||
@@ -94,8 +94,8 @@ const {
|
||||
noPaymentRequired,
|
||||
} = useStripe(
|
||||
props.publishableKey,
|
||||
props.customer,
|
||||
props.paymentMethods,
|
||||
toRef(props, 'customer'),
|
||||
toRef(props, 'paymentMethods'),
|
||||
props.currency,
|
||||
selectedPlan,
|
||||
selectedInterval,
|
||||
@@ -116,6 +116,10 @@ const steps: Step[] = props.planStage
|
||||
? (['plan', 'region', 'payment', 'review'] as Step[])
|
||||
: (['region', 'payment', 'review'] as Step[])
|
||||
|
||||
const isUpgrade = computed(() => !!props.existingSubscription)
|
||||
const skipRegionStep = computed(() => isUpgrade.value && !customServer.value)
|
||||
const visibleSteps = computed(() => steps.filter((s) => !(s === 'region' && skipRegionStep.value)))
|
||||
|
||||
const titles: Record<Step, MessageDescriptor> = {
|
||||
plan: defineMessage({ id: 'servers.purchase.step.plan.title', defaultMessage: 'Plan' }),
|
||||
region: defineMessage({ id: 'servers.purchase.step.region.title', defaultMessage: 'Region' }),
|
||||
@@ -146,17 +150,24 @@ const currentPing = computed(() => {
|
||||
|
||||
const currentStep = ref<Step>()
|
||||
|
||||
const currentStepIndex = computed(() => (currentStep.value ? steps.indexOf(currentStep.value) : -1))
|
||||
const currentStepIndex = computed(() =>
|
||||
currentStep.value ? visibleSteps.value.indexOf(currentStep.value) : -1,
|
||||
)
|
||||
const previousStep = computed(() => {
|
||||
const step = currentStep.value ? steps[steps.indexOf(currentStep.value) - 1] : undefined
|
||||
if (!currentStep.value) return undefined
|
||||
const idx = visibleSteps.value.indexOf(currentStep.value)
|
||||
let step = idx > 0 ? visibleSteps.value[idx - 1] : undefined
|
||||
if (step === 'payment' && skipPaymentMethods.value && primaryPaymentMethodId.value) {
|
||||
return 'region'
|
||||
const paymentIdx = visibleSteps.value.indexOf('payment')
|
||||
step = paymentIdx > 0 ? visibleSteps.value[paymentIdx - 1] : undefined
|
||||
}
|
||||
return step
|
||||
})
|
||||
const nextStep = computed(() =>
|
||||
currentStep.value ? steps[steps.indexOf(currentStep.value) + 1] : undefined,
|
||||
)
|
||||
const nextStep = computed(() => {
|
||||
if (!currentStep.value) return undefined
|
||||
const idx = visibleSteps.value.indexOf(currentStep.value)
|
||||
return idx >= 0 ? visibleSteps.value[idx + 1] : undefined
|
||||
})
|
||||
|
||||
const canProceed = computed(() => {
|
||||
switch (currentStep.value) {
|
||||
@@ -195,12 +206,14 @@ async function beforeProceed(step: string) {
|
||||
await initializeStripe()
|
||||
|
||||
if (primaryPaymentMethodId.value && skipPaymentMethods.value) {
|
||||
const paymentMethod = await props.paymentMethods.find(
|
||||
const paymentMethod = props.paymentMethods.find(
|
||||
(x) => x.id === primaryPaymentMethodId.value,
|
||||
)
|
||||
await selectPaymentMethod(paymentMethod)
|
||||
await setStep('review', true)
|
||||
return false
|
||||
if (paymentMethod) {
|
||||
await selectPaymentMethod(paymentMethod)
|
||||
await setStep('review', true)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case 'review':
|
||||
@@ -261,11 +274,25 @@ async function setStep(step: Step | undefined, skipValidation = false) {
|
||||
}
|
||||
}
|
||||
|
||||
watch(selectedPlan, () => {
|
||||
if (currentStep.value === 'plan') {
|
||||
customServer.value = !selectedPlan.value
|
||||
}
|
||||
})
|
||||
watch(
|
||||
selectedPlan,
|
||||
() => {
|
||||
if (currentStep.value === 'plan') {
|
||||
customServer.value = !selectedPlan.value
|
||||
}
|
||||
},
|
||||
{ flush: 'sync' },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.existingSubscription,
|
||||
(sub) => {
|
||||
if (sub?.metadata?.type === 'pyro' && sub.metadata.region) {
|
||||
selectedRegion.value = sub.metadata.region
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const defaultPlan = computed<Labrinth.Billing.Internal.Product | undefined>(() => {
|
||||
return (
|
||||
@@ -295,7 +322,9 @@ function begin(
|
||||
customServer.value = !selectedPlan.value
|
||||
selectedPaymentMethod.value = undefined
|
||||
const skipPlanStep = props.planStage && plan !== undefined
|
||||
currentStep.value = skipPlanStep ? (steps[1] ?? steps[0]) : steps[0]
|
||||
currentStep.value = skipPlanStep
|
||||
? (visibleSteps.value[1] ?? visibleSteps.value[0])
|
||||
: visibleSteps.value[0]
|
||||
skipPaymentMethods.value = true
|
||||
projectId.value = project
|
||||
modal.value?.show()
|
||||
@@ -339,7 +368,7 @@ function goToBreadcrumbStep(id: string) {
|
||||
<NewModal ref="modal" @hide="$emit('hide')">
|
||||
<template #title>
|
||||
<div class="flex items-center gap-1 font-bold text-secondary">
|
||||
<template v-for="(step, index) in steps" :key="step">
|
||||
<template v-for="(step, index) in visibleSteps" :key="step">
|
||||
<button
|
||||
v-if="index < currentStepIndex"
|
||||
class="bg-transparent active:scale-95 font-bold text-secondary p-0"
|
||||
@@ -356,7 +385,7 @@ function goToBreadcrumbStep(id: string) {
|
||||
{{ formatMessage(titles[step]) }}
|
||||
</span>
|
||||
<ChevronRightIcon
|
||||
v-if="index < steps.length - 1"
|
||||
v-if="index < visibleSteps.length - 1"
|
||||
class="h-5 w-5 text-secondary"
|
||||
stroke-width="3"
|
||||
/>
|
||||
@@ -381,6 +410,7 @@ function goToBreadcrumbStep(id: string) {
|
||||
:regions="regions"
|
||||
:pings="pings"
|
||||
:custom="customServer"
|
||||
:hide-region-selection="isUpgrade"
|
||||
:available-products="availableProducts"
|
||||
:currency="currency"
|
||||
:interval="selectedInterval"
|
||||
|
||||
@@ -25,6 +25,7 @@ const props = defineProps<{
|
||||
request: Archon.Servers.v0.StockRequest,
|
||||
) => Promise<number>
|
||||
custom: boolean
|
||||
hideRegionSelection?: boolean
|
||||
currency: string
|
||||
interval: ServerBillingInterval
|
||||
availableProducts: Labrinth.Billing.Internal.Product[]
|
||||
@@ -177,6 +178,10 @@ const messages = defineMessages({
|
||||
id: 'servers.region.custom.prompt',
|
||||
defaultMessage: `How much RAM do you want your server to have?`,
|
||||
},
|
||||
customPromptRamOnly: {
|
||||
id: 'servers.region.custom.prompt-ram-only',
|
||||
defaultMessage: `RAM`,
|
||||
},
|
||||
})
|
||||
|
||||
async function updateStock() {
|
||||
@@ -219,8 +224,10 @@ onMounted(() => {
|
||||
if (b.ping <= 0) return -1
|
||||
return a.ping - b.ping
|
||||
})[0]?.region
|
||||
selectedRegion.value = undefined
|
||||
selectedRam.value = minRam.value
|
||||
if (!props.hideRegionSelection) {
|
||||
selectedRegion.value = undefined
|
||||
}
|
||||
checkingCustomStock.value = true
|
||||
updateStock().then(() => {
|
||||
const firstWithStock = sortedRegions.value.find(
|
||||
@@ -228,8 +235,9 @@ onMounted(() => {
|
||||
)
|
||||
let stockedRegion = selectedRegion.value
|
||||
if (!stockedRegion) {
|
||||
stockedRegion =
|
||||
bestPing.value && currentStock.value[bestPing.value] > 0
|
||||
stockedRegion = props.hideRegionSelection
|
||||
? firstWithStock?.shortcode
|
||||
: bestPing.value && currentStock.value[bestPing.value] > 0
|
||||
? bestPing.value
|
||||
: firstWithStock?.shortcode
|
||||
}
|
||||
@@ -247,37 +255,44 @@ onMounted(() => {
|
||||
Checking availability...
|
||||
</ModalLoadingIndicator>
|
||||
<template v-else>
|
||||
<h2 class="mt-0 mb-4 text-xl font-bold text-contrast">
|
||||
{{ formatMessage(messages.prompt) }}
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<ServersRegionButton
|
||||
v-for="region in visibleRegions"
|
||||
:key="region.shortcode"
|
||||
v-model="selectedRegion"
|
||||
:region="region"
|
||||
:out-of-stock="currentStock[region.shortcode] === 0"
|
||||
:ping="pings.find((p) => p.region === region.shortcode)?.ping"
|
||||
:best-ping="bestPing === region.shortcode"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3 text-sm">
|
||||
<IntlFormatted :message-id="messages.regionUnsupported">
|
||||
<template #link="{ children }">
|
||||
<a
|
||||
class="text-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://surveys.modrinth.com/servers-region-waitlist"
|
||||
>
|
||||
<component :is="() => children" />
|
||||
</a>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</div>
|
||||
<template v-if="!hideRegionSelection">
|
||||
<h2 class="mt-0 mb-4 text-xl font-bold text-contrast">
|
||||
{{ formatMessage(messages.prompt) }}
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<ServersRegionButton
|
||||
v-for="region in visibleRegions"
|
||||
:key="region.shortcode"
|
||||
v-model="selectedRegion"
|
||||
:region="region"
|
||||
:out-of-stock="currentStock[region.shortcode] === 0"
|
||||
:ping="pings.find((p) => p.region === region.shortcode)?.ping"
|
||||
:best-ping="bestPing === region.shortcode"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-3 text-sm">
|
||||
<IntlFormatted :message-id="messages.regionUnsupported">
|
||||
<template #link="{ children }">
|
||||
<a
|
||||
class="text-link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://surveys.modrinth.com/servers-region-waitlist"
|
||||
>
|
||||
<component :is="() => children" />
|
||||
</a>
|
||||
</template>
|
||||
</IntlFormatted>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="custom">
|
||||
<h2 class="mt-4 mb-2 text-xl font-bold text-contrast">
|
||||
{{ formatMessage(messages.customPrompt) }}
|
||||
<h2
|
||||
class="mb-2 text-xl font-bold text-contrast"
|
||||
:class="hideRegionSelection ? 'mt-0' : 'mt-4'"
|
||||
>
|
||||
{{
|
||||
formatMessage(hideRegionSelection ? messages.customPromptRamOnly : messages.customPrompt)
|
||||
}}
|
||||
</h2>
|
||||
<div>
|
||||
<Slider v-model="selectedRam" :min="minRam" :max="maxRam" :step="2" unit="GB" />
|
||||
|
||||
@@ -180,7 +180,7 @@ type MedalServerListingProps = {
|
||||
|
||||
const props = defineProps<MedalServerListingProps>()
|
||||
|
||||
const emit = defineEmits<{ (e: 'upgrade'): void }>()
|
||||
const emit = defineEmits<{ (e: 'upgrade', serverId: string): void }>()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const client = injectModrinthClient()
|
||||
@@ -257,7 +257,7 @@ const expiryDate = computed(() => (props.medal_expires ? dayjs(props.medal_expir
|
||||
|
||||
function handleUpgrade(event: Event) {
|
||||
event.stopPropagation()
|
||||
emit('upgrade')
|
||||
emit('upgrade', props.server_id)
|
||||
}
|
||||
|
||||
function navigateToServer(event: MouseEvent | KeyboardEvent) {
|
||||
|
||||
@@ -18,8 +18,8 @@ import { getPriceForInterval } from '../utils/product-utils'
|
||||
|
||||
export const useStripe = (
|
||||
publishableKey: string,
|
||||
customer: Stripe.Customer,
|
||||
paymentMethods: Stripe.PaymentMethod[],
|
||||
customer: Ref<Stripe.Customer | null | undefined>,
|
||||
paymentMethods: Ref<Stripe.PaymentMethod[]>,
|
||||
currency: string,
|
||||
product: Ref<Labrinth.Billing.Internal.Product | undefined>,
|
||||
interval: Ref<ServerBillingInterval>,
|
||||
@@ -96,7 +96,7 @@ export const useStripe = (
|
||||
|
||||
const contacts: ContactOption[] = []
|
||||
|
||||
paymentMethods.forEach((method) => {
|
||||
paymentMethods.value.forEach((method) => {
|
||||
const addr = method.billing_details?.address
|
||||
if (
|
||||
addr &&
|
||||
@@ -131,15 +131,22 @@ export const useStripe = (
|
||||
}
|
||||
|
||||
const primaryPaymentMethodId = computed<string | null>(() => {
|
||||
if (customer && customer.invoice_settings && customer.invoice_settings.default_payment_method) {
|
||||
const method = customer.invoice_settings.default_payment_method
|
||||
const customerValue = customer.value
|
||||
const paymentMethodsValue = paymentMethods.value
|
||||
|
||||
if (
|
||||
customerValue &&
|
||||
customerValue.invoice_settings &&
|
||||
customerValue.invoice_settings.default_payment_method
|
||||
) {
|
||||
const method = customerValue.invoice_settings.default_payment_method
|
||||
if (typeof method === 'string') {
|
||||
return method
|
||||
} else {
|
||||
return method.id
|
||||
}
|
||||
} else if (paymentMethods && paymentMethods[0] && paymentMethods[0].id) {
|
||||
return paymentMethods[0].id
|
||||
} else if (paymentMethodsValue[0] && paymentMethodsValue[0].id) {
|
||||
return paymentMethodsValue[0].id
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
@@ -148,7 +155,7 @@ export const useStripe = (
|
||||
const loadStripeElements = async () => {
|
||||
loadingFailed.value = undefined
|
||||
try {
|
||||
if (!customer && primaryPaymentMethodId.value) {
|
||||
if (!customer.value && primaryPaymentMethodId.value) {
|
||||
paymentMethodLoading.value = true
|
||||
await refreshPaymentIntent(primaryPaymentMethodId.value, false)
|
||||
paymentMethodLoading.value = false
|
||||
@@ -189,7 +196,7 @@ export const useStripe = (
|
||||
try {
|
||||
paymentMethodLoading.value = true
|
||||
if (!confirmation) {
|
||||
selectedPaymentMethod.value = paymentMethods.find((x) => x.id === id)
|
||||
selectedPaymentMethod.value = paymentMethods.value.find((x) => x.id === id)
|
||||
}
|
||||
|
||||
if (!product.value) {
|
||||
@@ -248,7 +255,7 @@ export const useStripe = (
|
||||
}
|
||||
).payment_method
|
||||
if (typeof paymentMethod === 'string') {
|
||||
const method = paymentMethods.find((x) => x.id === paymentMethod)
|
||||
const method = paymentMethods.value.find((x) => x.id === paymentMethod)
|
||||
if (method) {
|
||||
inputtedPaymentMethod.value = method
|
||||
}
|
||||
|
||||
@@ -276,7 +276,9 @@ export function useBrowseSearch(options: UseBrowseSearchOptions): BrowseSearchSt
|
||||
() => options.projectType.value,
|
||||
(newType, oldType) => {
|
||||
debug('projectType changed', { from: oldType, to: newType })
|
||||
currentSortType.value = { display: 'Relevance', name: 'relevance' }
|
||||
effectiveCurrentSortType.value =
|
||||
effectiveSortTypes.value.find((sortType) => sortType.name === 'relevance') ??
|
||||
effectiveSortTypes.value[0]
|
||||
query.value = ''
|
||||
},
|
||||
)
|
||||
|
||||
@@ -59,7 +59,6 @@ const maxResultsOptions = computed<ComboboxOption<number>[]>(() =>
|
||||
:model-value="ctx.effectiveCurrentSortType.value"
|
||||
:options="sortOptions"
|
||||
:class="ctx.variant === 'web' ? '!w-auto flex-grow md:flex-grow-0' : 'max-w-[16rem]'"
|
||||
placeholder="Sort by"
|
||||
@update:model-value="(val: SortType) => (ctx.effectiveCurrentSortType.value = val)"
|
||||
>
|
||||
<template #prefix>
|
||||
@@ -191,11 +190,15 @@ const maxResultsOptions = computed<ComboboxOption<number>[]>(() =>
|
||||
:title="result.title"
|
||||
:icon-url="result.icon_url"
|
||||
:author="{
|
||||
name: result.author,
|
||||
name: result.organization == null ? result.author : result.organization,
|
||||
link:
|
||||
ctx.variant === 'web'
|
||||
? `/user/${result.author}`
|
||||
: `https://modrinth.com/user/${result.author}`,
|
||||
result.organization_id == null
|
||||
? ctx.variant === 'web'
|
||||
? `/user/${result.author_id ?? result.author}`
|
||||
: `https://modrinth.com/user/${result.author_id ?? result.author}`
|
||||
: ctx.variant === 'web'
|
||||
? `/organization/${result.organization_id}`
|
||||
: `https://modrinth.com/organization/${result.organization_id}`,
|
||||
}"
|
||||
:date-updated="result.date_modified"
|
||||
:date-published="result.date_created"
|
||||
@@ -248,7 +251,7 @@ const maxResultsOptions = computed<ComboboxOption<number>[]>(() =>
|
||||
</template>
|
||||
</ProjectCardList>
|
||||
|
||||
<div :class="ctx.variant === 'web' ? 'pagination-after' : 'flex justify-end'">
|
||||
<div :class="ctx.variant === 'web' ? 'pagination-after my-3' : 'flex justify-end'">
|
||||
<Pagination
|
||||
:page="ctx.currentPage.value"
|
||||
:count="ctx.pageCount.value"
|
||||
|
||||
@@ -32,6 +32,13 @@
|
||||
@purchase-success="handlePurchaseSuccess"
|
||||
@hide="clearPurchaseIntent"
|
||||
/>
|
||||
<ServersUpgradeModalWrapper
|
||||
v-if="loggedIn"
|
||||
ref="medalUpgradeModal"
|
||||
:stripe-publishable-key="props.stripePublishableKey"
|
||||
:site-url="props.siteUrl"
|
||||
:products="props.products"
|
||||
/>
|
||||
<ResubscribeModal ref="resubscribeModal" @resubscribe="handleResubscribeConfirm" />
|
||||
|
||||
<div
|
||||
@@ -163,7 +170,7 @@
|
||||
v-for="server in filteredData.filter((s) => s.is_medal)"
|
||||
:key="server.server_id"
|
||||
v-bind="server"
|
||||
@upgrade="openPurchaseModal"
|
||||
@upgrade="openMedalUpgradeModal"
|
||||
/>
|
||||
<ServerListing
|
||||
v-for="server in filteredData.filter((s) => !s.is_medal)"
|
||||
@@ -208,9 +215,10 @@ import { useIntervalFn } from '@vueuse/core'
|
||||
import dayjs from 'dayjs'
|
||||
import Fuse from 'fuse.js'
|
||||
import type Stripe from 'stripe'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { type ComponentPublicInstance, computed, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import ServersUpgradeModalWrapper from '#ui/components/billing/ServersUpgradeModalWrapper.vue'
|
||||
import MedalServerListing from '#ui/components/servers/marketing/MedalServerListing.vue'
|
||||
import ServerListing from '#ui/components/servers/ServerListing.vue'
|
||||
import { createHostingPurchaseIntentContext, provideHostingPurchaseIntent } from '#ui/providers'
|
||||
@@ -339,6 +347,8 @@ function startNewServerPolling(initialServers: Archon.Servers.v0.Server[]) {
|
||||
|
||||
const guestPlanModal = ref<InstanceType<typeof ServersGuestPlanModal> | null>(null)
|
||||
const purchaseModal = ref<InstanceType<typeof ModrinthServersPurchaseModal> | null>(null)
|
||||
type UpgradeModalRef = ComponentPublicInstance<{ open: (id?: string) => void | Promise<void> }>
|
||||
const medalUpgradeModal = ref<UpgradeModalRef | null>(null)
|
||||
const resubscribeModal = ref<InstanceType<typeof ResubscribeModal> | null>(null)
|
||||
const affiliateCode = ref<string | null>(null)
|
||||
const selectedCurrency = ref<string>('USD')
|
||||
@@ -691,6 +701,10 @@ provideHostingPurchaseIntent(hostingPurchaseIntent)
|
||||
|
||||
const { openPurchaseModal, handleGuestPlanContinue, clearPurchaseIntent } = hostingPurchaseIntent
|
||||
|
||||
function openMedalUpgradeModal(serverId: string) {
|
||||
medalUpgradeModal.value?.open(serverId)
|
||||
}
|
||||
|
||||
const { data: subscriptions } = useQuery({
|
||||
queryKey: ['billing', 'subscriptions'],
|
||||
queryFn: () => client.labrinth.billing_internal.getSubscriptions(),
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"affiliate.viewAnalytics": {
|
||||
"defaultMessage": "عرض التحليلات"
|
||||
},
|
||||
"app.server-settings.failed-to-load-server": {
|
||||
"defaultMessage": "عذر تحميل خادم الإعدادات"
|
||||
},
|
||||
"badge.beta": {
|
||||
"defaultMessage": "تجريبي"
|
||||
},
|
||||
@@ -41,6 +44,9 @@
|
||||
"badge.new": {
|
||||
"defaultMessage": "جديد"
|
||||
},
|
||||
"billing.resubscribe-modal.cancel": {
|
||||
"defaultMessage": "إلغاء"
|
||||
},
|
||||
"button.accept": {
|
||||
"defaultMessage": "اقبل"
|
||||
},
|
||||
@@ -227,6 +233,9 @@
|
||||
"content.confirm-modpack-update.header": {
|
||||
"defaultMessage": "{action, select, downgrade {خفض ٳصدار} other {تحديث}} حزمة التعديلات"
|
||||
},
|
||||
"content.modpack-card.dismiss-hint": {
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"form.label.address-line": {
|
||||
"defaultMessage": "خط عنوان"
|
||||
},
|
||||
|
||||
@@ -215,6 +215,9 @@
|
||||
"content.confirm-deletion.header": {
|
||||
"defaultMessage": "Smazat {itemType}{count, plural, one {} other {}}"
|
||||
},
|
||||
"content.confirm-modpack-update.admonition-body": {
|
||||
"defaultMessage": "{action, select, downgrade {Downgrading} other {Updating}} může způsobit problémy s kompatibilitou. Mody nebo obsah, který jste přidali na modpack, zůstanou zachovány, ale nemusí být kompatibilní s novou verzí."
|
||||
},
|
||||
"content.confirm-modpack-update.admonition-header": {
|
||||
"defaultMessage": "Upozornění na {action, select, downgrade {zhoršení} other {vylepšení}}"
|
||||
},
|
||||
@@ -347,6 +350,12 @@
|
||||
"content.page-layout.uploading-files": {
|
||||
"defaultMessage": "Nahrávnání souborů ({completed}/{total})"
|
||||
},
|
||||
"content.selection-bar.bulk.updating-waiting": {
|
||||
"defaultMessage": "Aktualizování {contentType}..."
|
||||
},
|
||||
"content.selection-bar.selected-count": {
|
||||
"defaultMessage": "{count} {contentType} vybráno"
|
||||
},
|
||||
"form.label.address-line": {
|
||||
"defaultMessage": "Adresa"
|
||||
},
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
{}
|
||||
{
|
||||
"displayName": {
|
||||
"description": "Please enter the name of the language in its specific variant or regional form (e.g., English (US) for American English, not just English). If the language does not have any specific variant, simply enter the name of the language (e.g., Français, Deutsch).",
|
||||
"message": "Angličtina (Spojené státy)"
|
||||
},
|
||||
"searchTerms": {
|
||||
"description": "Please provide additional search terms associated with the language, if needed, to enhance the search functionality (e.g., American English, Deutschland). Each search term should be entered on a separate line. Translate as a hyphen (-) if no additional terms are needed.",
|
||||
"message": "USA Americká Angličtina"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
"affiliate.viewAnalytics": {
|
||||
"defaultMessage": "Analysen anzeigen"
|
||||
},
|
||||
"app.server-settings.failed-to-load-server": {
|
||||
"defaultMessage": "Server-Einstellungen konnten nicht geladen werden"
|
||||
},
|
||||
"badge.alpha": {
|
||||
"defaultMessage": "Alpha"
|
||||
},
|
||||
"badge.beta": {
|
||||
"defaultMessage": "Beta"
|
||||
},
|
||||
@@ -41,6 +47,54 @@
|
||||
"badge.new": {
|
||||
"defaultMessage": "Neu"
|
||||
},
|
||||
"billing.resubscribe-modal.cancel": {
|
||||
"defaultMessage": "Abbrechen"
|
||||
},
|
||||
"billing.resubscribe-modal.cpus": {
|
||||
"defaultMessage": "{sharedCpus} Geteilte CPUs"
|
||||
},
|
||||
"billing.resubscribe-modal.description": {
|
||||
"defaultMessage": "Du bist dabei, dein Abonnement für <server-name>{serverName}</server-name> zu verlängern. Dein Abonnement wird reaktiviert und dein Server läuft ohne Unterbrechung weiter."
|
||||
},
|
||||
"billing.resubscribe-modal.error.text": {
|
||||
"defaultMessage": "Das Abonnement konnte nicht erneuert werden, das Laden von Details zum Abonnement ist fehlgeschlagen."
|
||||
},
|
||||
"billing.resubscribe-modal.error.title": {
|
||||
"defaultMessage": "Fehler"
|
||||
},
|
||||
"billing.resubscribe-modal.failed-load": {
|
||||
"defaultMessage": "Details zum Abonnement konnten nicht geladen werden."
|
||||
},
|
||||
"billing.resubscribe-modal.interval.five-days": {
|
||||
"defaultMessage": "/5 Tage"
|
||||
},
|
||||
"billing.resubscribe-modal.interval.monthly": {
|
||||
"defaultMessage": "/Monat"
|
||||
},
|
||||
"billing.resubscribe-modal.interval.quarterly": {
|
||||
"defaultMessage": "/Quartal"
|
||||
},
|
||||
"billing.resubscribe-modal.interval.yearly": {
|
||||
"defaultMessage": "/Jahr"
|
||||
},
|
||||
"billing.resubscribe-modal.next-charge": {
|
||||
"defaultMessage": "Deine nächste Abrechnung erfolgt am <charge-date>{date}</charge-date>."
|
||||
},
|
||||
"billing.resubscribe-modal.plan-label": {
|
||||
"defaultMessage": "Plan"
|
||||
},
|
||||
"billing.resubscribe-modal.ram": {
|
||||
"defaultMessage": "{ramGb} GB RAM"
|
||||
},
|
||||
"billing.resubscribe-modal.resubscribe": {
|
||||
"defaultMessage": "Erneut abonnieren"
|
||||
},
|
||||
"billing.resubscribe-modal.storage": {
|
||||
"defaultMessage": "{storageGb} GB Speicherplatz"
|
||||
},
|
||||
"billing.resubscribe-modal.title": {
|
||||
"defaultMessage": "Server erneut abonnieren"
|
||||
},
|
||||
"button.accept": {
|
||||
"defaultMessage": "Akzeptieren"
|
||||
},
|
||||
@@ -251,6 +305,9 @@
|
||||
"content.confirm-deletion.header": {
|
||||
"defaultMessage": "Lösche {itemType}{count, plural, one {} other {s}}"
|
||||
},
|
||||
"content.confirm-modpack-update.admonition-body": {
|
||||
"defaultMessage": "{action, select, downgrade {Downgraden} other {Aktualiseren}} kann zu Kompatibilitätsproblemen führen. Mods oder Inhalte, die du zusätzlich zum Modpack hinzugefügt hast, bleiben erhalten, sind jedoch eventuell nicht mit der neuen Version kompatibel."
|
||||
},
|
||||
"content.confirm-modpack-update.admonition-header": {
|
||||
"defaultMessage": "{action, select, downgrade {Downgrade} other {Aktualisierungs}}warnung"
|
||||
},
|
||||
@@ -494,12 +551,42 @@
|
||||
"files.editor.file-saved-title": {
|
||||
"defaultMessage": "Datei gespeichert"
|
||||
},
|
||||
"files.editor.find-close": {
|
||||
"defaultMessage": "Schliessen"
|
||||
},
|
||||
"files.editor.find-in-file": {
|
||||
"defaultMessage": "Finden"
|
||||
},
|
||||
"files.editor.find-match-count": {
|
||||
"defaultMessage": "{current} von {total}"
|
||||
},
|
||||
"files.editor.find-next-match": {
|
||||
"defaultMessage": "Nächster Ergebnis"
|
||||
},
|
||||
"files.editor.find-no-results": {
|
||||
"defaultMessage": "Keine Ergebnisse"
|
||||
},
|
||||
"files.editor.find-previous-match": {
|
||||
"defaultMessage": "Vorheriges Ergebnis"
|
||||
},
|
||||
"files.editor.find-toggle-replace": {
|
||||
"defaultMessage": "Ersetzen umschalten"
|
||||
},
|
||||
"files.editor.log-url-copied-text": {
|
||||
"defaultMessage": "Deine Logdatei-URL wurde in die Zwischenablage kopiert."
|
||||
},
|
||||
"files.editor.log-url-copied-title": {
|
||||
"defaultMessage": "Log-URL kopiert"
|
||||
},
|
||||
"files.editor.replace": {
|
||||
"defaultMessage": "Ersetzen"
|
||||
},
|
||||
"files.editor.replace-all": {
|
||||
"defaultMessage": "Alle ersetzen"
|
||||
},
|
||||
"files.editor.replace-in-file": {
|
||||
"defaultMessage": "Ersetzen"
|
||||
},
|
||||
"files.editor.save-failed-text": {
|
||||
"defaultMessage": "Die Datei konnte nicht gespeichert werden."
|
||||
},
|
||||
@@ -557,9 +644,6 @@
|
||||
"files.layout.extraction-started-title": {
|
||||
"defaultMessage": "Extrahierung wurde gestartet"
|
||||
},
|
||||
"files.layout.loading": {
|
||||
"defaultMessage": "Dateien werden geladen..."
|
||||
},
|
||||
"files.layout.selected-count": {
|
||||
"defaultMessage": "{count} ausgewählt"
|
||||
},
|
||||
@@ -590,6 +674,9 @@
|
||||
"files.navbar.file-navigation": {
|
||||
"defaultMessage": "Dateinavigation"
|
||||
},
|
||||
"files.navbar.find-in-file": {
|
||||
"defaultMessage": "In Datei finden"
|
||||
},
|
||||
"files.navbar.home": {
|
||||
"defaultMessage": "Start"
|
||||
},
|
||||
@@ -896,6 +983,60 @@
|
||||
"hosting.content.failed-to-upload": {
|
||||
"defaultMessage": "Hochladen von Datei fehlgeschlagen"
|
||||
},
|
||||
"hosting.loader.failed-to-change-version": {
|
||||
"defaultMessage": "Änder der Modpackversion fehlgeschlagen"
|
||||
},
|
||||
"hosting.loader.failed-to-load-versions": {
|
||||
"defaultMessage": "Versionen konnten nicht geladen werden"
|
||||
},
|
||||
"hosting.loader.failed-to-reinstall": {
|
||||
"defaultMessage": "Neuinstallation des Modpack fehlgeschlagen"
|
||||
},
|
||||
"hosting.loader.failed-to-repair": {
|
||||
"defaultMessage": "Reparatur des Servers fehlgeschlagen"
|
||||
},
|
||||
"hosting.loader.failed-to-reset-to-onboarding": {
|
||||
"defaultMessage": "Server konnte nicht in den Onboarding-Zustand zurückgesetzt werden"
|
||||
},
|
||||
"hosting.loader.failed-to-save-settings": {
|
||||
"defaultMessage": "Speichern der Installationseinstellungen fehlgeschlagen"
|
||||
},
|
||||
"hosting.loader.failed-to-unlink": {
|
||||
"defaultMessage": "Entkoppeln von Modpack fehlgeschlagen"
|
||||
},
|
||||
"hosting.loader.loader-version": {
|
||||
"defaultMessage": "{loader, select, null {Loader} other {{loader}}}version"
|
||||
},
|
||||
"hosting.loader.repair-started-text": {
|
||||
"defaultMessage": "Deine Server-Installation wurde repariert."
|
||||
},
|
||||
"hosting.loader.repair-started-title": {
|
||||
"defaultMessage": "Reparatur abgeschlossen"
|
||||
},
|
||||
"hosting.loader.reset-server": {
|
||||
"defaultMessage": "Server zurücksetzen"
|
||||
},
|
||||
"hosting.loader.reset-server-description": {
|
||||
"defaultMessage": "Entfernt sämtliche daten von deinem Server, inklusive deiner Welten, Mods und Konfigurationsdateien. Sicherung bleiben und können wiederhergestellt werden."
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-button": {
|
||||
"defaultMessage": "Auf Onboarding zurücksetzen"
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-modal-description": {
|
||||
"defaultMessage": "Dadurch wird der Server in den Onboarding-Zustand zurückgesetzt, sodass die Einrichtung erneut abgeschlossen werden kann. Möchtest du wirklich fortfahren?"
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-modal-title": {
|
||||
"defaultMessage": "Auf Onboarding zurücksetzen"
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-success-description": {
|
||||
"defaultMessage": "Der Server wurde in den Onboarding-Ablauf zurückgesetzt."
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-success-title": {
|
||||
"defaultMessage": "Server auf Onboarding zurückgesetzt"
|
||||
},
|
||||
"hosting.loader.support-options-title": {
|
||||
"defaultMessage": "Supportoptionen"
|
||||
},
|
||||
"hosting.specs.burst": {
|
||||
"defaultMessage": "Spitzenauslastungen von bis zu {cpus} CPUs"
|
||||
},
|
||||
@@ -1214,6 +1355,9 @@
|
||||
"label.client-depends-warning": {
|
||||
"defaultMessage": "Diese Mod ist von einem clientseitigen Mod abhängig und kann beim Starten deines Servers zu Problemen führen"
|
||||
},
|
||||
"label.client-only-warning": {
|
||||
"defaultMessage": "Dieser ist ein clientseitiger Mod und kann beim Starten deines Servers zu Problemen führen"
|
||||
},
|
||||
"label.client-retained-warning": {
|
||||
"defaultMessage": "Dies ist eine clientseitige Mod, welche als Abhängigkeit installiert wurde und beim Starten deines Servers zu Problemen führen kann"
|
||||
},
|
||||
@@ -2576,6 +2720,234 @@
|
||||
"servers.backups.item.restore": {
|
||||
"defaultMessage": "Wiederherstellen"
|
||||
},
|
||||
"servers.busy.backup-creating": {
|
||||
"defaultMessage": "Backup-Erstellung im gange"
|
||||
},
|
||||
"servers.busy.backup-restoring": {
|
||||
"defaultMessage": "Wiederherstellung aus Sicherung läuft"
|
||||
},
|
||||
"servers.busy.installing": {
|
||||
"defaultMessage": "Server wird installiert"
|
||||
},
|
||||
"servers.busy.syncing-content": {
|
||||
"defaultMessage": "Inhalt wird synchronisiert"
|
||||
},
|
||||
"servers.list-empty.already-have-server-label": {
|
||||
"defaultMessage": "Du hast bereits einen Server?"
|
||||
},
|
||||
"servers.list-empty.learn-more-link": {
|
||||
"defaultMessage": "Erfahre mehr über Modrinth Hosting"
|
||||
},
|
||||
"servers.list-empty.modrinth-hosting-label": {
|
||||
"defaultMessage": "Modrinth Hosting"
|
||||
},
|
||||
"servers.list-empty.new-server-button": {
|
||||
"defaultMessage": "Neuer Server"
|
||||
},
|
||||
"servers.list-empty.no-servers-description": {
|
||||
"defaultMessage": "Installiere Mods, lade Freunde ein und spiel gemeinsam, alles über die Modrinth App."
|
||||
},
|
||||
"servers.list-empty.no-servers-title": {
|
||||
"defaultMessage": "Noch keine Server"
|
||||
},
|
||||
"servers.list-empty.one-click-mod-installs-description": {
|
||||
"defaultMessage": "Wähle deine Lieblingsmods aus, den Rest erledigen wir."
|
||||
},
|
||||
"servers.list-empty.one-click-mod-installs-title": {
|
||||
"defaultMessage": "Installation von Mods mit nur einem Klick"
|
||||
},
|
||||
"servers.list-empty.play-with-friends-description": {
|
||||
"defaultMessage": "Lade Freunde ein und konfiguriere sie direkt in der Modrinth App."
|
||||
},
|
||||
"servers.list-empty.play-with-friends-title": {
|
||||
"defaultMessage": "Spiel mit Freunden"
|
||||
},
|
||||
"servers.list-empty.sign-in-button": {
|
||||
"defaultMessage": "Anmelden"
|
||||
},
|
||||
"servers.list-empty.simple-setup-description": {
|
||||
"defaultMessage": "Richte deinen Server genauso wie eine Einzelspielerwelt ein."
|
||||
},
|
||||
"servers.list-empty.simple-setup-title": {
|
||||
"defaultMessage": "Einfache Einrichtung"
|
||||
},
|
||||
"servers.listing.copied-label": {
|
||||
"defaultMessage": "Kopiert"
|
||||
},
|
||||
"servers.listing.copy-code-tooltip": {
|
||||
"defaultMessage": "Code in die Zwischenablage kopieren"
|
||||
},
|
||||
"servers.listing.copy-id-label": {
|
||||
"defaultMessage": "ID kopieren"
|
||||
},
|
||||
"servers.listing.download-latest-backup-tooltip": {
|
||||
"defaultMessage": "Neueste Sicherung herunterladen"
|
||||
},
|
||||
"servers.listing.manage-billing-label": {
|
||||
"defaultMessage": "Abrechnung verwalten"
|
||||
},
|
||||
"servers.listing.new-label": {
|
||||
"defaultMessage": "Neu"
|
||||
},
|
||||
"servers.listing.notice.files-kept-for-download": {
|
||||
"defaultMessage": "Deine Dateien werden noch <days-remaining>{daysRemaining} {daysRemaining, plural, one {weiteren Tag} other {weitere Tage} }</days-remaining> aufbewahrt. Wende dich an den Support, um die Dateien herunterzuladen, bevor sie gelöscht werden. "
|
||||
},
|
||||
"servers.listing.notice.files-preserved-after-cancellation": {
|
||||
"defaultMessage": "Deine Dateien werden nach der Kündigung noch 30 Tage lang aufbewahrt."
|
||||
},
|
||||
"servers.listing.notice.moderated": {
|
||||
"defaultMessage": "Dein Server wurde aufgrund einer Moderationsmassnahme deaktiviert. "
|
||||
},
|
||||
"servers.listing.notice.pending-change": {
|
||||
"defaultMessage": "Dein Server wird am <date>{formattedDate}</date> auf den {planSize} Plan {verb}. "
|
||||
},
|
||||
"servers.listing.notice.provisioning": {
|
||||
"defaultMessage": "Bitte warte, während wir deinen Server einrichten. Dies kann bis zu 10 Minuten dauern."
|
||||
},
|
||||
"servers.listing.notice.subscription-cancelled": {
|
||||
"defaultMessage": "Dein Abonnement wurde gekündigt."
|
||||
},
|
||||
"servers.listing.notice.subscription-cancelled-on-date": {
|
||||
"defaultMessage": "Dein Abonnement wurde am <date>{formattedDate}</date> gekündigt. "
|
||||
},
|
||||
"servers.listing.notice.subscription-cancelled-payment-failed": {
|
||||
"defaultMessage": "Dein Abonnement wurde aufgrund eines Problems bei der Zahlung gekündigt."
|
||||
},
|
||||
"servers.listing.notice.subscription-cancelled-payment-failed-on-date": {
|
||||
"defaultMessage": "Dein Abonnement wurde am <date>{formattedDate}</date> aufgrund eines Problems bei der Zahlung gekündigt. "
|
||||
},
|
||||
"servers.listing.notice.subscription-set-to-cancel": {
|
||||
"defaultMessage": "Dein Abonnement läuft aus."
|
||||
},
|
||||
"servers.listing.notice.subscription-set-to-cancel-on-date": {
|
||||
"defaultMessage": "Dein Abonnement läuft am <date>{formattedDate}</date> aus. "
|
||||
},
|
||||
"servers.listing.notice.suspended": {
|
||||
"defaultMessage": "Dein Server wurde deaktiviert. Bitte wende dich an den Modrinth-Support, um weitere Informationen zu erhalten."
|
||||
},
|
||||
"servers.listing.notice.upgrading": {
|
||||
"defaultMessage": "Die Hardware deines Servers wird derzeit verbessert und ist in Kürze wieder verfügbar."
|
||||
},
|
||||
"servers.listing.resubscribe-label": {
|
||||
"defaultMessage": "Erneut abonnieren"
|
||||
},
|
||||
"servers.listing.server-icon-alt": {
|
||||
"defaultMessage": "Server-Icon"
|
||||
},
|
||||
"servers.listing.support-label": {
|
||||
"defaultMessage": "Support"
|
||||
},
|
||||
"servers.listing.using-project-label": {
|
||||
"defaultMessage": "Nutzt {projectTitle}"
|
||||
},
|
||||
"servers.manage.checking-for-new-servers": {
|
||||
"defaultMessage": "Suche nach neuen Servern..."
|
||||
},
|
||||
"servers.manage.confirm-leave.body": {
|
||||
"defaultMessage": "Eine Datei wird derzeit hochgeladen. Das Verlassen dieser Seite bricht den Upload ab."
|
||||
},
|
||||
"servers.manage.confirm-leave.upload-in-progress": {
|
||||
"defaultMessage": "Hochladen läuft"
|
||||
},
|
||||
"servers.manage.contact-support-button": {
|
||||
"defaultMessage": "Modrinth-Support kontaktieren"
|
||||
},
|
||||
"servers.manage.error.alert-notice": {
|
||||
"defaultMessage": "Unsere Systeme benachrichtigen unser Team automatisch, wenn ein Problem auftritt. Wir arbeiten bereits daran, sie wieder online zu bringen."
|
||||
},
|
||||
"servers.manage.error.description": {
|
||||
"defaultMessage": "Wir haben möglicherweise temporäre Probleme mit unseren Servern."
|
||||
},
|
||||
"servers.manage.error.details": {
|
||||
"defaultMessage": "Fehlerdetails:"
|
||||
},
|
||||
"servers.manage.error.queue-notice": {
|
||||
"defaultMessage": "Falls du deinen Server von Modrinth Hosting erst vor kurzem gekauft hast, befindet er sich derzeit in der Warteschlange und wird hier angezeigt, sobald er bereit ist. <warning>Versuche nicht, einen neuen Server zu kaufen.</warning>"
|
||||
},
|
||||
"servers.manage.error.support-notice": {
|
||||
"defaultMessage": "Falls du individuelle Unterstützung bezüglich des Status deines Servers benötigst, wende dich bitte an den Modrinth-Support."
|
||||
},
|
||||
"servers.manage.error.title": {
|
||||
"defaultMessage": "Server konnten nicht geladen werden"
|
||||
},
|
||||
"servers.manage.handle-error.title": {
|
||||
"defaultMessage": "Es ist ein Fehler aufgetreten"
|
||||
},
|
||||
"servers.manage.new-server-button": {
|
||||
"defaultMessage": "Neuer Server"
|
||||
},
|
||||
"servers.manage.no-servers-found": {
|
||||
"defaultMessage": "Keine Server gefunden."
|
||||
},
|
||||
"servers.manage.purchase-unavailable.text": {
|
||||
"defaultMessage": "Zahlungsinformationen werden noch geladen. Der Bezahlvorgang wird gestartet, sobald sie bereit sind."
|
||||
},
|
||||
"servers.manage.purchase-unavailable.title": {
|
||||
"defaultMessage": "Kauf nicht verfügbar"
|
||||
},
|
||||
"servers.manage.reload-button": {
|
||||
"defaultMessage": "Neu laden"
|
||||
},
|
||||
"servers.manage.resubscribe-error.text": {
|
||||
"defaultMessage": "Beim erneuten Abonnieren deines Modrinth-Servers ist ein Fehler aufgetreten."
|
||||
},
|
||||
"servers.manage.resubscribe-error.title": {
|
||||
"defaultMessage": "Fehler beim erneuten Abonnieren"
|
||||
},
|
||||
"servers.manage.resubscribe-submitted.text": {
|
||||
"defaultMessage": "Falls der Server momentan deaktiviert ist, kann es bis zu 10 Minuten dauern, bis ein weiterer Zahlungsversuch unternommen wird."
|
||||
},
|
||||
"servers.manage.resubscribe-submitted.title": {
|
||||
"defaultMessage": "Anfrage zum erneuten Abonnieren übermittelt"
|
||||
},
|
||||
"servers.manage.resubscribe-success.text": {
|
||||
"defaultMessage": "Server-Abonnement erfolgreich erneuert"
|
||||
},
|
||||
"servers.manage.resubscribe-success.title": {
|
||||
"defaultMessage": "Erfolg"
|
||||
},
|
||||
"servers.manage.search-placeholder": {
|
||||
"defaultMessage": "Durchsuche {count} Server..."
|
||||
},
|
||||
"servers.manage.servers-title": {
|
||||
"defaultMessage": "Modrinth Hosting"
|
||||
},
|
||||
"servers.manage.settings-hint.description": {
|
||||
"defaultMessage": "Ab sofort sind sie hier zu finden!"
|
||||
},
|
||||
"servers.manage.settings-hint.dismiss": {
|
||||
"defaultMessage": "Nicht erneut anzeigen"
|
||||
},
|
||||
"servers.manage.settings-hint.title": {
|
||||
"defaultMessage": "Deine Server-Einstellungen wurden verschoben"
|
||||
},
|
||||
"servers.medal-listing.countdown.remaining": {
|
||||
"defaultMessage": "<days-count>{days}</days-count> {days, plural, one {Tag} other {Tage}} <hours-count>{hours}</hours-count> {hours, plural, one {Stunde} other {Stunden}} <minutes-count>{minutes}</minutes-count> {minutes, plural, one {Minute} other {Minuten}} <seconds-count>{seconds}</seconds-count> {seconds, plural, one {Sekunde} other {Sekunden}} übrig..."
|
||||
},
|
||||
"servers.medal-listing.new-server-label": {
|
||||
"defaultMessage": "Neuer Server"
|
||||
},
|
||||
"servers.medal-listing.notice.medal-trial-ended": {
|
||||
"defaultMessage": "Die Testphase deines Medal-Servers ist abgelaufen und dein Server wurde deaktiviert. Bitte aktualisiere, um deinen Server weiterhin nutzen zu können."
|
||||
},
|
||||
"servers.medal-listing.notice.suspended": {
|
||||
"defaultMessage": "Dein Server wurde deaktiviert. Bitte aktualisiere deine Zahlungsdaten oder wende dich an den Modrinth-Support, um weitere Informationen zu erhalten."
|
||||
},
|
||||
"servers.medal-listing.notice.suspended-with-reason": {
|
||||
"defaultMessage": "Dein Server wurde deaktiviert: {reason}. Bitte aktualisiere deine Zahlungsdaten oder wende dich an den Modrinth-Support, um weitere Informationen zu erhalten."
|
||||
},
|
||||
"servers.medal-listing.notice.upgrading": {
|
||||
"defaultMessage": "Die Hardware deines Servers wird derzeit aktualisiert und ist in Kürze wieder verfügbar."
|
||||
},
|
||||
"servers.medal-listing.server-icon-alt": {
|
||||
"defaultMessage": "Server-Icon"
|
||||
},
|
||||
"servers.medal-listing.upgrade-button": {
|
||||
"defaultMessage": "Verbessern"
|
||||
},
|
||||
"servers.medal-listing.using-project-label": {
|
||||
"defaultMessage": "Nutzt {projectTitle}"
|
||||
},
|
||||
"servers.notice.dismiss": {
|
||||
"defaultMessage": "Ausblenden"
|
||||
},
|
||||
@@ -2603,6 +2975,12 @@
|
||||
"servers.notice.undismissable": {
|
||||
"defaultMessage": "Nicht entfernbar"
|
||||
},
|
||||
"servers.purchase.notification.success.text": {
|
||||
"defaultMessage": "Dein Kauf bei Modrinth Hosting wurde erfolgreich abgeschlossen."
|
||||
},
|
||||
"servers.purchase.notification.success.title": {
|
||||
"defaultMessage": "Kauf erfolgreich"
|
||||
},
|
||||
"servers.purchase.step.payment.description": {
|
||||
"defaultMessage": "Dir werden noch keine Kosten berechnet."
|
||||
},
|
||||
@@ -2612,15 +2990,27 @@
|
||||
"servers.purchase.step.payment.title": {
|
||||
"defaultMessage": "Zahlungsmethode"
|
||||
},
|
||||
"servers.purchase.step.plan.billing-subtitle": {
|
||||
"defaultMessage": "Verfügbar in Nordamerika, Europa und Südostasien."
|
||||
},
|
||||
"servers.purchase.step.plan.custom.desc": {
|
||||
"defaultMessage": "Wähle einen benutzerdefinierten Plan mit genau den Eigenschaften, die du brauchst."
|
||||
},
|
||||
"servers.purchase.step.plan.custom.heading": {
|
||||
"defaultMessage": "Du weißt genau, was du brauchst?"
|
||||
},
|
||||
"servers.purchase.step.plan.get-started": {
|
||||
"defaultMessage": "Loslegen"
|
||||
},
|
||||
"servers.purchase.step.plan.large": {
|
||||
"defaultMessage": "Gross"
|
||||
},
|
||||
"servers.purchase.step.plan.large.desc": {
|
||||
"defaultMessage": "Ideal für 15 bis 25 Spieler, Modpackete oder umfangreiche Modding-Projekte."
|
||||
},
|
||||
"servers.purchase.step.plan.medium": {
|
||||
"defaultMessage": "Mittel"
|
||||
},
|
||||
"servers.purchase.step.plan.medium.desc": {
|
||||
"defaultMessage": "Ideal für 6 bis 15 Spieler und mehrere Mods."
|
||||
},
|
||||
@@ -2630,12 +3020,18 @@
|
||||
"servers.purchase.step.plan.select": {
|
||||
"defaultMessage": "Plan wählen"
|
||||
},
|
||||
"servers.purchase.step.plan.small": {
|
||||
"defaultMessage": "Klein"
|
||||
},
|
||||
"servers.purchase.step.plan.small.desc": {
|
||||
"defaultMessage": "Perfekt für 1 bis 5 Freunde mit ein paar leichten Mods."
|
||||
},
|
||||
"servers.purchase.step.plan.title": {
|
||||
"defaultMessage": "Plan"
|
||||
},
|
||||
"servers.purchase.step.plan.your-current-plan": {
|
||||
"defaultMessage": "Dein aktueller Plan"
|
||||
},
|
||||
"servers.purchase.step.region.title": {
|
||||
"defaultMessage": "Region"
|
||||
},
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
"affiliate.viewAnalytics": {
|
||||
"defaultMessage": "Analysen anzeigen"
|
||||
},
|
||||
"app.server-settings.failed-to-load-server": {
|
||||
"defaultMessage": "Server-Einstellungen konnten nicht geladen werden"
|
||||
},
|
||||
"badge.alpha": {
|
||||
"defaultMessage": "Alpha"
|
||||
},
|
||||
"badge.beta": {
|
||||
"defaultMessage": "Beta"
|
||||
},
|
||||
@@ -41,6 +47,54 @@
|
||||
"badge.new": {
|
||||
"defaultMessage": "Neu"
|
||||
},
|
||||
"billing.resubscribe-modal.cancel": {
|
||||
"defaultMessage": "Abbrechen"
|
||||
},
|
||||
"billing.resubscribe-modal.cpus": {
|
||||
"defaultMessage": "{sharedCpus} Geteilte CPUs"
|
||||
},
|
||||
"billing.resubscribe-modal.description": {
|
||||
"defaultMessage": "Du bist dabei, dein Abonnement für <server-name>{serverName}</server-name> zu verlängern. Dein Abonnement wird reaktiviert und dein Server läuft ohne Unterbrechung weiter."
|
||||
},
|
||||
"billing.resubscribe-modal.error.text": {
|
||||
"defaultMessage": "Das Abonnement konnte nicht erneuert werden, das Laden von Details zum Abonnement ist fehlgeschlagen."
|
||||
},
|
||||
"billing.resubscribe-modal.error.title": {
|
||||
"defaultMessage": "Fehler"
|
||||
},
|
||||
"billing.resubscribe-modal.failed-load": {
|
||||
"defaultMessage": "Details zum Abonnement konnten nicht geladen werden."
|
||||
},
|
||||
"billing.resubscribe-modal.interval.five-days": {
|
||||
"defaultMessage": "/5 Tage"
|
||||
},
|
||||
"billing.resubscribe-modal.interval.monthly": {
|
||||
"defaultMessage": "/Monat"
|
||||
},
|
||||
"billing.resubscribe-modal.interval.quarterly": {
|
||||
"defaultMessage": "/Quartal"
|
||||
},
|
||||
"billing.resubscribe-modal.interval.yearly": {
|
||||
"defaultMessage": "/Jahr"
|
||||
},
|
||||
"billing.resubscribe-modal.next-charge": {
|
||||
"defaultMessage": "Deine nächste Abrechnung erfolgt am <charge-date>{date}</charge-date>."
|
||||
},
|
||||
"billing.resubscribe-modal.plan-label": {
|
||||
"defaultMessage": "Plan"
|
||||
},
|
||||
"billing.resubscribe-modal.ram": {
|
||||
"defaultMessage": "{ramGb} GB RAM"
|
||||
},
|
||||
"billing.resubscribe-modal.resubscribe": {
|
||||
"defaultMessage": "Erneut abonnieren"
|
||||
},
|
||||
"billing.resubscribe-modal.storage": {
|
||||
"defaultMessage": "{storageGb} GB Speicherplatz"
|
||||
},
|
||||
"billing.resubscribe-modal.title": {
|
||||
"defaultMessage": "Server erneut abonnieren"
|
||||
},
|
||||
"button.accept": {
|
||||
"defaultMessage": "Akzeptieren"
|
||||
},
|
||||
@@ -251,6 +305,9 @@
|
||||
"content.confirm-deletion.header": {
|
||||
"defaultMessage": "Lösche {itemType}{count, plural, one {} other {s}}"
|
||||
},
|
||||
"content.confirm-modpack-update.admonition-body": {
|
||||
"defaultMessage": "{action, select, downgrade {Downgraden} other {Aktualiseren}} kann zu Kompatibilitätsproblemen führen. Mods oder Inhalte, die du zusätzlich zum Modpack hinzugefügt hast, bleiben erhalten, sind jedoch eventuell nicht mit der neuen Version kompatibel."
|
||||
},
|
||||
"content.confirm-modpack-update.admonition-header": {
|
||||
"defaultMessage": "{action, select, downgrade {Downgrade} other {Aktualisierungs}}warnung"
|
||||
},
|
||||
@@ -494,12 +551,42 @@
|
||||
"files.editor.file-saved-title": {
|
||||
"defaultMessage": "Datei gespeichert"
|
||||
},
|
||||
"files.editor.find-close": {
|
||||
"defaultMessage": "Schließen"
|
||||
},
|
||||
"files.editor.find-in-file": {
|
||||
"defaultMessage": "Finden"
|
||||
},
|
||||
"files.editor.find-match-count": {
|
||||
"defaultMessage": "{current} von {total}"
|
||||
},
|
||||
"files.editor.find-next-match": {
|
||||
"defaultMessage": "Nächstes Ergebnis"
|
||||
},
|
||||
"files.editor.find-no-results": {
|
||||
"defaultMessage": "Keine Ergebnisse"
|
||||
},
|
||||
"files.editor.find-previous-match": {
|
||||
"defaultMessage": "Vorheriges Ergebnis"
|
||||
},
|
||||
"files.editor.find-toggle-replace": {
|
||||
"defaultMessage": "Ersetzen umschalten"
|
||||
},
|
||||
"files.editor.log-url-copied-text": {
|
||||
"defaultMessage": "Deine Logdatei-URL wurde in die Zwischenablage kopiert."
|
||||
},
|
||||
"files.editor.log-url-copied-title": {
|
||||
"defaultMessage": "Log-URL kopiert"
|
||||
},
|
||||
"files.editor.replace": {
|
||||
"defaultMessage": "Ersetzen"
|
||||
},
|
||||
"files.editor.replace-all": {
|
||||
"defaultMessage": "Alle ersetzen"
|
||||
},
|
||||
"files.editor.replace-in-file": {
|
||||
"defaultMessage": "Ersetzen"
|
||||
},
|
||||
"files.editor.save-failed-text": {
|
||||
"defaultMessage": "Die Datei konnte nicht gespeichert werden."
|
||||
},
|
||||
@@ -557,9 +644,6 @@
|
||||
"files.layout.extraction-started-title": {
|
||||
"defaultMessage": "Extrahierung wurde gestartet"
|
||||
},
|
||||
"files.layout.loading": {
|
||||
"defaultMessage": "Dateien werden geladen..."
|
||||
},
|
||||
"files.layout.selected-count": {
|
||||
"defaultMessage": "{count} ausgewählt"
|
||||
},
|
||||
@@ -590,6 +674,9 @@
|
||||
"files.navbar.file-navigation": {
|
||||
"defaultMessage": "Dateinavigation"
|
||||
},
|
||||
"files.navbar.find-in-file": {
|
||||
"defaultMessage": "In Datei finden"
|
||||
},
|
||||
"files.navbar.home": {
|
||||
"defaultMessage": "Start"
|
||||
},
|
||||
@@ -896,6 +983,60 @@
|
||||
"hosting.content.failed-to-upload": {
|
||||
"defaultMessage": "Hochladen von Datei fehlgeschlagen"
|
||||
},
|
||||
"hosting.loader.failed-to-change-version": {
|
||||
"defaultMessage": "Modpack-Version konnte nicht geändert werden"
|
||||
},
|
||||
"hosting.loader.failed-to-load-versions": {
|
||||
"defaultMessage": "Versionen konnten nicht geladen werden"
|
||||
},
|
||||
"hosting.loader.failed-to-reinstall": {
|
||||
"defaultMessage": "Modpack konnte nicht neuinstalliert werden"
|
||||
},
|
||||
"hosting.loader.failed-to-repair": {
|
||||
"defaultMessage": "Server konnte nicht repariert werden"
|
||||
},
|
||||
"hosting.loader.failed-to-reset-to-onboarding": {
|
||||
"defaultMessage": "Server konnte nicht in den Onboarding-Zustand zurückgesetzt werden"
|
||||
},
|
||||
"hosting.loader.failed-to-save-settings": {
|
||||
"defaultMessage": "Installationseinstellungen konnten nicht gespeichert werden"
|
||||
},
|
||||
"hosting.loader.failed-to-unlink": {
|
||||
"defaultMessage": "Modpack konnte nicht getrennt werden"
|
||||
},
|
||||
"hosting.loader.loader-version": {
|
||||
"defaultMessage": "{loader, select, null {Loader} other {{loader}}}-Version"
|
||||
},
|
||||
"hosting.loader.repair-started-text": {
|
||||
"defaultMessage": "Deine Serverinstallation wurde repariert."
|
||||
},
|
||||
"hosting.loader.repair-started-title": {
|
||||
"defaultMessage": "Reparatur abgeschlossen"
|
||||
},
|
||||
"hosting.loader.reset-server": {
|
||||
"defaultMessage": "Server zurücksetzen"
|
||||
},
|
||||
"hosting.loader.reset-server-description": {
|
||||
"defaultMessage": "Löscht alle Daten auf deinem Server, einschließlich deiner Welten, Mods und Konfigurationsdateien. Die Sicherungen bleiben erhalten und können wiederhergestellt werden."
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-button": {
|
||||
"defaultMessage": "Auf Onboarding zurücksetzen"
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-modal-description": {
|
||||
"defaultMessage": "Dadurch wird der Server in den Onboarding-Zustand zurückgesetzt, sodass die Einrichtung erneut abgeschlossen werden kann. Möchtest du wirklich fortfahren?"
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-modal-title": {
|
||||
"defaultMessage": "Auf Onboarding zurücksetzen"
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-success-description": {
|
||||
"defaultMessage": "Der Server wurde in den Onboarding-Ablauf zurückgesetzt."
|
||||
},
|
||||
"hosting.loader.reset-to-onboarding-success-title": {
|
||||
"defaultMessage": "Server auf Onboarding zurückgesetzt"
|
||||
},
|
||||
"hosting.loader.support-options-title": {
|
||||
"defaultMessage": "Supportoptionen"
|
||||
},
|
||||
"hosting.specs.burst": {
|
||||
"defaultMessage": "Spitzenlasten von bis zu {cpus} CPUs"
|
||||
},
|
||||
@@ -1214,6 +1355,9 @@
|
||||
"label.client-depends-warning": {
|
||||
"defaultMessage": "Dieser Mod ist von einem clientseitigen Mod abhängig und kann beim Starten deines Servers zu Problemen führen"
|
||||
},
|
||||
"label.client-only-warning": {
|
||||
"defaultMessage": "Dieser ist ein clientseitiger Mod und kann beim Starten deines Servers zu Problemen führen"
|
||||
},
|
||||
"label.client-retained-warning": {
|
||||
"defaultMessage": "Das ist ein clientseitiger Mod, der als Abhängigkeit installiert wurde und beim Starten deines Servers zu Problemen führen kann"
|
||||
},
|
||||
@@ -2576,6 +2720,234 @@
|
||||
"servers.backups.item.restore": {
|
||||
"defaultMessage": "Wiederherstellen"
|
||||
},
|
||||
"servers.busy.backup-creating": {
|
||||
"defaultMessage": "Sicherung wird erstellt"
|
||||
},
|
||||
"servers.busy.backup-restoring": {
|
||||
"defaultMessage": "Wiederherstellung aus Sicherung läuft"
|
||||
},
|
||||
"servers.busy.installing": {
|
||||
"defaultMessage": "Server wird installiert"
|
||||
},
|
||||
"servers.busy.syncing-content": {
|
||||
"defaultMessage": "Inhalt wird synchronisiert"
|
||||
},
|
||||
"servers.list-empty.already-have-server-label": {
|
||||
"defaultMessage": "Du hast bereits einen Server?"
|
||||
},
|
||||
"servers.list-empty.learn-more-link": {
|
||||
"defaultMessage": "Erfahre mehr über Modrinth Hosting"
|
||||
},
|
||||
"servers.list-empty.modrinth-hosting-label": {
|
||||
"defaultMessage": "Modrinth Hosting"
|
||||
},
|
||||
"servers.list-empty.new-server-button": {
|
||||
"defaultMessage": "Neuer Server"
|
||||
},
|
||||
"servers.list-empty.no-servers-description": {
|
||||
"defaultMessage": "Installiere Mods, lade Freunde ein und spiel gemeinsam, alles über die Modrinth App."
|
||||
},
|
||||
"servers.list-empty.no-servers-title": {
|
||||
"defaultMessage": "Noch keine Server"
|
||||
},
|
||||
"servers.list-empty.one-click-mod-installs-description": {
|
||||
"defaultMessage": "Wähle deine Lieblingsmods aus, den Rest erledigen wir."
|
||||
},
|
||||
"servers.list-empty.one-click-mod-installs-title": {
|
||||
"defaultMessage": "Installation von Mods mit nur einem Klick"
|
||||
},
|
||||
"servers.list-empty.play-with-friends-description": {
|
||||
"defaultMessage": "Lade Freunde ein und konfiguriere sie direkt in der Modrinth App."
|
||||
},
|
||||
"servers.list-empty.play-with-friends-title": {
|
||||
"defaultMessage": "Spiel mit Freunden"
|
||||
},
|
||||
"servers.list-empty.sign-in-button": {
|
||||
"defaultMessage": "Anmelden"
|
||||
},
|
||||
"servers.list-empty.simple-setup-description": {
|
||||
"defaultMessage": "Richte deinen Server genauso wie eine Einzelspielerwelt ein."
|
||||
},
|
||||
"servers.list-empty.simple-setup-title": {
|
||||
"defaultMessage": "Einfache Einrichtung"
|
||||
},
|
||||
"servers.listing.copied-label": {
|
||||
"defaultMessage": "Kopiert"
|
||||
},
|
||||
"servers.listing.copy-code-tooltip": {
|
||||
"defaultMessage": "Code in die Zwischenablage kopieren"
|
||||
},
|
||||
"servers.listing.copy-id-label": {
|
||||
"defaultMessage": "ID kopieren"
|
||||
},
|
||||
"servers.listing.download-latest-backup-tooltip": {
|
||||
"defaultMessage": "Neueste Sicherung herunterladen"
|
||||
},
|
||||
"servers.listing.manage-billing-label": {
|
||||
"defaultMessage": "Abrechnung verwalten"
|
||||
},
|
||||
"servers.listing.new-label": {
|
||||
"defaultMessage": "Neu"
|
||||
},
|
||||
"servers.listing.notice.files-kept-for-download": {
|
||||
"defaultMessage": "Deine Dateien werden noch <days-remaining>{daysRemaining} {daysRemaining, plural, one {weiteren Tag} other {weitere Tage} }</days-remaining> aufbewahrt. Wende dich an den Support, um die Dateien herunterzuladen, bevor sie gelöscht werden. "
|
||||
},
|
||||
"servers.listing.notice.files-preserved-after-cancellation": {
|
||||
"defaultMessage": "Deine Dateien werden nach der Kündigung noch 30 Tage lang aufbewahrt."
|
||||
},
|
||||
"servers.listing.notice.moderated": {
|
||||
"defaultMessage": "Dein Server wurde aufgrund einer Moderationsmaßnahme deaktiviert. "
|
||||
},
|
||||
"servers.listing.notice.pending-change": {
|
||||
"defaultMessage": "Dein Server wird am <date>{formattedDate}</date> auf den {planSize} Plan {verb}. "
|
||||
},
|
||||
"servers.listing.notice.provisioning": {
|
||||
"defaultMessage": "Bitte warte, während wir deinen Server einrichten. Dies kann bis zu 10 Minuten dauern."
|
||||
},
|
||||
"servers.listing.notice.subscription-cancelled": {
|
||||
"defaultMessage": "Dein Abonnement wurde gekündigt."
|
||||
},
|
||||
"servers.listing.notice.subscription-cancelled-on-date": {
|
||||
"defaultMessage": "Dein Abonnement wurde am <date>{formattedDate}</date> gekündigt. "
|
||||
},
|
||||
"servers.listing.notice.subscription-cancelled-payment-failed": {
|
||||
"defaultMessage": "Dein Abonnement wurde aufgrund eines Problems bei der Zahlung gekündigt."
|
||||
},
|
||||
"servers.listing.notice.subscription-cancelled-payment-failed-on-date": {
|
||||
"defaultMessage": "Dein Abonnement wurde am <date>{formattedDate}</date> aufgrund eines Problems bei der Zahlung gekündigt. "
|
||||
},
|
||||
"servers.listing.notice.subscription-set-to-cancel": {
|
||||
"defaultMessage": "Dein Abonnement läuft aus."
|
||||
},
|
||||
"servers.listing.notice.subscription-set-to-cancel-on-date": {
|
||||
"defaultMessage": "Dein Abonnement läuft am <date>{formattedDate}</date> aus. "
|
||||
},
|
||||
"servers.listing.notice.suspended": {
|
||||
"defaultMessage": "Dein Server wurde deaktiviert. Bitte wende dich an den Modrinth-Support, um weitere Informationen zu erhalten."
|
||||
},
|
||||
"servers.listing.notice.upgrading": {
|
||||
"defaultMessage": "Die Hardware deines Servers wird derzeit aktualisiert und ist in Kürze wieder verfügbar."
|
||||
},
|
||||
"servers.listing.resubscribe-label": {
|
||||
"defaultMessage": "Erneut abonnieren"
|
||||
},
|
||||
"servers.listing.server-icon-alt": {
|
||||
"defaultMessage": "Server-Icon"
|
||||
},
|
||||
"servers.listing.support-label": {
|
||||
"defaultMessage": "Support"
|
||||
},
|
||||
"servers.listing.using-project-label": {
|
||||
"defaultMessage": "Nutzt {projectTitle}"
|
||||
},
|
||||
"servers.manage.checking-for-new-servers": {
|
||||
"defaultMessage": "Suche nach neuen Servern..."
|
||||
},
|
||||
"servers.manage.confirm-leave.body": {
|
||||
"defaultMessage": "Eine Datei wird derzeit hochgeladen. Das Verlassen dieser Seite bricht den Upload ab."
|
||||
},
|
||||
"servers.manage.confirm-leave.upload-in-progress": {
|
||||
"defaultMessage": "Hochladen läuft"
|
||||
},
|
||||
"servers.manage.contact-support-button": {
|
||||
"defaultMessage": "Kontaktiere den Modrinth-Support"
|
||||
},
|
||||
"servers.manage.error.alert-notice": {
|
||||
"defaultMessage": "Unsere Systeme benachrichtigen unser Team automatisch, wenn ein Problem auftritt. Wir arbeiten bereits daran, sie wieder online zu bringen."
|
||||
},
|
||||
"servers.manage.error.description": {
|
||||
"defaultMessage": "Wir haben möglicherweise temporäre Probleme mit unseren Servern."
|
||||
},
|
||||
"servers.manage.error.details": {
|
||||
"defaultMessage": "Fehlerdetails:"
|
||||
},
|
||||
"servers.manage.error.queue-notice": {
|
||||
"defaultMessage": "Falls du deinen Server von Modrinth Hosting erst vor kurzem gekauft hast, befindet er sich derzeit in der Warteschlange und wird hier angezeigt, sobald er bereit ist. <warning>Versuche nicht, einen neuen Server zu kaufen.</warning>"
|
||||
},
|
||||
"servers.manage.error.support-notice": {
|
||||
"defaultMessage": "Falls du individuelle Unterstützung bezüglich des Status deines Servers benötigst, wende dich bitte an den Modrinth-Support."
|
||||
},
|
||||
"servers.manage.error.title": {
|
||||
"defaultMessage": "Server konnten nicht geladen werden"
|
||||
},
|
||||
"servers.manage.handle-error.title": {
|
||||
"defaultMessage": "Es ist ein Fehler aufgetreten"
|
||||
},
|
||||
"servers.manage.new-server-button": {
|
||||
"defaultMessage": "Neuer Server"
|
||||
},
|
||||
"servers.manage.no-servers-found": {
|
||||
"defaultMessage": "Keine Server gefunden."
|
||||
},
|
||||
"servers.manage.purchase-unavailable.text": {
|
||||
"defaultMessage": "Zahlungsinformationen werden noch geladen. Der Bezahlvorgang wird gestartet, sobald sie bereit sind."
|
||||
},
|
||||
"servers.manage.purchase-unavailable.title": {
|
||||
"defaultMessage": "Kauf nicht verfügbar"
|
||||
},
|
||||
"servers.manage.reload-button": {
|
||||
"defaultMessage": "Neu laden"
|
||||
},
|
||||
"servers.manage.resubscribe-error.text": {
|
||||
"defaultMessage": "Beim erneuten Abonnieren deines Modrinth-Servers ist ein Fehler aufgetreten."
|
||||
},
|
||||
"servers.manage.resubscribe-error.title": {
|
||||
"defaultMessage": "Fehler beim erneuten Abonnieren"
|
||||
},
|
||||
"servers.manage.resubscribe-submitted.text": {
|
||||
"defaultMessage": "Falls der Server momentan deaktiviert ist, kann es bis zu 10 Minuten dauern, bis ein weiterer Zahlungsversuch unternommen wird."
|
||||
},
|
||||
"servers.manage.resubscribe-submitted.title": {
|
||||
"defaultMessage": "Anfrage zum erneuten Abonnieren übermittelt"
|
||||
},
|
||||
"servers.manage.resubscribe-success.text": {
|
||||
"defaultMessage": "Server-Abonnement erfolgreich erneuert"
|
||||
},
|
||||
"servers.manage.resubscribe-success.title": {
|
||||
"defaultMessage": "Erfolg"
|
||||
},
|
||||
"servers.manage.search-placeholder": {
|
||||
"defaultMessage": "Durchsuche {count} Server"
|
||||
},
|
||||
"servers.manage.servers-title": {
|
||||
"defaultMessage": "Modrinth Hosting"
|
||||
},
|
||||
"servers.manage.settings-hint.description": {
|
||||
"defaultMessage": "Ab sofort sind sie hier zu finden!"
|
||||
},
|
||||
"servers.manage.settings-hint.dismiss": {
|
||||
"defaultMessage": "Nicht erneut anzeigen"
|
||||
},
|
||||
"servers.manage.settings-hint.title": {
|
||||
"defaultMessage": "Deine Server-Einstellungen wurden verschoben"
|
||||
},
|
||||
"servers.medal-listing.countdown.remaining": {
|
||||
"defaultMessage": "<days-count>{days}</days-count> {days, plural, one {Tag} other {Tage}} <hours-count>{hours}</hours-count> {hours, plural, one {Stunde} other {Stunden}} <minutes-count>{minutes}</minutes-count> {minutes, plural, one {Minute} other {Minuten}} <seconds-count>{seconds}</seconds-count> {seconds, plural, one {Sekunde} other {Sekunden}} übrig..."
|
||||
},
|
||||
"servers.medal-listing.new-server-label": {
|
||||
"defaultMessage": "Neuer Server"
|
||||
},
|
||||
"servers.medal-listing.notice.medal-trial-ended": {
|
||||
"defaultMessage": "Die Testphase deines Medal-Servers ist abgelaufen und dein Server wurde deaktiviert. Bitte aktualisiere, um deinen Server weiterhin nutzen zu können."
|
||||
},
|
||||
"servers.medal-listing.notice.suspended": {
|
||||
"defaultMessage": "Dein Server wurde deaktiviert. Bitte aktualisiere deine Zahlungsdaten oder wende dich an den Modrinth-Support, um weitere Informationen zu erhalten."
|
||||
},
|
||||
"servers.medal-listing.notice.suspended-with-reason": {
|
||||
"defaultMessage": "Dein Server wurde deaktiviert: {reason}. Bitte aktualisiere deine Zahlungsdaten oder wende dich an den Modrinth-Support, um weitere Informationen zu erhalten."
|
||||
},
|
||||
"servers.medal-listing.notice.upgrading": {
|
||||
"defaultMessage": "Die Hardware deines Servers wird derzeit aktualisiert und ist in Kürze wieder verfügbar."
|
||||
},
|
||||
"servers.medal-listing.server-icon-alt": {
|
||||
"defaultMessage": "Server-Icon"
|
||||
},
|
||||
"servers.medal-listing.upgrade-button": {
|
||||
"defaultMessage": "Verbessern"
|
||||
},
|
||||
"servers.medal-listing.using-project-label": {
|
||||
"defaultMessage": "Nutzt {projectTitle}"
|
||||
},
|
||||
"servers.notice.dismiss": {
|
||||
"defaultMessage": "Ausblenden"
|
||||
},
|
||||
@@ -2603,6 +2975,12 @@
|
||||
"servers.notice.undismissable": {
|
||||
"defaultMessage": "Nicht ausblendbar"
|
||||
},
|
||||
"servers.purchase.notification.success.text": {
|
||||
"defaultMessage": "Dein Kauf bei Modrinth Hosting wurde erfolgreich abgeschlossen."
|
||||
},
|
||||
"servers.purchase.notification.success.title": {
|
||||
"defaultMessage": "Kauf erfolgreich"
|
||||
},
|
||||
"servers.purchase.step.payment.description": {
|
||||
"defaultMessage": "Es werden dir noch keine Kosten berechnet."
|
||||
},
|
||||
@@ -2612,15 +2990,27 @@
|
||||
"servers.purchase.step.payment.title": {
|
||||
"defaultMessage": "Zahlungsmethode"
|
||||
},
|
||||
"servers.purchase.step.plan.billing-subtitle": {
|
||||
"defaultMessage": "Verfügbar in Nordamerika, Europa und Südostasien."
|
||||
},
|
||||
"servers.purchase.step.plan.custom.desc": {
|
||||
"defaultMessage": "Wähle einen benutzerdefinierten Plan mit genau den Spezifikationen, die du brauchst."
|
||||
},
|
||||
"servers.purchase.step.plan.custom.heading": {
|
||||
"defaultMessage": "Du weißt genau, was du brauchst?"
|
||||
},
|
||||
"servers.purchase.step.plan.get-started": {
|
||||
"defaultMessage": "Loslegen"
|
||||
},
|
||||
"servers.purchase.step.plan.large": {
|
||||
"defaultMessage": "Groß"
|
||||
},
|
||||
"servers.purchase.step.plan.large.desc": {
|
||||
"defaultMessage": "Ideal für 15–25 Spieler, Modpacks oder umfangreiche Modding-Projekte."
|
||||
},
|
||||
"servers.purchase.step.plan.medium": {
|
||||
"defaultMessage": "Mittel"
|
||||
},
|
||||
"servers.purchase.step.plan.medium.desc": {
|
||||
"defaultMessage": "Ideal für 6–15 Spieler und mehrere Mods."
|
||||
},
|
||||
@@ -2630,12 +3020,18 @@
|
||||
"servers.purchase.step.plan.select": {
|
||||
"defaultMessage": "Plan auswählen"
|
||||
},
|
||||
"servers.purchase.step.plan.small": {
|
||||
"defaultMessage": "Klein"
|
||||
},
|
||||
"servers.purchase.step.plan.small.desc": {
|
||||
"defaultMessage": "Perfekt für 1–5 Freunde mit ein paar kleinen Mods."
|
||||
},
|
||||
"servers.purchase.step.plan.title": {
|
||||
"defaultMessage": "Plan"
|
||||
},
|
||||
"servers.purchase.step.plan.your-current-plan": {
|
||||
"defaultMessage": "Dein aktueller Plan"
|
||||
},
|
||||
"servers.purchase.step.region.title": {
|
||||
"defaultMessage": "Region"
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user