21 Commits

Author SHA1 Message Date
didirus 448f22bf1f Update README files 2026-06-21 22:51:38 +03:00
didirus 9ff96b3318 build(mise): add rust and turbo tool versions 2026-06-21 22:01:21 +03:00
didirus 9b78384226 chore(app): update AstralRinth app branding and version 2026-06-21 17:59:19 +03:00
didirus eba23d70ba Merge tag 'v0.14.8' into beta
v0.14.8
2026-06-21 17:52:19 +03:00
didirus f23f220a0c feat(astralrinth): add launcher update installer selection 2026-06-21 17:33:43 +03:00
Calum H. 49dd844fe1 feat: changelog app 0.14.8 (#6450) 2026-06-19 20:34:28 +00:00
Calum H. 56536abb24 fix: modal shift on close + readd settings preload (#6448)
* fix: NewModal layout shift on close

* fix: preload queries in settings modals

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

* fix backoff

* fix clippy

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

* feat: adjust multiselect scrollbar style

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

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

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

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

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

* convert to sqlx macros

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

* prepare

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

* fix(frontend): wss assumptions

* chore: fix export

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

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

* fix(frontend): don't strip ws path
2026-06-16 21:39:38 +00:00
139 changed files with 6386 additions and 1108 deletions
+15
View File
@@ -3,6 +3,7 @@
- [🔧 Install Instructions](#install-instructions)
- [✨ Features](#features)
- [🚀 Getting Started](#getting-started)
- [🛠️ Разработка](#development)
- [⚠️ Disclaimer](#disclaimer)
- [💰 Donate](#support-our-project-crypto-wallets)
@@ -103,6 +104,20 @@ To begin using AstralRinth:
---
# Development
Before continue you need to install `mise` tool for easy install libraries and running application on any OS (Windows, macOS, Linux)
1. `mise activate`
2. `mise install`
3. `mise exec rust -- rustup toolchain install stable`
4. `mise exec pnpm -- pnpm install`
5. `mise exec rust -- cargo install tauri-cli --version "^2.5.0"`
6. `mise exec pnpm -- pnpm app:dev` — Development (unoptimized)
7. `mise exec pnpm -- pnpm app:build` — Production
---
# Disclaimer
- **AstralRinth** is intended **solely for educational and experimental use**.
@@ -1,14 +1,15 @@
<script setup lang="ts">
import { Button, defineMessages, useVIntl } from '@modrinth/ui'
import { computed, ref } from 'vue'
import { Button, Combobox, defineMessages, useVIntl } from '@modrinth/ui'
import { computed, ref, watch } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import {
downloadLatestRelease,
getAvailableInstallers,
isUpdateInstalling,
LAUNCHER_RELEASES_URL,
LAUNCHER_REPOSITORY_URL,
latestLauncherRelease,
latestLauncherReleases,
} from '@/helpers/astralrinth/update'
type ModalHandle = {
@@ -24,9 +25,17 @@ const { formatMessage } = useVIntl()
const updateModalView = ref<ModalHandle | null>(null)
const updateRequestFailView = ref<ModalHandle | null>(null)
const selectedInstallerName = ref<string | null>(null)
const releaseTag = computed(() => latestLauncherRelease.value?.tag_name ?? '')
const releaseTitle = computed(() => latestLauncherRelease.value?.name ?? '')
const releaseTag = computed(() => latestLauncherReleases.value?.tag_name ?? '')
const releaseTitle = computed(() => latestLauncherReleases.value?.name ?? '')
const availableInstallers = computed(() => getAvailableInstallers())
const selectedInstaller = computed(
() =>
availableInstallers.value.find((installer) => installer.name === selectedInstallerName.value) ??
null,
)
const selectedInstallerUrl = computed(() => selectedInstaller.value?.browser_download_url ?? null)
const messages = defineMessages({
updateHeader: {
@@ -63,6 +72,18 @@ const messages = defineMessages({
id: 'astralrinth.app.launcher-update-modal.update.notice-outro',
defaultMessage: 'To avoid data loss, keep a backup copy in a safe place before continuing.',
},
installerTitle: {
id: 'astralrinth.app.launcher-update-modal.update.installer-title',
defaultMessage: 'Installer type',
},
installerDescription: {
id: 'astralrinth.app.launcher-update-modal.update.installer-description',
defaultMessage: 'Choose the installer package you want to continue with.',
},
selectInstaller: {
id: 'astralrinth.app.launcher-update-modal.update.select-installer',
defaultMessage: 'Select an installer',
},
latestReleaseTag: {
id: 'astralrinth.app.launcher-update-modal.update.latest-release-tag',
defaultMessage: '☁️ Latest release tag:',
@@ -85,7 +106,7 @@ const messages = defineMessages({
},
downloadAction: {
id: 'astralrinth.app.launcher-update-modal.update.download-action',
defaultMessage: 'Download update and close',
defaultMessage: 'Download update',
},
errorHeader: {
id: 'astralrinth.app.launcher-update-modal.error.header',
@@ -121,13 +142,29 @@ const messages = defineMessages({
},
})
watch(
availableInstallers,
(installers) => {
const hasSelectedInstaller = installers.some(
(installer) => installer.name === selectedInstallerName.value,
)
if (hasSelectedInstaller) {
return
}
selectedInstallerName.value = installers.length === 1 ? installers[0].name : null
},
{ immediate: true },
)
async function show() {
updateModalView.value?.show()
}
async function initDownload() {
updateModalView.value?.hide()
const result = await downloadLatestRelease()
const result = await downloadLatestRelease(selectedInstaller.value)
if (!result) {
updateRequestFailView.value?.show()
@@ -197,11 +234,37 @@ defineExpose({
</a>
</div>
<div class="space-y-2 rounded-2xl border border-solid border-[rgba(255,255,255,0.12)] p-3">
<div>
<p class="m-0 text-base">
<strong>{{ formatMessage(messages.installerTitle) }}</strong>
</p>
<p class="m-0 text-secondary text-sm">
{{ formatMessage(messages.installerDescription) }}
</p>
</div>
<Combobox
v-model="selectedInstallerName"
name="AstralRinth launcher installer"
:options="
availableInstallers.map((installer) => ({
value: installer.name,
label: installer.name,
}))
"
:display-value="selectedInstallerName ?? formatMessage(messages.selectInstaller)"
/>
</div>
<div class="absolute bottom-4 right-4 flex items-center gap-4 neon-button neon">
<Button class="bordered" @click="updateModalView?.hide()">
{{ formatMessage(messages.cancelAction) }}
</Button>
<Button class="bordered" :disabled="isUpdateInstalling" @click="initDownload()">
<Button
class="bordered"
:disabled="isUpdateInstalling || !selectedInstallerUrl"
@click="initDownload()"
>
{{ formatMessage(messages.downloadAction) }}
</Button>
</div>
@@ -215,7 +278,9 @@ defineExpose({
>
<div class="space-y-3 pb-16">
<div class="space-y-2 rounded-2xl border border-solid border-[rgba(255,255,255,0.12)] p-3">
<p><strong>{{ formatMessage(messages.errorTitle) }}</strong></p>
<p>
<strong>{{ formatMessage(messages.errorTitle) }}</strong>
</p>
<p class="m-0 text-secondary">{{ formatMessage(messages.errorDescription) }}</p>
<p class="m-0 text-sm">
{{ formatMessage(messages.errorHelpText) }}
@@ -231,7 +296,9 @@ defineExpose({
</p>
</div>
<div class="rounded-2xl border border-solid border-[rgba(255,255,255,0.12)] p-3 text-sm text-secondary">
<div
class="rounded-2xl border border-solid border-[rgba(255,255,255,0.12)] p-3 text-sm text-secondary"
>
<p class="m-0">
<strong>{{ formatMessage(messages.localVersion) }}</strong>
<span class="neon-text">v{{ props.version }}</span>
@@ -13,7 +13,7 @@ import {
} from '@modrinth/ui'
import type { GameVersionTag, PlatformTag } from '@modrinth/utils'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed, ref, shallowRef } from 'vue'
import { computed, ref } from 'vue'
import { trackEvent } from '@/helpers/analytics'
import { get_project_versions, get_version } from '@/helpers/cache'
@@ -47,55 +47,62 @@ debug('metadata load: start', {
installStage: instance.value.install_stage,
})
const [
fabric_versions,
forge_versions,
quilt_versions,
neoforge_versions,
all_game_versions,
loaders,
] = await Promise.all([
get_loader_versions('fabric')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('forge')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('quilt')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_loader_versions('neo')
.then((manifest: Manifest) => shallowRef(manifest))
.catch(handleError),
get_game_versions()
.then((gameVersions: GameVersionTag[]) => shallowRef(gameVersions))
.catch(handleError),
get_loaders()
.then((value: PlatformTag[]) =>
value
.filter(
(item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla',
)
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
.then((loader: PlatformTag[]) => ref(loader))
.catch(handleError),
])
function getSupportedModpackLoaders() {
return get_loaders().then((value: PlatformTag[]) =>
value
.filter((item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla')
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
}
debug('metadata load: done', {
hasFabricManifest: !!fabric_versions?.value,
hasForgeManifest: !!forge_versions?.value,
hasQuiltManifest: !!quilt_versions?.value,
hasNeoforgeManifest: !!neoforge_versions?.value,
gameVersions: all_game_versions?.value?.length ?? 0,
availablePlatforms: loaders?.value?.map((loader) => loader.name) ?? [],
const fabricVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'fabric'],
queryFn: () => get_loader_versions('fabric') as Promise<Manifest>,
})
const forgeVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'forge'],
queryFn: () => get_loader_versions('forge') as Promise<Manifest>,
})
const quiltVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'quilt'],
queryFn: () => get_loader_versions('quilt') as Promise<Manifest>,
})
const neoforgeVersionsQuery = useQuery({
queryKey: ['instance-settings', 'loader-versions', 'neo'],
queryFn: () => get_loader_versions('neo') as Promise<Manifest>,
})
const gameVersionsQuery = useQuery({
queryKey: ['instance-settings', 'game-versions'],
queryFn: () => get_game_versions() as Promise<GameVersionTag[]>,
})
const loadersQuery = useQuery({
queryKey: ['instance-settings', 'loaders', 'modpack'],
queryFn: getSupportedModpackLoaders,
})
const { data: modpackInfo } = useQuery({
const metadataLoading = computed(() =>
[
fabricVersionsQuery,
forgeVersionsQuery,
quiltVersionsQuery,
neoforgeVersionsQuery,
gameVersionsQuery,
loadersQuery,
].some((query) => query.isLoading.value),
)
debug('metadata queries configured', {
instancePath: instance.value.path,
loader: instance.value.loader,
gameVersion: instance.value.game_version,
})
const modpackInfoQuery = useQuery({
queryKey: computed(() => ['linkedModpackInfo', instance.value.path]),
queryFn: () => get_linked_modpack_info(instance.value.path, 'must_revalidate'),
enabled: computed(() => !!instance.value.linked_data?.project_id && !offline),
})
const modpackInfo = modpackInfoQuery.data
const repairing = ref(false)
const reinstalling = ref(false)
@@ -108,17 +115,17 @@ const messages = defineMessages({
})
function getManifest(loader: string) {
const map: Record<string, typeof fabric_versions> = {
fabric: fabric_versions,
forge: forge_versions,
quilt: quilt_versions,
neoforge: neoforge_versions,
const map: Record<string, Manifest | undefined> = {
fabric: fabricVersionsQuery.data.value,
forge: forgeVersionsQuery.data.value,
quilt: quiltVersionsQuery.data.value,
neoforge: neoforgeVersionsQuery.data.value,
}
const manifest = map[loader]
debug('getManifest:', {
loader,
hasManifest: !!manifest?.value,
gameVersions: manifest?.value?.gameVersions?.length ?? 0,
hasManifest: !!manifest,
gameVersions: manifest?.gameVersions?.length ?? 0,
})
return manifest
}
@@ -144,7 +151,7 @@ provideAppBackup({
provideInstallationSettings({
closeSettings: closeModal,
loading: ref(false),
loading: computed(() => metadataLoading.value || modpackInfoQuery.isLoading.value),
installationInfo: computed(() => {
const rows = [
{
@@ -186,14 +193,14 @@ provideInstallationSettings({
currentPlatform: computed(() => instance.value.loader),
currentGameVersion: computed(() => instance.value.game_version),
currentLoaderVersion: computed(() => instance.value.loader_version ?? ''),
availablePlatforms: loaders?.value?.map((x) => x.name) ?? [],
availablePlatforms: computed(() => loadersQuery.data.value?.map((x) => x.name) ?? []),
resolveGameVersions(loader, showSnapshots) {
const versions = all_game_versions?.value ?? []
const versions = gameVersionsQuery.data.value ?? []
const filtered = versions.filter((item) => {
if (loader === 'vanilla') return true
const manifest = getManifest(loader)
return !!manifest?.value?.gameVersions?.some((x) => item.version === x.id)
return !!manifest?.gameVersions?.some((x) => item.version === x.id)
})
const result = (
showSnapshots ? filtered : filtered.filter((x) => x.version_type === 'release')
@@ -214,12 +221,12 @@ provideInstallationSettings({
return []
}
const manifest = getManifest(loader)
if (!manifest?.value) {
if (!manifest) {
debug('resolveLoaderVersions: no manifest', { loader, gameVersion })
return []
}
if (loader === 'fabric' || loader === 'quilt') {
const result = manifest.value.gameVersions[0]?.loaders ?? []
const result = manifest.gameVersions[0]?.loaders ?? []
debug('resolveLoaderVersions: fabric/quilt result', {
loader,
gameVersion,
@@ -227,14 +234,13 @@ provideInstallationSettings({
})
return result
}
const result =
manifest.value.gameVersions?.find((item) => item.id === gameVersion)?.loaders ?? []
const result = manifest.gameVersions?.find((item) => item.id === gameVersion)?.loaders ?? []
debug('resolveLoaderVersions: result', { loader, gameVersion, count: result.length })
return result
},
resolveHasSnapshots(loader) {
const versions = all_game_versions?.value ?? []
const versions = gameVersionsQuery.data.value ?? []
if (loader === 'vanilla') {
const result = versions.some((x) => x.version_type !== 'release')
debug('resolveHasSnapshots: vanilla', { loader, result })
@@ -242,7 +248,7 @@ provideInstallationSettings({
}
const manifest = getManifest(loader)
const supported = versions.filter(
(item) => !!manifest?.value?.gameVersions?.some((x) => item.version === x.id),
(item) => !!manifest?.gameVersions?.some((x) => item.version === x.id),
)
const result = supported.some((x) => x.version_type !== 'release')
debug('resolveHasSnapshots:', {
@@ -16,7 +16,8 @@ import {
type TabbedModalTab,
useVIntl,
} from '@modrinth/ui'
import { useQueryClient } from '@tanstack/vue-query'
import type { PlatformTag } from '@modrinth/utils'
import { useQuery } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import { computed, nextTick, ref, watch } from 'vue'
@@ -26,7 +27,9 @@ import InstallationSettings from '@/components/ui/instance_settings/Installation
import JavaSettings from '@/components/ui/instance_settings/JavaSettings.vue'
import WindowSettings from '@/components/ui/instance_settings/WindowSettings.vue'
import { get_project_v3 } from '@/helpers/cache'
import { get_loader_versions } from '@/helpers/metadata'
import { get_linked_modpack_info } from '@/helpers/profile'
import { get_game_versions, get_loaders } from '@/helpers/tags'
import { provideInstanceSettings } from '@/providers/instance-settings'
import type { GameInstance } from '../../../helpers/types'
@@ -45,7 +48,6 @@ const isMinecraftServer = ref(false)
const handleUnlinked = () => emit('unlinked')
const instanceRef = computed(() => props.instance)
const queryClient = useQueryClient()
const tabbedModal = ref<InstanceType<typeof TabbedModal> | null>(null)
function hide() {
@@ -120,13 +122,46 @@ const tabs = computed<TabbedModalTab[]>(() => [
},
])
function getSupportedModpackLoaders() {
return get_loaders().then((value: PlatformTag[]) =>
value
.filter((item) => item.supported_project_types.includes('modpack') || item.name === 'vanilla')
.sort((a, b) => (a.name === 'vanilla' ? -1 : b.name === 'vanilla' ? 1 : 0)),
)
}
// Preload
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'fabric'],
queryFn: () => get_loader_versions('fabric'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'forge'],
queryFn: () => get_loader_versions('forge'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'quilt'],
queryFn: () => get_loader_versions('quilt'),
})
useQuery({
queryKey: ['instance-settings', 'loader-versions', 'neo'],
queryFn: () => get_loader_versions('neo'),
})
useQuery({
queryKey: ['instance-settings', 'game-versions'],
queryFn: get_game_versions,
})
useQuery({
queryKey: ['instance-settings', 'loaders', 'modpack'],
queryFn: getSupportedModpackLoaders,
})
useQuery({
queryKey: computed(() => ['linkedModpackInfo', props.instance.path]),
queryFn: () => get_linked_modpack_info(props.instance.path, 'stale_while_revalidate'),
enabled: computed(() => !!props.instance.linked_data?.project_id && !props.offline),
})
function show(tabIndex?: number) {
if (props.instance.linked_data?.project_id) {
queryClient.prefetchQuery({
queryKey: ['linkedModpackInfo', props.instance.path],
queryFn: () => get_linked_modpack_info(props.instance.path, 'stale_while_revalidate'),
})
}
tabbedModal.value?.show()
if (tabIndex !== undefined) {
nextTick(() => tabbedModal.value?.setTab(tabIndex))
@@ -22,15 +22,15 @@ const LAUNCHER_LATEST_RELEASE_API = `${import.meta.env.GIT_ASTRALIUM_API_URL}rep
export const isUpdateInstalling = ref(false)
export const isUpdateAvailable = ref(false)
export const latestLauncherRelease = ref<LauncherRelease | null>(null)
export const latestLauncherReleases = ref<LauncherRelease | null>(null)
const currentOS = ref('')
const systems = ['macos', 'windows', 'linux'] as const
const osExtensions = {
"linux": ['.deb'],
"macos": ['.dmg', '.pkg', '.app'],
"windows": ['.exe', '.msi']
linux: ['.deb', '.rpm', '.AppImage'],
macos: ['.dmg', '.pkg', '.app'],
windows: ['.exe', '.msi'],
}
const isDeveloper = await isDev()
@@ -47,15 +47,17 @@ const blacklistBeginPrefixes = [
export async function fetchRemote(): Promise<void> {
currentOS.value = (await getOS()).toLowerCase()
try {
if (!currentOS.value) {
throw new Error(String('Current OS is undefined'))
}
const response = await fetch(LAUNCHER_LATEST_RELEASE_API)
if (!response.ok) {
throw new Error(String(response.status))
}
const remoteData = (await response.json()) as LauncherRelease
latestLauncherRelease.value = remoteData
latestLauncherReleases.value = remoteData
if (systems.includes(currentOS.value as (typeof systems)[number])) {
const rawLocalVersion = await getVersion()
@@ -89,14 +91,16 @@ export async function fetchRemote(): Promise<void> {
}
} catch (error) {
console.error('Failed to fetch remote releases:', error)
latestLauncherRelease.value = null
latestLauncherReleases.value = null
isUpdateAvailable.value = false
isUpdateInstalling.value = false
}
}
export async function downloadLatestRelease(): Promise<boolean> {
if (!latestLauncherRelease.value) {
export async function downloadLatestRelease(
selectedInstaller?: LauncherReleaseAsset | null,
): Promise<boolean> {
if (!latestLauncherReleases.value) {
return false
}
@@ -104,7 +108,7 @@ export async function downloadLatestRelease(): Promise<boolean> {
currentOS.value = (await getOS()).toLowerCase()
}
const installer = getInstaller(resolveOperationalSystemExtension(), latestLauncherRelease.value.assets)
const installer = selectedInstaller ?? null
if (isDeveloper) {
console.debug(installer)
}
@@ -119,43 +123,51 @@ export async function downloadLatestRelease(): Promise<boolean> {
installer.browser_download_url,
installer.name,
currentOS.value,
true,
)
} finally {
isUpdateInstalling.value = false
}
}
function getInstaller(
osExtensions: string[],
builds: LauncherReleaseAsset[],
): LauncherReleaseAsset | null {
for (const build of builds) {
if (blacklistBeginPrefixes.some((prefix) => build.name.startsWith(prefix))) {
continue
}
if (osExtensions.some((extension) => build.name.endsWith(extension))) {
if (isDeveloper) {
console.debug(build.name, build.browser_download_url)
}
return build
}
export function getAvailableInstallers(): LauncherReleaseAsset[] {
if (!latestLauncherReleases.value) {
return []
}
return null
return getInstallers(resolveOperationalSystemExtension(), latestLauncherReleases.value.assets)
}
function getInstallers(os: string[], builds: LauncherReleaseAsset[]): LauncherReleaseAsset[] {
return builds.filter((build) => {
if (blacklistBeginPrefixes.some((prefix) => build.name.startsWith(prefix))) {
return false
}
const matchesExtension = os.some((extension) => build.name.endsWith(extension))
if (matchesExtension && isDeveloper) {
console.debug(build.name, build.browser_download_url)
}
return matchesExtension
})
}
function resolveOperationalSystemExtension(): string[] {
if (currentOS.value === 'macos') {
return osExtensions["macos"]
try {
switch (currentOS.value) {
case 'macos':
return osExtensions.macos
case 'windows':
return osExtensions.windows
case 'linux':
return osExtensions.linux
default:
throw new Error(String("Operational System can't be resolved"))
}
} catch (error) {
console.error("Operational System can't be resolved")
return []
}
if (currentOS.value === 'linux') {
return osExtensions["linux"]
}
return osExtensions["windows"]
}
function normalizeVersion(version: string): string {
+2 -2
View File
@@ -33,8 +33,8 @@ export async function getOS() {
// This code is modified by AstralRinth
export async function initUpdateLauncher(downloadUrl, filename, osType, autoUpdateSupported) {
console.log('Downloading build', downloadUrl, filename, osType, autoUpdateSupported)
return await invoke('plugin:utils|init_update_launcher', { downloadUrl, filename, osType, autoUpdateSupported })
console.log('Downloading build', downloadUrl, filename, osType)
return await invoke('plugin:utils|init_update_launcher', { downloadUrl, filename, osType })
}
export async function isNetworkMetered() {
+135 -9
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "جاز تنزيل إصدار جافا {version}"
},
"app.action-bar.downloading-update": {
"message": "تحميل التحديث"
},
"app.action-bar.downloads": {
"message": "التنزيلات"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "النسخة ألرئيسية"
},
"app.action-bar.reload-to-update": {
"message": "أعد التشغيل للتحديث"
},
"app.action-bar.show-more-running-instances": {
"message": "عرض المزيد من النسخ قيد التشغيل"
},
"app.action-bar.stop-instance": {
"message": "إيقاف النسخة"
},
"app.action-bar.update": {
"message": "تحديث"
},
"app.action-bar.view-active-downloads": {
"message": "عرض التحميلات الفعالة"
},
@@ -35,12 +44,36 @@
"app.action-bar.view-logs": {
"message": "عرض التسجيل"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "يتيح العرض المتقدم لتأثيرات مثل التشويش الذي قد تسبب مشكلات في الأداء دون استخدام العرض المسرع بالأجهزة."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "العرض المتقدم"
},
"app.appearance-settings.color-theme.description": {
"message": "اختر لون سمتك المفضل من أجل تطبيق Modrinth App."
},
"app.appearance-settings.color-theme.title": {
"message": "لون السمة"
},
"app.appearance-settings.default-landing-page.description": {
"message": "غير الصفحة التي يفتح عليها المشغل."
},
"app.appearance-settings.default-landing-page.home": {
"message": "صفحة الرئيسية"
},
"app.appearance-settings.default-landing-page.library": {
"message": "مكتبة"
},
"app.appearance-settings.default-landing-page.title": {
"message": "الصفحة الرئيسة الافتراضية"
},
"app.appearance-settings.hide-nametag.description": {
"message": "ألغ تفعيل عرض الاسم فوق لاعبك في صفحة المظاهر."
},
"app.appearance-settings.hide-nametag.title": {
"message": "إخفاء عرض الاسم"
},
"app.auth-servers.unreachable.body": {
"message": "قد تكون خوادم مصادقة ماينكرافت معطلة حاليًا. تحقق من اتصالك بالإنترنت وحاول مرة أخرى لاحقًا."
},
@@ -59,15 +92,27 @@
"app.browse.already-added": {
"message": "مضاف فعلا"
},
"app.browse.back-to-instance": {
"message": "العودة للنموذج"
},
"app.browse.discover-content": {
"message": "استكشف محتوى"
},
"app.browse.discover-servers": {
"message": "استكشف خوادم"
},
"app.browse.hide-added-servers": {
"message": "إخفاء الخوادم المضافة مسبقًا"
},
"app.browse.server.installing": {
"message": "جاري التثبيت"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
"app.creation-modal.installing-modpack.title": {
"message": "تنزيل الحزم"
},
"app.export-modal.description-placeholder": {
"message": "أدخل وصف التعديل..."
},
@@ -83,6 +128,9 @@
"app.export-modal.modpack-name-placeholder": {
"message": "إسم حزمة التعديل"
},
"app.export-modal.select-files-label": {
"message": "أختيار الملفات التي سيتم تصديرها"
},
"app.export-modal.version-number-label": {
"message": "رقم الإصدار"
},
@@ -102,13 +150,13 @@
"message": "حذف النموذج"
},
"app.instance.modpack-already-installed.body": {
"message": "حُزْمَة التعديل هذه مثبته فعلًا في نموذج <bold>{instanceName}</bold>. هل انت متأكد بإرادة نسخه؟"
"message": "حُزْمَة التعديل مثبته مسبقًا فعلًا في نموذج <bold>{instanceName}</bold>. هل أنت متأكد بإرادة نسخه؟"
},
"app.instance.modpack-already-installed.create": {
"message": "إنشاء"
},
"app.instance.modpack-already-installed.header": {
"message": "حُزْمَة التعديل مثبتة بالفعل"
"message": "حُزْمَة التعديل مثبتة فعلًا"
},
"app.instance.modpack-already-installed.instance": {
"message": "النموذج"
@@ -126,7 +174,7 @@
"message": "تحقق من المشاريع التي أستخدمها في حزمة التعديل الخاص بي!"
},
"app.instance.mods.share-title": {
"message": "مشاركة محتوى حزمة التعديل"
"message": "مشاركة محتوى حُزْمَة التعديل"
},
"app.instance.mods.successfully-uploaded": {
"message": "تم الرفع بنجاح"
@@ -189,7 +237,7 @@
"message": "{count, plural, one {# تعديل} other {# تعديلات}}"
},
"app.modal.install-to-play.required-modpack": {
"message": زمة التعديل مطلوبة"
"message": ُزْمَة التعديل مطلوبة"
},
"app.modal.install-to-play.server-requires-mods": {
"message": "يتطلب هذا الخادم تعديلات للعب. انقر فوق \"تثبيت\" لإعداد الملفات المطلوبة من Modrinth، ثم قم بتشغيله مباشرة إلى الخادم."
@@ -210,7 +258,13 @@
"message": "يلزم التحديث"
},
"app.modal.update-to-play.update-required-description": {
"message": "هناك تحديث مطلوب للعب بـ {name}. الرجاء التحديث إلى آخر اصدار لتشغيل اللعبة."
"message": "هناك تحديث مطلوب للعب بـ {name}. الرجاء التحديث إلى أحدث اصدار لتشغيل اللعبة."
},
"app.project.install-context.back-to-browse": {
"message": "العودة إلى الاستكشاف"
},
"app.project.install-context.install-content-to-instance": {
"message": "تثبيت المحتويات الى النموذج"
},
"app.settings.developer-mode-enabled": {
"message": "تم تفعيل وضع المطوّر."
@@ -236,6 +290,75 @@
"app.settings.tabs.resource-management": {
"message": "إدارة الموارد"
},
"app.skins.add-button": {
"message": "إضافة مظهر"
},
"app.skins.add-button.drag-and-drop": {
"message": "أسحب ثم أفلت"
},
"app.skins.apply-button": {
"message": "تطبيق"
},
"app.skins.delete-button": {
"message": "حذف المظهر"
},
"app.skins.delete-modal.description": {
"message": "سيتم حذف المظهر بشكل نهائي، ولايمكن إرجاعه."
},
"app.skins.delete-modal.title": {
"message": "هل أنت متأكد من حذف المظهر هذا؟"
},
"app.skins.edit-button": {
"message": "تعديل المظهر"
},
"app.skins.modal.add-skin-button": {
"message": "إضافة المظهر"
},
"app.skins.modal.add-title": {
"message": "إضافة المظهر"
},
"app.skins.modal.cape-fallback-name": {
"message": "رداء"
},
"app.skins.modal.cape-section": {
"message": "رداء"
},
"app.skins.modal.edit-title": {
"message": "تعديل المظهر"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "عدل المظهر أولا للحفظ !"
},
"app.skins.modal.no-cape-tooltip": {
"message": "بدون رداء"
},
"app.skins.modal.none-cape-option": {
"message": "لا شيء"
},
"app.skins.modal.save-skin-button": {
"message": "حفظ المظهر"
},
"app.skins.modal.saving-tooltip": {
"message": "حفظ..."
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "أرفع المظهر أولا !"
},
"app.skins.preview.edit-button": {
"message": "تعديل المظهر"
},
"app.skins.previewing-badge": {
"message": "أستعراض"
},
"app.skins.rate-limit.title": {
"message": "تمهل !"
},
"app.skins.sign-in.title": {
"message": "الرجاء تسجيل الدخول"
},
"app.skins.title": {
"message": "اختيار المظهر"
},
"app.update-popup.body.download-complete": {
"message": "انتهى تنزيل تطبيق Modrinth v{version}. أعد التحميل للتحديث الآن، أو تلقائيًا عند إغلاق تطبيق Modrinth."
},
@@ -254,9 +377,6 @@
"app.update-popup.download-complete": {
"message": "اكتمل التنزيل"
},
"app.update-popup.reload": {
"message": "إعادة تحميل"
},
"app.update-popup.title": {
"message": "تحديث متاح"
},
@@ -267,7 +387,7 @@
"message": "تم تثبيت الإصدار {version} بنجاح!"
},
"app.world.server-modal.placeholder-address": {
"message": "مثال.مودرنث.جج"
"message": "مثال (example.modrinth.gg)"
},
"app.world.server-modal.select-an-option": {
"message": "حدد خيارا"
@@ -518,6 +638,9 @@
"instance.settings.tabs.java.java-memory": {
"message": "الذاكرة المخصَّصة"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/path/to/java"
},
"instance.settings.tabs.window": {
"message": "النافذة"
},
@@ -593,6 +716,9 @@
"minecraft-account.add-account": {
"message": "إضافة حساب"
},
"minecraft-account.label": {
"message": "حساب ماينكرافت"
},
"minecraft-account.select-account": {
"message": "اختيار الحساب"
},
+27 -3
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Stahování Javy {version}"
},
"app.action-bar.downloading-update": {
"message": "Stahování aktualizace"
},
"app.action-bar.downloads": {
"message": "Stažené Soubory"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primární instance"
},
"app.action-bar.reload-to-update": {
"message": "Obnovit pro aktualizaci"
},
"app.action-bar.show-more-running-instances": {
"message": "Zobrazit další spuštěné instance"
},
"app.action-bar.stop-instance": {
"message": "Ukončit instanci"
},
"app.action-bar.update": {
"message": "Aktualizace"
},
"app.action-bar.view-active-downloads": {
"message": "Zobrazit probíhající stahování"
},
@@ -332,6 +341,9 @@
"app.skins.add-button": {
"message": "Přidat skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Vem a drž"
},
"app.skins.apply-button": {
"message": "Použít"
},
@@ -344,9 +356,18 @@
"app.skins.modal.add-skin-button": {
"message": "Přidat skin"
},
"app.skins.modal.saving-tooltip": {
"message": "Ukládání..."
},
"app.skins.modal.texture-section": {
"message": "Textura"
},
"app.skins.preview.edit-button": {
"message": "Upravit skin"
},
"app.skins.rate-limit.title": {
"message": "Zpomal!"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
@@ -377,9 +398,6 @@
"app.update-popup.download-complete": {
"message": "Stahování bylo dokončeno"
},
"app.update-popup.reload": {
"message": "Načíst znovu"
},
"app.update-popup.title": {
"message": "Je k dispozici aktualizace"
},
@@ -575,6 +593,12 @@
"instance.settings.tabs.general.name": {
"message": "Název"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.hooks": {
"message": "Spouštěcí hooky"
},
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Downloader Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Downloader opdatering"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -473,9 +476,6 @@
"app.update-popup.download-complete": {
"message": "Download færdiggjort"
},
"app.update-popup.reload": {
"message": "Geninlæs"
},
"app.update-popup.title": {
"message": "Opdatering tilgængelig"
},
+40 -1
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} wird heruntergeladen"
},
"app.action-bar.downloading-update": {
"message": "Update wird heruntergeladen"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primäre Instanz"
},
"app.action-bar.reload-to-update": {
"message": "Zum Aktualisieren neu laden"
},
"app.action-bar.show-more-running-instances": {
"message": "Weitere laufende Instanzen anzeigen"
},
"app.action-bar.stop-instance": {
"message": "Instanz stoppen"
},
"app.action-bar.update": {
"message": "Aktualisieren"
},
"app.action-bar.view-active-downloads": {
"message": "Aktive Downloads anzeigen"
},
@@ -419,6 +428,12 @@
"app.skins.rate-limit.title": {
"message": "Nicht so schnell!"
},
"app.skins.reorder-error.text": {
"message": "Die Anordnung deiner Skins konnte nicht gespeichert werden."
},
"app.skins.reorder-error.title": {
"message": "Skins konnten nicht neu angeordnet werden"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -492,7 +507,7 @@
"message": "Download abgeschlossen"
},
"app.update-popup.reload": {
"message": "Neu Laden"
"message": "Zum Aktualisieren neu laden"
},
"app.update-popup.title": {
"message": "Aktualisierung verfügbar"
@@ -692,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "Name"
},
"instance.settings.tabs.general.update-channel": {
"message": "Aktualisierungskanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Voll, Beta- und Alpha-Versionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Voll- und Beta-Versionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Vollversion"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Nur Vollversionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Aktualisierungskanal auswählen"
},
"instance.settings.tabs.hooks": {
"message": "Start Hooks"
},
+43 -4
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} wird heruntergeladen"
},
"app.action-bar.downloading-update": {
"message": "Update wird heruntergeladen"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -12,7 +15,7 @@
"message": "Zur primären Instanz machen"
},
"app.action-bar.no-instances-running": {
"message": "Keine aktiven Instanzen"
"message": "Keine laufenden Instanzen"
},
"app.action-bar.offline": {
"message": "Offline"
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primäre Instanz"
},
"app.action-bar.reload-to-update": {
"message": "Zum Aktualisieren neu laden"
},
"app.action-bar.show-more-running-instances": {
"message": "Weitere laufende Instanzen anzeigen"
},
"app.action-bar.stop-instance": {
"message": "Instanz stoppen"
},
"app.action-bar.update": {
"message": "Aktualisieren"
},
"app.action-bar.view-active-downloads": {
"message": "Aktive Downloads anzeigen"
},
@@ -33,7 +42,7 @@
"message": "Instanz anzeigen"
},
"app.action-bar.view-logs": {
"message": "Protokolle anzeigen"
"message": "Logs anzeigen"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Aktiviert erweiterte Rendering-Funktionen wie Unschärfe-Effekte, welche ohne Hardware-beschleunigtes Rendering zu Leistungsproblemen führen können."
@@ -414,11 +423,17 @@
"message": "Vorschau"
},
"app.skins.rate-limit.text": {
"message": "Du wechselst dein Skin zu oft. Die Server von Mojang haben weitere Anfragen vorübergehend blockiert. Bitte warte einen Moment, bevor du es erneut versuchst."
"message": "Du wechselst deinen Skin zu oft. Die Server von Mojang haben weitere Anfragen vorübergehend blockiert. Bitte warte einen Moment, bevor du es erneut versuchst."
},
"app.skins.rate-limit.title": {
"message": "Langsamer!"
},
"app.skins.reorder-error.text": {
"message": "Die Anordnung deiner Skins konnte nicht gespeichert werden."
},
"app.skins.reorder-error.title": {
"message": "Skins konnten nicht neu angeordnet werden"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -492,7 +507,7 @@
"message": "Download abgeschlossen"
},
"app.update-popup.reload": {
"message": "Neu laden"
"message": "Zum Aktualisieren neu laden"
},
"app.update-popup.title": {
"message": "Aktualisierung verfügbar"
@@ -692,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "Name"
},
"instance.settings.tabs.general.update-channel": {
"message": "Aktualisierungskanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Voll, Beta- und Alpha-Versionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Voll- und Beta-Versionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Vollversion"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Nur Vollversionen werden als verfügbare Aktualisierungen angezeigt."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Aktualisierungskanal auswählen"
},
"instance.settings.tabs.hooks": {
"message": "Startargumente"
},
+10 -1
View File
@@ -165,11 +165,17 @@
"message": "You are using an older version. We recommend updating now for the latest fixes and improvements."
},
"astralrinth.app.launcher-update-modal.update.download-action": {
"message": "Download update and close"
"message": "Download update"
},
"astralrinth.app.launcher-update-modal.update.header": {
"message": "AstralRinth launcher update"
},
"astralrinth.app.launcher-update-modal.update.installer-description": {
"message": "Choose the installer package you want to continue with."
},
"astralrinth.app.launcher-update-modal.update.installer-title": {
"message": "Installer type"
},
"astralrinth.app.launcher-update-modal.update.installed-version": {
"message": "💾 Installed & Running version:"
},
@@ -197,6 +203,9 @@
"astralrinth.app.launcher-update-modal.update.repository-link": {
"message": "Open the project repository"
},
"astralrinth.app.launcher-update-modal.update.select-installer": {
"message": "Select an installer"
},
"astralrinth.app.launcher-update-modal.update.title": {
"message": "A new version of the AstralRinth launcher is available."
},
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Descargando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Descargando actualización"
},
"app.action-bar.downloads": {
"message": "Descargas"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instancia principal"
},
"app.action-bar.reload-to-update": {
"message": "Reinicia para actualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar más instancias en ejecución"
},
"app.action-bar.stop-instance": {
"message": "Detener instancia"
},
"app.action-bar.update": {
"message": "Actualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver descargas en curso"
},
@@ -303,7 +312,7 @@
"message": "Este proyecto ya está instalado"
},
"app.project.install-context.back-to-browse": {
"message": "Volver a descubrir contenido"
"message": "Volver al explorador"
},
"app.project.install-context.install-content-to-instance": {
"message": "Instalar contenido a la instancia"
@@ -336,7 +345,7 @@
"message": "Añadir skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Arrastrar y soltar"
"message": "Arrastra y suelta"
},
"app.skins.apply-button": {
"message": "Aplicar"
@@ -345,10 +354,10 @@
"message": "Eliminar skin"
},
"app.skins.delete-modal.description": {
"message": "Esto eliminará permanentemente el skin seleccionado. Esta acción no se puede deshacer."
"message": "Esto eliminará permanentemente la skin seleccionada. Esta acción no se puede deshacer."
},
"app.skins.delete-modal.title": {
"message": "¿Estás seguro de que quieres eliminar este skin?"
"message": "¿Estás seguro de que quieres eliminar esta skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Error al leer el archivo arrastrado."
@@ -363,7 +372,7 @@
"message": "Añadir skin"
},
"app.skins.modal.add-title": {
"message": "Añadiendo un skin"
"message": "Añadiendo una skin"
},
"app.skins.modal.arm-style-section": {
"message": "Estilo de brazo"
@@ -405,7 +414,7 @@
"message": "Textura"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "¡Primero carga un skin!"
"message": "¡Primero carga una skin!"
},
"app.skins.preview.edit-button": {
"message": "Editar skin"
@@ -419,6 +428,12 @@
"app.skins.rate-limit.title": {
"message": "¡Más despacio!"
},
"app.skins.reorder-error.text": {
"message": "No se ha podido guardar el orden de las skins."
},
"app.skins.reorder-error.title": {
"message": "Error al reordenar las skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Constructores y Biomas"
},
@@ -444,7 +459,7 @@
"message": "Monturas del Caos"
},
"app.skins.section.saved-skins": {
"message": "Skins guardados"
"message": "Skins guardadas"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
@@ -492,7 +507,7 @@
"message": "Descarga completada"
},
"app.update-popup.reload": {
"message": "Recargar"
"message": "Reinicia para actualizar"
},
"app.update-popup.title": {
"message": "Actualización disponible"
@@ -692,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "Nombre"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de actualizaciones"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Las versiones estables, beta y alpha se mostrarán como actualizaciones disponibles. "
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Las versiones estables y beta serán mostradas como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Estable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Sólo las versiones estables serán mostradas como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Selecciona el canal de actualizaciones"
},
"instance.settings.tabs.hooks": {
"message": "Hooks de inicio"
},
@@ -882,7 +921,7 @@
"message": "Proporcionado por el servidor"
},
"search.filter.locked.server-environment.title": {
"message": "Solo se puede añadir mods que sean del lado del cliente a la instancia del servidor"
"message": "Solo se pueden añadir mods que sean del lado del cliente a la instancia del servidor"
},
"search.filter.locked.server-game-version.title": {
"message": "La versión del juego es proporcionada por el servidor"
+43 -4
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Descargando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Descargando actualización"
},
"app.action-bar.downloads": {
"message": "Descargas"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instancia principal"
},
"app.action-bar.reload-to-update": {
"message": "Recargar para actualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar más instancias en ejecución"
},
"app.action-bar.stop-instance": {
"message": "Detener instancia"
},
"app.action-bar.update": {
"message": "Actualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Mostrar descargas activas"
},
@@ -48,7 +57,7 @@
"message": "Tema de color"
},
"app.appearance-settings.default-landing-page.description": {
"message": "Cambia la página que se abre al iniciar el lanzador."
"message": "Cambia la página en la que el launcher inicia."
},
"app.appearance-settings.default-landing-page.home": {
"message": "Inicio"
@@ -428,6 +437,12 @@
"app.skins.rate-limit.title": {
"message": "¡Cálmate!"
},
"app.skins.reorder-error.text": {
"message": "No se ha podido guardar el orden de las skins."
},
"app.skins.reorder-error.title": {
"message": "Error al reordenar las skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders y Biomas"
},
@@ -501,7 +516,7 @@
"message": "Descarga completada"
},
"app.update-popup.reload": {
"message": "Recargar"
"message": "Recargar para actualizar"
},
"app.update-popup.title": {
"message": "Actualización disponible"
@@ -701,6 +716,30 @@
"instance.settings.tabs.general.name": {
"message": "Nombre"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de actualizaciones"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Se mostrarán las versiones estables, beta y alfa como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Se mostrarán las versiones estables y beta como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Estable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Solo se mostrarán las versiones estables como actualizaciones disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Seleccionar canal de actualizaciones"
},
"instance.settings.tabs.hooks": {
"message": "Hooks de lanzamiento"
},
@@ -882,7 +921,7 @@
"message": "Versión del juego proporciona por la instancia"
},
"search.filter.locked.instance-loader.title": {
"message": "Cargador proporcionado por la instancia"
"message": "Loader proporcionado por la instancia"
},
"search.filter.locked.instance.sync": {
"message": "Sincronizar con la instancia"
@@ -897,7 +936,7 @@
"message": "La versión del juego es proporcionada por el servidor"
},
"search.filter.locked.server-loader.title": {
"message": "El cargador lo proporciona el servidor"
"message": "Loader proporcionado por el servidor"
},
"unknown-pack-warning-modal.body": {
"message": "Un archivo solo es revisado si es subido a Modrinth, independientemente de su formato de archivo (incluyendo .mrpack)."
+28 -1
View File
@@ -143,6 +143,9 @@
"app.browse.server.installing": {
"message": "Asennetaan"
},
"app.content-install.no-compatible-versions": {
"message": "Yhtään versiota ei ole saatavilla yhteensopivuudella {compatibilityLabel}. Valitse asennettava versio siitä huolimatta. Riippuvuuksia ei asenneta automaattisesti."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -332,6 +335,21 @@
"app.settings.tabs.resource-management": {
"message": "Resurssien hallinta"
},
"app.skins.reorder-error.title": {
"message": "Skinien järjestyksen muuttaminen epäonnistui"
},
"app.skins.section.mounts-of-mayhem": {
"message": "Sekasorron ratsut"
},
"app.skins.section.tiny-takeover": {
"message": "Pienten vallankumous"
},
"app.skins.sign-in.title": {
"message": "Kirjaudu sisään"
},
"app.skins.title": {
"message": "Skinivalinta"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App versio {version} on ladattu. Voit käynnistää sovelluksen uudelleen päivittääksesi heti tai antaa päivityksen asentua automaattisesti, kun suljet Modrinth Appin."
},
@@ -351,7 +369,7 @@
"message": "Lataus valmis"
},
"app.update-popup.reload": {
"message": "Lataa uudelleen"
"message": "Lataa uudelleen päivittääksesi"
},
"app.update-popup.title": {
"message": "Päivitys saatavilla"
@@ -551,6 +569,15 @@
"instance.settings.tabs.general.name": {
"message": "Nimi"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Julkaisu"
},
"instance.settings.tabs.hooks": {
"message": "Aloitustoiminnot"
},
@@ -26,6 +26,9 @@
"app.action-bar.stop-instance": {
"message": "Itigil ang instansiya"
},
"app.action-bar.update": {
"message": "Mag-update"
},
"app.action-bar.view-active-downloads": {
"message": "Tignan ang mga active downloads"
},
@@ -377,9 +380,6 @@
"app.update-popup.download-complete": {
"message": "Nakumpleto ang pagdownload"
},
"app.update-popup.reload": {
"message": "Mag-reload"
},
"app.update-popup.title": {
"message": "May bagong update"
},
@@ -578,6 +578,12 @@
"instance.settings.tabs.general.name": {
"message": "Pangalan"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.hooks": {
"message": "Mga launch hook"
},
+41 -2
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Téléchargement de Java {version} en cours"
},
"app.action-bar.downloading-update": {
"message": "Téléchargement de la mise à jour"
},
"app.action-bar.downloads": {
"message": "Téléchargements"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instance première"
},
"app.action-bar.reload-to-update": {
"message": "Recharger pour mettre à jour"
},
"app.action-bar.show-more-running-instances": {
"message": "Montrer plus d'instances en exécution"
},
"app.action-bar.stop-instance": {
"message": "Arrêter l'instance"
},
"app.action-bar.update": {
"message": "Mettre à jour"
},
"app.action-bar.view-active-downloads": {
"message": "Voir les téléchargements actifs"
},
@@ -93,7 +102,7 @@
"message": "Afficher le temps de jeu"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Permet d'ouvrir de fermer la barre latérale."
"message": "Permet dafficher ou de masquer la barre latérale."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Tiroir latéral"
@@ -419,6 +428,12 @@
"app.skins.rate-limit.title": {
"message": "Du calme !"
},
"app.skins.reorder-error.text": {
"message": "L'ordre des skins n'a pas pu être sauvegardé."
},
"app.skins.reorder-error.title": {
"message": "Impossible de réorganiser les skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -492,7 +507,7 @@
"message": "Téléchargement terminé"
},
"app.update-popup.reload": {
"message": "Recharger"
"message": "Recharger pour mettre à jour"
},
"app.update-popup.title": {
"message": "Mise à jour disponible"
@@ -689,6 +704,30 @@
"instance.settings.tabs.general.name": {
"message": "Nom"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de mise à jour"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Les versions stables, bêta et alpha seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Bêta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Les versions stables et bêta seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stable"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Seules les versions stables seront affichées comme mises à jour disponibles."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Sélectionner le canal de mise à jour"
},
"instance.settings.tabs.hooks": {
"message": "Crochets de lancement"
},
@@ -1,4 +1,10 @@
{
"app.action-bar.downloading-java": {
"message": "מוריד את Java {version}"
},
"app.action-bar.downloading-update": {
"message": "מוריד עדכון"
},
"app.action-bar.downloads": {
"message": "הורדות"
},
@@ -11,6 +17,9 @@
"app.appearance-settings.advanced-rendering.description": {
"message": "מאפשר עיבוד מתקדם כגון אפקטים טשטוש העלולים לגרום לבעיות ביצועים ללא עיבוד מואץ לחומרה."
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "חזרה לעולמות"
},
"app.auth-servers.unreachable.body": {
"message": "ייתכן ששרתי האימות של Minecraft מושבתים כרגע. יש לבדוק את חיבור האינטרנט שלך ולנסות שוב מאוחר יותר."
},
@@ -221,9 +230,6 @@
"app.update-popup.download-complete": {
"message": "הורדה הושלמה"
},
"app.update-popup.reload": {
"message": "רענן"
},
"app.update-popup.title": {
"message": "עדכון זמין"
},
+34 -1
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "A Java {version} letöltése"
},
"app.action-bar.downloading-update": {
"message": "Frissítés letöltése"
},
"app.action-bar.downloads": {
"message": "Letöltések"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Elsődleges példány"
},
"app.action-bar.reload-to-update": {
"message": "Töltsd újra a frissítéshez"
},
"app.action-bar.show-more-running-instances": {
"message": "További futó pédányok megjelenítése"
},
"app.action-bar.stop-instance": {
"message": "Példány leállítása"
},
"app.action-bar.update": {
"message": "Frissítés"
},
"app.action-bar.view-active-downloads": {
"message": "Aktív letöltések megtekintése"
},
@@ -498,7 +507,7 @@
"message": "Sikeres letöltés"
},
"app.update-popup.reload": {
"message": "Frissítés"
"message": "Töltsd újra a frissítéshez"
},
"app.update-popup.title": {
"message": "Frissítés elérhető"
@@ -695,6 +704,30 @@
"instance.settings.tabs.general.name": {
"message": "Név"
},
"instance.settings.tabs.general.update-channel": {
"message": "Frissítési csatorna"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "A release, béta és az alpha verziók is meg fognak jelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Béta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "A release és a béta verziók is meg fognak jelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Csak a release verziók fognak megjelenni az elérhető frissítések között."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Válaszd ki a frissítési csatornát"
},
"instance.settings.tabs.hooks": {
"message": "Indítási horgok"
},
+49 -1
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Mengunduh Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Mengunduh pembaruan"
},
"app.action-bar.downloads": {
"message": "Unduhan"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instans utama"
},
"app.action-bar.reload-to-update": {
"message": "Muat ulang untuk memperbarui"
},
"app.action-bar.show-more-running-instances": {
"message": "Tampilkan lebih banyak instans yang sedang berjalan"
},
"app.action-bar.stop-instance": {
"message": "Hentikan instans"
},
"app.action-bar.update": {
"message": "Perbarui"
},
"app.action-bar.view-active-downloads": {
"message": "Lihat pengunduhan berlangsung"
},
@@ -86,6 +95,12 @@
"app.appearance-settings.select-option": {
"message": "Pilih opsi"
},
"app.appearance-settings.show-play-time.description": {
"message": "Menampilkan seberapa lama Anda memainkan sebuah instans."
},
"app.appearance-settings.show-play-time.title": {
"message": "Tampilkan waktu bermain"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Menghidupkan kemampuan untuk menghidupkan dan mematikan bilah sisi."
},
@@ -143,6 +158,9 @@
"app.browse.server.installing": {
"message": "Memasang"
},
"app.content-install.no-compatible-versions": {
"message": "Tidak ada versi tersedia yang cocok untuk {compatibilityLabel}. Anda tetap dapat memilih versi yang akan dipasang. Dependensi tidak akan dipasang secara otomatis."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -416,6 +434,12 @@
"app.skins.rate-limit.title": {
"message": "Pelan-pelan!"
},
"app.skins.reorder-error.text": {
"message": "Urutan rupa Anda tidak dapat disimpan."
},
"app.skins.reorder-error.title": {
"message": "Gagal mengurut ulang rupa"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -489,7 +513,7 @@
"message": "Selesai mengunduh"
},
"app.update-popup.reload": {
"message": "Muat ulang"
"message": "Muat ulang untuk memperbarui"
},
"app.update-popup.title": {
"message": "Pembaruan tersedia"
@@ -686,6 +710,30 @@
"instance.settings.tabs.general.name": {
"message": "Nama"
},
"instance.settings.tabs.general.update-channel": {
"message": "Perbarui kanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Versi rilisan, beta, dan alpha akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Versi beta dan alpha akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Rilisan"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Hanya versi rilisan yang akan ditampilkan sebagai pembaruan yang tersedia."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Pilih kanal pembaruan"
},
"instance.settings.tabs.hooks": {
"message": "Luncurkan kait"
},
+60 -27
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Scaricando l'aggiornamento"
},
"app.action-bar.downloads": {
"message": "Download"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primaria"
},
"app.action-bar.reload-to-update": {
"message": "Ricarica per aggiornare"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostra più istanze in esecuzione"
},
"app.action-bar.stop-instance": {
"message": "Ferma l'istanza"
},
"app.action-bar.update": {
"message": "Aggiorna"
},
"app.action-bar.view-active-downloads": {
"message": "Mostra i download attivi"
},
@@ -90,7 +99,7 @@
"message": "Mostra quanto tempo hai speso giocando a un'istanza."
},
"app.appearance-settings.show-play-time.title": {
"message": "Mostra tempo di gioco"
"message": "Mostra le ore di gioco"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Mostra o nascondi la barra laterale."
@@ -144,13 +153,13 @@
"message": "Pacchetti di mod"
},
"app.browse.server-instance-content-warning": {
"message": "Aggiungere dei contenuti potrebbe causare problemi entrando nel server. Inoltre, qualsiasi contenuto aggiunto sarà perso a ogni aggiornamento dei contenuti dell'istanza."
"message": "Aggiungere dei contenuti potrebbe causare problemi entrando nel server. Inoltre, qualsiasi contenuto aggiunto sarà perso quando aggiorni i contenuti dell'istanza."
},
"app.browse.server.installing": {
"message": "Installazione"
},
"app.content-install.no-compatible-versions": {
"message": "Nessuna versione disponibile corrisponde a {compatibilityLabel}. Seleziona una versione da installare comunque. Le dipendenze non verranno scaricate automaticamente."
"message": "Nessuna versione disponibile soddisfa {compatibilityLabel}. Puoi selezionare una versione da installare comunque. Le dipendenze non verranno scaricate automaticamente."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
@@ -215,9 +224,6 @@
"app.instance.mods.project-was-added": {
"message": "\"{name}\" è stato aggiunto"
},
"app.instance.mods.projects-were-added": {
"message": "{count} progetti aggiunti"
},
"app.instance.mods.share-text": {
"message": "Guarda le mod che uso nel mio pacchetto!"
},
@@ -333,7 +339,7 @@
"message": "Nuova skin"
},
"app.skins.add-button.drag-and-drop": {
"message": "Trascina e rilascia"
"message": "Trascina qui"
},
"app.skins.apply-button": {
"message": "Applica"
@@ -348,7 +354,7 @@
"message": "Vuoi davvero eliminare questa skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Non è stato possibile leggere il file rilasciato."
"message": "Non è stato possibile leggere il file trascinato."
},
"app.skins.dropped-file-error.title": {
"message": "Impossibile processare il file"
@@ -357,13 +363,13 @@
"message": "Modifica"
},
"app.skins.modal.add-skin-button": {
"message": "Salva"
"message": "Aggiungi"
},
"app.skins.modal.add-title": {
"message": "Aggiungi una nuova skin"
"message": "Aggiungi una skin"
},
"app.skins.modal.arm-style-section": {
"message": "Modello del giocatore"
"message": "Modello"
},
"app.skins.modal.arm-style-slim": {
"message": "Snello"
@@ -381,7 +387,7 @@
"message": "Modifica la skin"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Modifica la skin prima!"
"message": "Modifica prima la skin!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Nessun mantello"
@@ -390,19 +396,19 @@
"message": "Nessuno"
},
"app.skins.modal.replace-texture-button": {
"message": "Sfoglia"
"message": "Cambia"
},
"app.skins.modal.save-skin-button": {
"message": "Salva"
},
"app.skins.modal.saving-tooltip": {
"message": "Salvataggio..."
"message": "Salvando..."
},
"app.skins.modal.texture-section": {
"message": "File della skin"
"message": "Aspetto"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Carica una skin prima!"
"message": "Carica prima una skin!"
},
"app.skins.preview.edit-button": {
"message": "Modifica"
@@ -411,11 +417,17 @@
"message": "Anteprima"
},
"app.skins.rate-limit.text": {
"message": "Stai modificando la skin troppo spesso. I server di Mojang hanno limitato le nostre richieste. Aspetta un momento prima di riprovare."
"message": "Stai cambiando skin troppo spesso. I server di Mojang hanno limitato le nostre richieste. Aspetta un momento prima di riprovare."
},
"app.skins.rate-limit.title": {
"message": "Rallenta!"
},
"app.skins.reorder-error.text": {
"message": "L'ordine delle skin non è stato salvato."
},
"app.skins.reorder-error.title": {
"message": "Errore nel riordine delle skin"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -441,7 +453,7 @@
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Raccolta"
"message": "Skin salvate"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
@@ -459,13 +471,13 @@
"message": "Accedi"
},
"app.skins.sign-in.description": {
"message": "Accedi col tuo account Minecraft per usare la gestione delle skin."
"message": "Accedi col tuo account Minecraft per poter gestire le skin qui."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Modrinth Bot emozionato"
},
"app.skins.sign-in.title": {
"message": "Accedi"
"message": "Effettua l'accesso"
},
"app.skins.title": {
"message": "Seleziona una skin"
@@ -521,9 +533,6 @@
"friends.action.add-friend": {
"message": "Stringi un'amicizia"
},
"friends.action.view-friend-requests": {
"message": "{count} {count, plural, one {richiesta} other {richieste}} d''amicizia"
},
"friends.add-friend.submit": {
"message": "Invia richiesta d'amicizia"
},
@@ -689,6 +698,30 @@
"instance.settings.tabs.general.name": {
"message": "Nome"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canale di aggiornamento"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Le versioni stabili, beta e alpha saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Le versioni stabili e beta saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stabile"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Solo le versioni stabili saranno mostrate come aggiornamenti disponibili."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Seleziona il canale di aggiornamento"
},
"instance.settings.tabs.hooks": {
"message": "Hook di avvio"
},
@@ -738,16 +771,16 @@
"message": "Java e memoria"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Variabili d'ambiente"
"message": "Personalizzate"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Argomenti JVM"
"message": "Personalizzati"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Eseguibile di Java"
"message": "Personalizzata"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Memoria allocata"
"message": "Personalizzata"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Inserisci le variabili d'ambiente..."
+15 -12
View File
@@ -42,7 +42,7 @@
"message": "高度なレンダリング"
},
"app.appearance-settings.color-theme.description": {
"message": "Modrinth Appでのお好みのテーマを選択してください"
"message": "Modrinth Appでのお好みのテーマを選択してください"
},
"app.appearance-settings.color-theme.title": {
"message": "テーマ"
@@ -60,25 +60,25 @@
"message": "デフォルトの起動ページ"
},
"app.appearance-settings.hide-nametag.description": {
"message": "スキンページでプレイヤー名の上にある名前タグを無効にします。"
"message": "スキンページでプレイヤー上部のネームタグを非表示にします。"
},
"app.appearance-settings.hide-nametag.title": {
"message": "名札を非表示にする"
"message": "ネームタグを非表示にする"
},
"app.appearance-settings.jump-back-into-worlds.description": {
"message": "ホームページの「ジャンプバック」セクションには、最近追加されたワールドが含まれています。"
},
"app.appearance-settings.jump-back-into-worlds.title": {
"message": "再び世界へ飛び込もう"
"message": "最近のワールドを表示"
},
"app.appearance-settings.minimize-launcher.description": {
"message": "Minecraftが起動した時、ランチャーを最小化します"
"message": "Minecraftが起動した時、ランチャーを最小化します"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "ランチャーを最小化"
},
"app.appearance-settings.native-decorations.description": {
"message": "OSデフォルトのウィンドウUIを使用します(アプリの再起動が必要です)"
"message": "OSデフォルトのウィンドウUIを使用します。(アプリの再起動が必要です"
},
"app.appearance-settings.native-decorations.title": {
"message": "OSのウィンドウUIを使用"
@@ -86,17 +86,23 @@
"app.appearance-settings.select-option": {
"message": "オプションを選択してください"
},
"app.appearance-settings.show-play-time.description": {
"message": "インスタンスのプレイ時間を表示します。"
},
"app.appearance-settings.show-play-time.title": {
"message": "プレイ時間を表示"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "サイドバーの表示/非表示を切り替える機能を有効にします。"
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "サイドバーの表示/非表示を切り替える"
"message": "サイドバーの表示切替を有効化"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Modrinth上にホストされていないModrinth Packファイル.mrpack)をインストールしようとした場合、インストール前にそのリスクを理解していただけるよう確認します。"
"message": "Modrinthで配布されていないModrinth Pack.mrpack)をインストールする場合、事前にリスクについて警告します。"
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "未知のMODパックインストールする前に警告してください"
"message": "不明なModパックインストール前に警告する"
},
"app.auth-servers.unreachable.body": {
"message": "Minecraftの認証サーバーは現在停止している可能性があります。インターネット接続を確認し、しばらくしてからもう一度お試しください。"
@@ -329,9 +335,6 @@
"app.update-popup.download-complete": {
"message": "ダウンロード完了"
},
"app.update-popup.reload": {
"message": "再読み込み"
},
"app.update-popup.title": {
"message": "アップデートが利用可能です"
},
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} 다운로드 중"
},
"app.action-bar.downloading-update": {
"message": "업데이트 다운로드중"
},
"app.action-bar.downloads": {
"message": "다운로드"
},
@@ -500,9 +503,6 @@
"app.update-popup.download-complete": {
"message": "다운로드 완료"
},
"app.update-popup.reload": {
"message": "새로고침"
},
"app.update-popup.title": {
"message": "업데이트 가능"
},
@@ -455,9 +455,6 @@
"app.update-popup.download-complete": {
"message": "Muat turun selesai"
},
"app.update-popup.reload": {
"message": "Muat semula"
},
"app.update-popup.title": {
"message": "Kemas kini tersedia"
},
@@ -341,9 +341,6 @@
"app.update-popup.download-complete": {
"message": "Downloaden voltooid"
},
"app.update-popup.reload": {
"message": "Herlaad"
},
"app.update-popup.title": {
"message": "Update beschikbaar"
},
+41 -2
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Pobieranie Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Pobieranie aktualizacji"
},
"app.action-bar.downloads": {
"message": "Pobrania"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Główna instancja"
},
"app.action-bar.reload-to-update": {
"message": "Załaduj ponownie, by zaktualizować"
},
"app.action-bar.show-more-running-instances": {
"message": "Pokaż więcej włączonych instancji"
},
"app.action-bar.stop-instance": {
"message": "Zatrzymaj instancję"
},
"app.action-bar.update": {
"message": "Zaktualizuj"
},
"app.action-bar.view-active-downloads": {
"message": "Pokaż aktualnie pobierane"
},
@@ -270,7 +279,7 @@
"message": "ten serwer"
},
"app.modal.install-to-play.content-required": {
"message": "Wymagane treści"
"message": "Wymagana zawartość"
},
"app.modal.install-to-play.header": {
"message": "Zainstaluj, aby grać"
@@ -422,6 +431,12 @@
"app.skins.rate-limit.title": {
"message": "Zwolnij!"
},
"app.skins.reorder-error.text": {
"message": "Nie udało się zapisać kolejności skórek."
},
"app.skins.reorder-error.title": {
"message": "Nie udało się zmienić kolejności skórek"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -495,7 +510,7 @@
"message": "Pobieranie ukończone"
},
"app.update-popup.reload": {
"message": "Załaduj ponownie"
"message": "Załaduj ponownie, by zaktualizować"
},
"app.update-popup.title": {
"message": "Dostępna aktualizacja"
@@ -692,6 +707,30 @@
"instance.settings.tabs.general.name": {
"message": "Nazwa"
},
"instance.settings.tabs.general.update-channel": {
"message": "Kanał aktualizacji"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Wersje stabilne, beta i alfa będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Tylko wersje stabilne i wersje beta będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Stabilny"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Tylko wersje stabilne będą pokazane jako dostępne aktualizacje."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Wybierz kanał aktualizacji"
},
"instance.settings.tabs.hooks": {
"message": "Haczyki startowe"
},
+74 -35
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Baixando Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Baixando atualização"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instância principal"
},
"app.action-bar.reload-to-update": {
"message": "Recarregar para atualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Exibir mais instâncias em execução"
},
"app.action-bar.stop-instance": {
"message": "Parar instância"
},
"app.action-bar.update": {
"message": "Atualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver downloads ativos"
},
@@ -42,7 +51,7 @@
"message": "Renderização avançada"
},
"app.appearance-settings.color-theme.description": {
"message": "Selecione seu tema de cores preferido para o Modrinth App."
"message": "Escolha seu tema de cores preferido para o Modrinth App."
},
"app.appearance-settings.color-theme.title": {
"message": "Tema de cores"
@@ -99,7 +108,7 @@
"message": "Alternar barra lateral"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Se você tentar instalar um arquivo Modrinth Pack (.mrpack) não hospedado no Modrinth, garantiremos que você entenda os riscos antes de instalá-lo."
"message": "Se você tentar instalar um arquivo Modrinth Pack (.mrpack) não hospedado no Modrinth, garantiremos que você entenda os riscos antes de instalar."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Avise-me antes de instalar pacotes de mods desconhecidos"
@@ -425,6 +434,12 @@
"app.skins.rate-limit.title": {
"message": "Mais lento!"
},
"app.skins.reorder-error.text": {
"message": "Não foi possível salvar a ordem de skins."
},
"app.skins.reorder-error.title": {
"message": "Falhou ao reordenar skins"
},
"app.skins.section.builders-and-biomes": {
"message": "Construtores & Biomas"
},
@@ -456,7 +471,7 @@
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "A Era do Cobre"
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
@@ -468,7 +483,7 @@
"message": "Iniciar sessão"
},
"app.skins.sign-in.description": {
"message": "Faça login na sua conta do Minecraft para usar os recursos de gerenciamento de skins do Modrinth app."
"message": "Inicie sessão na sua conta do Minecraft para usar os recursos de gerenciamento de skins no Modrinth App."
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Modrinth Bot animado"
@@ -480,10 +495,10 @@
"message": "Seletor de skins"
},
"app.update-popup.body.download-complete": {
"message": "O Modrinth App v{version} foi baixado. Recarregue para atualizar agora ou a atualização será aplicada automaticamente ao fechar o Modrinth App."
"message": "O Modrinth App v{version} foi instalado. Recarregue para atualizar agora ou ela será feita automaticamente ao fechar o Modrinth App."
},
"app.update-popup.body.linux": {
"message": "O Modrinth App v{version} está disponível. Use seu gerenciador de pacotes para atualizar e obter os recursos e correções mais recentes!"
"message": "O Modrinth App v{version} está disponível. Use o gerenciador de pacotes para atualizar para obter novos recursos e correções!"
},
"app.update-popup.body.metered": {
"message": "O Modrinth App v{version} já está disponível! Já que sua rede é limitada, não a instalamos automaticamente."
@@ -498,7 +513,7 @@
"message": "Instalação concluída"
},
"app.update-popup.reload": {
"message": "Recarregar"
"message": "Recarregar para atualizar"
},
"app.update-popup.title": {
"message": "Atualização disponível"
@@ -552,13 +567,13 @@
"message": "<link>Adicione amigos</link> para ver o que eles estão jogando!"
},
"friends.friend.cancel-request": {
"message": "Cancelar solicitação"
"message": "Cancelar pedido"
},
"friends.friend.remove-friend": {
"message": "Remover amigo"
},
"friends.friend.request-sent": {
"message": "Solicitação de amizade enviada"
"message": "Pedido de amizade enviado"
},
"friends.friend.view-profile": {
"message": "Ver perfil"
@@ -612,7 +627,7 @@
"message": "Editar servidor"
},
"instance.edit-world.hide-from-home": {
"message": "Esconder da página inicial"
"message": "Ocultar da página inicial"
},
"instance.edit-world.name": {
"message": "Nome"
@@ -654,7 +669,7 @@
"message": "Excluir instância"
},
"instance.settings.tabs.general.delete.description": {
"message": "Exclui permanentemente uma instância do seu dispositivo, incluindo seus mundos, configurações e todo o conteúdo instalado. Tome cuidado, pois, uma vez excluída, a instância não pode ser recuperada."
"message": "Exclui permanentemente uma instância do seu dispositivo, incluindo seus mundos, configs e todo o conteúdo instalado. Tome cuidado, assim que excluído, a instância não pode ser recuperada."
},
"instance.settings.tabs.general.deleting.button": {
"message": "Excluindo..."
@@ -669,7 +684,7 @@
"message": "Duplicar instância"
},
"instance.settings.tabs.general.duplicate-instance.description": {
"message": "Cria uma cópia desta instância, incluindo mundos, configurações, mods, etc."
"message": "Cria uma cópia desta instância, incluindo mundos, configs, mods, etc."
},
"instance.settings.tabs.general.edit-icon": {
"message": "Editar ícone"
@@ -687,7 +702,7 @@
"message": "Grupos"
},
"instance.settings.tabs.general.library-groups.create": {
"message": "Criar novo grupo"
"message": "Criar grupo novo"
},
"instance.settings.tabs.general.library-groups.description": {
"message": "Os grupos da biblioteca permitem que você organize suas instâncias em diferentes seções."
@@ -698,6 +713,30 @@
"instance.settings.tabs.general.name": {
"message": "Nome"
},
"instance.settings.tabs.general.update-channel": {
"message": "Canal de atualização"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "As versões release, beta e alpha aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "As versões release e beta aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Release"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Apenas versões release aparecerão como atualizações disponíveis."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Selecionar canal de atualização"
},
"instance.settings.tabs.hooks": {
"message": "Ações de inicialização"
},
@@ -705,7 +744,7 @@
"message": "Ações de inicialização personalizadas"
},
"instance.settings.tabs.hooks.description": {
"message": "Essas ações permitem que usuários mais experientes executem comandos do sistema antes e depois de inicializar o jogo."
"message": "As ações permitem usuários experientes que executem certos comandos do sistema antes ou após iniciar o jogo."
},
"instance.settings.tabs.hooks.post-exit": {
"message": "Ao sair"
@@ -714,16 +753,16 @@
"message": "Executado após o jogo fechar."
},
"instance.settings.tabs.hooks.post-exit.enter": {
"message": "Insira o comando a ser executado após o jogo fechar..."
"message": "Insira o comando ao sair..."
},
"instance.settings.tabs.hooks.pre-launch": {
"message": "Pré-inicialização"
"message": "Ao iniciar"
},
"instance.settings.tabs.hooks.pre-launch.description": {
"message": "Executado antes que a instância seja inicializada."
},
"instance.settings.tabs.hooks.pre-launch.enter": {
"message": "Insira o comando a ser executado antes da inicialização..."
"message": "Insira o comando ao iniciar..."
},
"instance.settings.tabs.hooks.title": {
"message": "Ações de inicialização do jogo"
@@ -735,13 +774,13 @@
"message": "Comando auxiliar para iniciar o Minecraft."
},
"instance.settings.tabs.hooks.wrapper.enter": {
"message": "Insira um comando..."
"message": "Insira um comando auxiliar..."
},
"instance.settings.tabs.installation": {
"message": "Instalação"
},
"instance.settings.tabs.installation.loader-version": {
"message": "Versão do {loader}"
"message": "Versão {loader}"
},
"instance.settings.tabs.java": {
"message": "Java e memória"
@@ -786,13 +825,13 @@
"message": "Janela"
},
"instance.settings.tabs.window.custom-window-settings": {
"message": "Configurações de janela personalizada"
"message": "Opções de janela personalizada"
},
"instance.settings.tabs.window.fullscreen": {
"message": "Tela cheia"
},
"instance.settings.tabs.window.fullscreen.description": {
"message": "Faz com que o jogo inicie em tela cheia (usando options.txt)."
"message": "Faz o jogo iniciar em tela cheia (usando options.txt)."
},
"instance.settings.tabs.window.height": {
"message": "Altura"
@@ -822,7 +861,7 @@
"message": "Copiar endereço"
},
"instance.worlds.dont_show_on_home": {
"message": "Não mostrar no início"
"message": "Não exibir na página inicial"
},
"instance.worlds.game_already_open": {
"message": "A instância já está aberta"
@@ -834,19 +873,19 @@
"message": "Servidor incompatível"
},
"instance.worlds.linked_server": {
"message": "Gerenciado pelo projeto de servidor"
"message": "Gerenciado pelo projeto do servidor"
},
"instance.worlds.no_contact": {
"message": "Não foi possível conectar-se ao servidor"
},
"instance.worlds.no_server_quick_play": {
"message": "Você só pode entrar diretamente em servidores a partir do Minecraft 1.0.5"
"message": "Só é possível entrar diretamente em servidores a partir do Minecraft Alpha 1.0.5"
},
"instance.worlds.no_singleplayer_quick_play": {
"message": "Você só pode entrar diretamente em mundos de um jogador a partir do Minecraft 1.20+"
"message": "Só é possível entrar em mundos um jogador diretamente a partir do Minecraft 1.20"
},
"instance.worlds.play_instance": {
"message": "Jogar na instância"
"message": "Iniciar instância"
},
"instance.worlds.view_instance": {
"message": "Ver instância"
@@ -858,7 +897,7 @@
"message": "Adicionar conta"
},
"minecraft-account.label": {
"message": "Conta do minecraft"
"message": "Conta de Minecraft"
},
"minecraft-account.not-signed-in": {
"message": "Não conectado"
@@ -870,7 +909,7 @@
"message": "Selecionar conta"
},
"minecraft-account.sign-in": {
"message": "Faça login no Minecraft"
"message": "Inicie sessão no Minecraft"
},
"search.filter.locked.instance": {
"message": "Fornecido pela instância"
@@ -879,7 +918,7 @@
"message": "A versão do jogo é fornecida pela instância"
},
"search.filter.locked.instance-loader.title": {
"message": "O carregador é fornecido pela instância"
"message": "O loader é fornecido pela instância"
},
"search.filter.locked.instance.sync": {
"message": "Sincronizar com a instância"
@@ -888,19 +927,19 @@
"message": "Fornecido pelo servidor"
},
"search.filter.locked.server-environment.title": {
"message": "Apenas mods do lado do cliente podem ser adicionados à instância do servidor"
"message": "Somente mods do cliente podem ser adicionados à instância do servidor"
},
"search.filter.locked.server-game-version.title": {
"message": "A versão do jogo é fornecida pelo servidor"
},
"search.filter.locked.server-loader.title": {
"message": "O carregador é fornecido pelo servidor"
"message": "O loader é fornecido pelo servidor"
},
"unknown-pack-warning-modal.body": {
"message": "Um arquivo só é revisado se for enviado para o Modrinth, não importa o seu formato de arquivo (incluindo .mrpack)."
"message": "Um arquivo só é revisado se for enviado no Modrinth, independente do formato do arquivo (incluindo .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Não mostrar este aviso novamente"
"message": "Não exibir aviso novamente"
},
"unknown-pack-warning-modal.header": {
"message": "Confirmar instalação"
@@ -909,10 +948,10 @@
"message": "Instalar mesmo assim"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Malware é frequentemente distribuído através de arquivos de pacotes de mods compartilhados em plataformas como o Discord."
"message": "O malware é distribuído frequentemente através de arquivos de pacote de mods compartilhados em plataformas como Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Não foi possível encontrar este arquivo no Modrinth. Recomendamos fortemente que você instale apenas arquivos de fontes confiáveis."
"message": "Não encontramos este arquivo no Modrinth. Nós recomendamos fortemente instalar arquivos apenas de fontes confiáveis."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Aviso de arquivo desconhecido"
+30 -3
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "A descarregar a versão do java {version}"
},
"app.action-bar.downloading-update": {
"message": "A transferir atualização"
},
"app.action-bar.downloads": {
"message": "Downloads"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Instância principal"
},
"app.action-bar.reload-to-update": {
"message": "Recarregar para atualizar"
},
"app.action-bar.show-more-running-instances": {
"message": "Mostrar mais instâncias a correr"
},
"app.action-bar.stop-instance": {
"message": "Para instância"
},
"app.action-bar.update": {
"message": "Atualizar"
},
"app.action-bar.view-active-downloads": {
"message": "Ver instâncias ativas"
},
@@ -35,6 +44,9 @@
"app.action-bar.view-logs": {
"message": "Ver logs"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "Ativa a renderização avançada, como efeitos de desfoque, que podem causar problemas de desempenho sem o uso de renderização acelerada por hardware."
},
"app.appearance-settings.advanced-rendering.title": {
"message": "Renderização avançada"
},
@@ -56,6 +68,12 @@
"app.appearance-settings.default-landing-page.title": {
"message": "Página de carregamento principal"
},
"app.appearance-settings.minimize-launcher.title": {
"message": "Minimizar launcher"
},
"app.appearance-settings.select-option": {
"message": "Seleciona uma opção"
},
"app.auth-servers.unreachable.body": {
"message": "Os servidores de autenticação do Minecraft poderão estar em baixo de momento. Verifica a tua ligação à internet e tenta novamente mais tarde."
},
@@ -80,6 +98,9 @@
"app.browse.discover-servers": {
"message": "Descobrir servidores"
},
"app.browse.project-type.modpacks": {
"message": "Modpacks"
},
"app.export-modal.description-placeholder": {
"message": "Insere a descrição do modpack..."
},
@@ -239,6 +260,15 @@
"app.settings.tabs.resource-management": {
"message": "Gestão de recursos"
},
"app.skins.delete-modal.title": {
"message": "Tens a certeza que queres apagar esta skin?"
},
"app.skins.dropped-file-error.text": {
"message": "Não foi possível ler o ficheiro largado."
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} acabou de ser transferida. Recarrega para atualizar agora, ou automaticamente quando fechares a Modrinth App."
},
@@ -257,9 +287,6 @@
"app.update-popup.download-complete": {
"message": "Transferência concluída"
},
"app.update-popup.reload": {
"message": "Recarregar"
},
"app.update-popup.title": {
"message": "Atualização disponível"
},
@@ -83,9 +83,6 @@
"app.update-popup.download-complete": {
"message": "Descărcare finalizată"
},
"app.update-popup.reload": {
"message": "Reîncărcați"
},
"app.update-popup.title": {
"message": "Actualizare disponibilă"
},
+49 -1
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Скачивание Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Скачивание"
},
"app.action-bar.downloads": {
"message": "Загрузки"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Основная сборка"
},
"app.action-bar.reload-to-update": {
"message": "Перезапустить"
},
"app.action-bar.show-more-running-instances": {
"message": "Показать другие активные сборки"
},
"app.action-bar.stop-instance": {
"message": "Остановить сборку"
},
"app.action-bar.update": {
"message": "Обновить"
},
"app.action-bar.view-active-downloads": {
"message": "Посмотреть текущие загрузки"
},
@@ -156,11 +165,17 @@
"message": "Вы используете более старую версию. Рекомендуем обновиться сейчас, чтобы получить последние исправления и улучшения."
},
"astralrinth.app.launcher-update-modal.update.download-action": {
"message": "Скачать обновление и закрыть"
"message": "Скачать обновление"
},
"astralrinth.app.launcher-update-modal.update.header": {
"message": "Обновление лаунчера AstralRinth"
},
"astralrinth.app.launcher-update-modal.update.installer-description": {
"message": "Выберите пакет установщика, с которым хотите продолжить."
},
"astralrinth.app.launcher-update-modal.update.installer-title": {
"message": "Тип установщика"
},
"astralrinth.app.launcher-update-modal.update.installed-version": {
"message": "💾 Установленная и запущенная версия:"
},
@@ -188,6 +203,9 @@
"astralrinth.app.launcher-update-modal.update.repository-link": {
"message": "Открыть репозиторий проекта"
},
"astralrinth.app.launcher-update-modal.update.select-installer": {
"message": "Выберите установщик"
},
"astralrinth.app.launcher-update-modal.update.title": {
"message": "Доступна новая версия лаунчера AstralRinth."
},
@@ -578,6 +596,12 @@
"app.skins.rate-limit.title": {
"message": "Не так быстро!"
},
"app.skins.reorder-error.text": {
"message": "Ваш порядок скинов не удалось сохранить."
},
"app.skins.reorder-error.title": {
"message": "Не удалось изменить порядок скинов"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
},
@@ -845,6 +869,30 @@
"instance.settings.tabs.general.name": {
"message": "Название"
},
"instance.settings.tabs.general.update-channel": {
"message": "Канал обновления"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Альфа"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Релизные, бета и альфа версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Бета"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Релизные и бета версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Релиз"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Только релизные версии будут отображаться как доступные обновления."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Выберите канал обновления"
},
"instance.settings.tabs.hooks": {
"message": "Команды запуска"
},
+184 -7
View File
@@ -2,17 +2,20 @@
"app.action-bar.downloading-java": {
"message": "Laddar ner Java {version}"
},
"app.action-bar.downloading-update": {
"message": "Laddar ner uppdatering"
},
"app.action-bar.downloads": {
"message": "Nedladdningar"
},
"app.action-bar.hide-more-running-instances": {
"message": "Göm fler pågående instanser"
"message": "Göm fler aktiva instanser"
},
"app.action-bar.make-primary-instance": {
"message": "Gör till primärinstans"
},
"app.action-bar.no-instances-running": {
"message": "Ingen pågående instans"
"message": "Ingen aktiva instans"
},
"app.action-bar.offline": {
"message": "Offline"
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Primärinstans"
},
"app.action-bar.reload-to-update": {
"message": "Ladda om för att uppdatera"
},
"app.action-bar.show-more-running-instances": {
"message": "Visa fler pågående instanser"
"message": "Visa fler aktiva instanser"
},
"app.action-bar.stop-instance": {
"message": "Avsluta instans"
},
"app.action-bar.update": {
"message": "Uppdatera"
},
"app.action-bar.view-active-downloads": {
"message": "Visa aktiva nedladdningar"
},
@@ -77,18 +86,33 @@
"app.appearance-settings.select-option": {
"message": "Välj ett alternativ"
},
"app.appearance-settings.show-play-time.description": {
"message": "Visar hur mycket tid du spelat på en instans."
},
"app.appearance-settings.show-play-time.title": {
"message": "Visa speltid"
},
"app.appearance-settings.toggle-sidebar.description": {
"message": "Låter dig växla sidofältet."
},
"app.appearance-settings.toggle-sidebar.title": {
"message": "Växla sidofält"
},
"app.appearance-settings.unknown-pack-warning.description": {
"message": "Om du försöker installera en Modrinth Pack fil (.mrpack) som inte kommer från Modrinth ser vi till att du känner till riskerna innan du installerar den."
},
"app.appearance-settings.unknown-pack-warning.title": {
"message": "Varna mig innan jag installerar okända modpaket"
},
"app.auth-servers.unreachable.body": {
"message": "Minecrafts autentiseringsservrar kan vara nere just nu. Kontrollera din internetanslutning och försök igen senare."
},
"app.auth-servers.unreachable.header": {
"message": "Kan ej nå autentiseringsservrarna"
},
"app.browse.add-servers-to-instance": {
"message": "Lägger till server till instans"
},
"app.browse.add-to-an-instance": {
"message": "Lägg till i en instans"
},
@@ -122,6 +146,9 @@
"app.browse.server.installing": {
"message": "Installerar"
},
"app.content-install.no-compatible-versions": {
"message": "Ingen tillgänglig version matchar {compatibilityLabel}. Välj en version att installera ändå. Beroenden kommer inte installeras automatiskt."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -146,6 +173,9 @@
"app.export-modal.modpack-name-placeholder": {
"message": "Modpaketets namn"
},
"app.export-modal.select-files-label": {
"message": "Konfigurera vilka filer som inkluderas i denna export"
},
"app.export-modal.version-number-label": {
"message": "Versionsnummer"
},
@@ -153,7 +183,7 @@
"message": "1.0.0"
},
"app.instance.confirm-delete.admonition-body": {
"message": "All data från din instans kommer att raderas permanent, inklusive dina världar, konfigurationer och allt installerat innehåll."
"message": "All data från din instans kommer permanent raderas, däribland dina världar, konfigurationer samt allt installerat innehåll."
},
"app.instance.confirm-delete.admonition-header": {
"message": "Detta kan inte ogöras"
@@ -275,6 +305,12 @@
"app.modal.update-to-play.update-required-description": {
"message": "En uppdatering krävs för att spela {name}. Vänligen uppdatera till senaste version för att starta spelet."
},
"app.project.install-button.already-installed": {
"message": "Detta projekt är redan installerat"
},
"app.project.install-context.install-content-to-instance": {
"message": "Installera innehåll till instans"
},
"app.settings.developer-mode-enabled": {
"message": "Utvecklarläge aktiverat."
},
@@ -302,9 +338,27 @@
"app.skins.add-button": {
"message": "Lägg till utseende"
},
"app.skins.add-button.drag-and-drop": {
"message": "Dra och släpp"
},
"app.skins.apply-button": {
"message": "Tillämpa"
},
"app.skins.delete-button": {
"message": "Ta bort utseende"
},
"app.skins.delete-modal.description": {
"message": "Du kommer permanent radera det valda utseendet. Detta kan inte ångras."
},
"app.skins.delete-modal.title": {
"message": "Är du säker på att du vill radera utseendet?"
},
"app.skins.dropped-file-error.text": {
"message": "Misslyckades att läsa den släppta filen."
},
"app.skins.dropped-file-error.title": {
"message": "Fel med behandlingen av filen"
},
"app.skins.edit-button": {
"message": "Redigera utseende"
},
@@ -353,6 +407,66 @@
"app.skins.modal.texture-section": {
"message": "Textur"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Ladda upp utseende först!"
},
"app.skins.preview.edit-button": {
"message": "Redigera utseende"
},
"app.skins.previewing-badge": {
"message": "Förhandsvisa"
},
"app.skins.rate-limit.text": {
"message": "Du ändrar ditt utseende för ofta. Mojangs servrar har temporärt blockerat ytterligare förfrågningar. Vänligen vänta en stund innan du försöker igen."
},
"app.skins.rate-limit.title": {
"message": "Sakta ner!"
},
"app.skins.section.chase-the-skies": {
"message": "Chase the Skies"
},
"app.skins.section.default-skins": {
"message": "Standard utseenden"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth Pride"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "Du har fått dessa utseende eftersom du donerat till en Modrinth Pride insamling under pride månaden."
},
"app.skins.section.mounts-of-mayhem": {
"message": "Mounts of Mayhem"
},
"app.skins.section.saved-skins": {
"message": "Sparade utseenden"
},
"app.skins.section.the-copper-age": {
"message": "The Copper Age"
},
"app.skins.section.the-garden-awakens": {
"message": "The Garden Awakens"
},
"app.skins.section.tiny-takeover": {
"message": "Tiny Takeover"
},
"app.skins.sign-in.button": {
"message": "Logga in"
},
"app.skins.sign-in.rinthbot-alt": {
"message": "Exalterad Modrinth bot"
},
"app.skins.sign-in.title": {
"message": "Vänligen logga in"
},
"app.skins.title": {
"message": "Utseende väljare"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} har laddats ner. Ladda om för att uppdatera nu, eller automatiskt när du stänger Modrinth App."
},
@@ -372,7 +486,7 @@
"message": "Nedladdning slutförd"
},
"app.update-popup.reload": {
"message": "Ladda om"
"message": "Ladda om för att uppdatera"
},
"app.update-popup.title": {
"message": "Uppdatering tillgänglig"
@@ -572,6 +686,30 @@
"instance.settings.tabs.general.name": {
"message": "Namn"
},
"instance.settings.tabs.general.update-channel": {
"message": "Uppdateringskanal"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Utgåvor, beta och alfa versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Utgåvor och beta versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Utgåva"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Endast utgivna versioner visas som tillgängliga uppdateringar."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Välj uppdateringskanal"
},
"instance.settings.tabs.hooks": {
"message": "Startkrokar"
},
@@ -620,6 +758,24 @@
"instance.settings.tabs.java": {
"message": "Java och minne"
},
"instance.settings.tabs.java.custom-environment-variables": {
"message": "Anpassade miljövariabler"
},
"instance.settings.tabs.java.custom-java-arguments": {
"message": "Anpassade Java-argument"
},
"instance.settings.tabs.java.custom-java-installation": {
"message": "Anpassad Java-installation"
},
"instance.settings.tabs.java.custom-memory-allocation": {
"message": "Anpassad minnestilldelning"
},
"instance.settings.tabs.java.enter-environment-variables": {
"message": "Skriv in miljövariabler..."
},
"instance.settings.tabs.java.enter-java-arguments": {
"message": "Skriv in Java-argument..."
},
"instance.settings.tabs.java.environment-variables": {
"message": "Miljövariabler"
},
@@ -635,6 +791,9 @@
"instance.settings.tabs.java.java-memory": {
"message": "Tilldelat minne"
},
"instance.settings.tabs.java.java-path-placeholder": {
"message": "/sökväg/till/java"
},
"instance.settings.tabs.window": {
"message": "Fönster"
},
@@ -651,7 +810,7 @@
"message": "Höjd"
},
"instance.settings.tabs.window.height.description": {
"message": "Höjden på spel-fönstret vid uppstart."
"message": "Höjden på spelfönstret vid uppstart."
},
"instance.settings.tabs.window.height.enter": {
"message": "Ange höjd..."
@@ -660,7 +819,7 @@
"message": "Bredd"
},
"instance.settings.tabs.window.width.description": {
"message": "Bredden på spel-fönstret vid uppstart."
"message": "Bredden på spelfönstret vid uppstart."
},
"instance.settings.tabs.window.width.enter": {
"message": "Ange bredd..."
@@ -749,7 +908,25 @@
"search.filter.locked.server-loader.title": {
"message": "Loader tillhandahålls av servern"
},
"unknown-pack-warning-modal.body": {
"message": "En fil granskas bara om den laddas upp till Modrinth, oavsett dess filformat (däribland .mrpack)."
},
"unknown-pack-warning-modal.dont-show-again": {
"message": "Visa inte denna varning igen"
},
"unknown-pack-warning-modal.header": {
"message": "Bekräfta installation"
},
"unknown-pack-warning-modal.install-anyway": {
"message": "Installera ändå"
},
"unknown-pack-warning-modal.malware-statement": {
"message": "Skadeprogram distribueras ofta via modpaketfiler genom att dela dem på plattformar som Discord."
},
"unknown-pack-warning-modal.warning.body": {
"message": "Vi kunde inte hitta filen på Modrinth. Vi rekommenderar starkt att endast installera filer från källor du litar på."
},
"unknown-pack-warning-modal.warning.title": {
"message": "Okänd filvarning"
}
}
@@ -326,9 +326,6 @@
"app.update-popup.download-complete": {
"message": "การดาวน์โหลดเสร็จสมบูรณ์"
},
"app.update-popup.reload": {
"message": "โหลดใหม่"
},
"app.update-popup.title": {
"message": "มีการอัปเดตใหม่"
},
+40 -1
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "Java {version} indiriliyor"
},
"app.action-bar.downloading-update": {
"message": "Güncelleme indiriliyor"
},
"app.action-bar.downloads": {
"message": "İndirmeler"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "Birincil kurulum"
},
"app.action-bar.reload-to-update": {
"message": "Güncellemek için yeniden yükle"
},
"app.action-bar.show-more-running-instances": {
"message": "Daha fazla çalışan kurulum göster"
},
"app.action-bar.stop-instance": {
"message": "Kurulumu durdur"
},
"app.action-bar.update": {
"message": "Güncelle"
},
"app.action-bar.view-active-downloads": {
"message": "Şu anda indirilienleri göster"
},
@@ -428,6 +437,12 @@
"app.skins.rate-limit.title": {
"message": "Yavaşla!"
},
"app.skins.reorder-error.text": {
"message": "Skinini kaydedemedik."
},
"app.skins.reorder-error.title": {
"message": "Skinler sıralanamadı"
},
"app.skins.section.builders-and-biomes": {
"message": "İnşaatçılar & Biyomlar"
},
@@ -501,7 +516,7 @@
"message": "İndirme tamamlandı"
},
"app.update-popup.reload": {
"message": "Yeniden başlat"
"message": "Güncellemek için yeniden yükle"
},
"app.update-popup.title": {
"message": "Güncelleme mevcut"
@@ -698,6 +713,30 @@
"instance.settings.tabs.general.name": {
"message": "Ad"
},
"instance.settings.tabs.general.update-channel": {
"message": "Güncelleme kanalı"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alfa"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "Release, beta ve alpha versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "Release ve beta versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.release": {
"message": "Yayın"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "Yalnızca release versiyonları mevcut güncellemeler olarak gösterilecek."
},
"instance.settings.tabs.general.update-channel.select": {
"message": "Güncelleme kanalını seç"
},
"instance.settings.tabs.hooks": {
"message": "Başlatma kancaları"
},
+18 -18
View File
@@ -149,6 +149,9 @@
"app.browse.server.installing": {
"message": "Установлення"
},
"app.content-install.no-compatible-versions": {
"message": "Жодна доступна версія не сумісна з {compatibilityLabel}. Виберіть версію, щоб усе одно її встановити. Залежності не встановлюватимуться автоматично."
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
@@ -195,7 +198,7 @@
"message": "Видалити профіль"
},
"app.instance.modpack-already-installed.body": {
"message": "Ця збірка вже встановлена в профілі <bold>«{instanceName}»</bold>. Ви впевнені, що хочете її скопіювати?"
"message": "Ця збірка вже встановлена в профілі <bold>«{instanceName}»</bold>. Дійсно бажаєте її клонувати?"
},
"app.instance.modpack-already-installed.create": {
"message": "Створити"
@@ -285,13 +288,13 @@
"message": "Дивитися вміст"
},
"app.modal.update-to-play.header": {
"message": "Оновлення перед грою"
"message": "Оновлення для гри"
},
"app.modal.update-to-play.update-required": {
"message": "Необхідне оновлення"
},
"app.modal.update-to-play.update-required-description": {
"message": "«{name}» потребує оновлення, щоб грати. Будь ласка, оновіть гру до останньої версії, щоб запустити її."
"message": "«{name}» потребує оновлення, щоб грати. Будь ласка, оновіть до останньої версії, щоб запустити гру."
},
"app.project.install-button.already-installed": {
"message": "Проєкт уже встановлено"
@@ -330,7 +333,7 @@
"message": "Додати скін"
},
"app.skins.add-button.drag-and-drop": {
"message": "Перетягніть і вставте"
"message": "Перетягніть сюди"
},
"app.skins.apply-button": {
"message": "Застосувати"
@@ -342,7 +345,7 @@
"message": "Це видалить вибраний скін назавжди. Ця дія не може бути скасована."
},
"app.skins.delete-modal.title": {
"message": "Чи ви впевнені що хочете видалити цей скін?"
"message": "Ви дійсно бажаєте видалити цей скін?"
},
"app.skins.dropped-file-error.text": {
"message": "Неможливо відкрити вибраний файл."
@@ -378,19 +381,19 @@
"message": "Редагування скіна"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "Відредагуйте скін для початку!"
"message": "Спершу відредагуйте скін!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "Без плаща"
},
"app.skins.modal.none-cape-option": {
"message": "Без"
"message": "Немає"
},
"app.skins.modal.replace-texture-button": {
"message": "Замінити текстуру"
},
"app.skins.modal.save-skin-button": {
"message": "Зберегти вигляд"
"message": "Зберегти скін"
},
"app.skins.modal.saving-tooltip": {
"message": "Збереження…"
@@ -399,7 +402,7 @@
"message": "Текстура"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "Завантажте скін для початку!"
"message": "Спершу завантажте скін!"
},
"app.skins.preview.edit-button": {
"message": "Редагувати скін"
@@ -408,10 +411,10 @@
"message": "Попередній перегляд"
},
"app.skins.rate-limit.text": {
"message": "Ви змінюєте свій скін дуже часто. Сервери Mojang тимчасово заблокували подальші запити. Зачекайте трохи перш ніж спробувати знову."
"message": "Ви змінюєте скін занадто часто. Сервери Mojang тимчасово заблокували подальші запити. Зачекайте трохи, перш ніж спробувати знову."
},
"app.skins.rate-limit.title": {
"message": "Шановний!"
"message": "Повільніше!"
},
"app.skins.section.builders-and-biomes": {
"message": "Builders & Biomes"
@@ -468,13 +471,13 @@
"message": "Вибір скіну"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} завантажено. Перезапустіть зараз, щоб оновити його, або це відбудеться автоматично після закриття Modrinth App."
"message": "Modrinth App v{version} завантажено. Перезапустіть, щоб оновити зараз, або це відбудеться автоматично після закриття застосунку."
},
"app.update-popup.body.linux": {
"message": "Modrinth App v{version} вже доступно. Скористайтеся вашим менеджером пакетів, щоб отримати найновіші функції та виправлення!"
"message": "Modrinth App v{version} вже доступно. Скористайтеся менеджером пакетів, щоб отримати найновіші функції та виправлення!"
},
"app.update-popup.body.metered": {
"message": "Modrinth App v{version} доступний для завантаження! Оскільки ви на лімітному з’єднанні, ми не завантажили його автоматично."
"message": "Modrinth App v{version} вже доступно! Оскільки ви використовуєте лімітне підключення, ми не завантажили його автоматично."
},
"app.update-popup.changelog": {
"message": "Журнал змін"
@@ -485,9 +488,6 @@
"app.update-popup.download-complete": {
"message": "Завантаження завершено"
},
"app.update-popup.reload": {
"message": "Перезавантажити"
},
"app.update-popup.title": {
"message": "Доступне оновлення"
},
@@ -510,7 +510,7 @@
"message": "Ще не зіграно"
},
"app.world.world-item.offline": {
"message": "Поза мережею"
"message": "Не в мережі"
},
"app.world.world-item.players-online": {
"message": "{count} у мережі"
@@ -494,9 +494,6 @@
"app.update-popup.download-complete": {
"message": "Cài đặt thành công"
},
"app.update-popup.reload": {
"message": "Khởi động lại"
},
"app.update-popup.title": {
"message": "Cập nhật mới hiện đang khả dụng"
},
+41 -2
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "正在下载 Java {version}"
},
"app.action-bar.downloading-update": {
"message": "下载更新中"
},
"app.action-bar.downloads": {
"message": "下载"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "主实例"
},
"app.action-bar.reload-to-update": {
"message": "重新加载以更新"
},
"app.action-bar.show-more-running-instances": {
"message": "显示更多运行中的实例"
},
"app.action-bar.stop-instance": {
"message": "停止实例"
},
"app.action-bar.update": {
"message": "更新"
},
"app.action-bar.view-active-downloads": {
"message": "查看正在下载内容"
},
@@ -111,7 +120,7 @@
"message": "无法连接到身份验证服务器"
},
"app.browse.add-servers-to-instance": {
"message": "将服务器添加到实例"
"message": "正在添加服务器到此实例"
},
"app.browse.add-to-an-instance": {
"message": "添加到实例"
@@ -416,6 +425,12 @@
"app.skins.rate-limit.title": {
"message": "慢点!"
},
"app.skins.reorder-error.text": {
"message": "未能保存皮肤顺序。"
},
"app.skins.reorder-error.title": {
"message": "皮肤排序失败"
},
"app.skins.section.builders-and-biomes": {
"message": "建造者与生物群系"
},
@@ -489,7 +504,7 @@
"message": "下载完成"
},
"app.update-popup.reload": {
"message": "重新启动"
"message": "重新加载以更新"
},
"app.update-popup.title": {
"message": "有可用的更新"
@@ -686,6 +701,30 @@
"instance.settings.tabs.general.name": {
"message": "名称"
},
"instance.settings.tabs.general.update-channel": {
"message": "更新渠道"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha 测试版"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "正式版,beta 和 alpha 测试版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta 测试版"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "正式版和 beta 测试版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "正式版"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "只有正式版会被显示为可用更新。"
},
"instance.settings.tabs.general.update-channel.select": {
"message": "选择更新渠道"
},
"instance.settings.tabs.hooks": {
"message": "启动 Hooks"
},
+133 -28
View File
@@ -2,6 +2,9 @@
"app.action-bar.downloading-java": {
"message": "正在下載 Java {version}"
},
"app.action-bar.downloading-update": {
"message": "正在下載更新"
},
"app.action-bar.downloads": {
"message": "下載"
},
@@ -20,12 +23,18 @@
"app.action-bar.primary-instance": {
"message": "主要實例"
},
"app.action-bar.reload-to-update": {
"message": "重新載入即可更新"
},
"app.action-bar.show-more-running-instances": {
"message": "顯示更多執行中的實例"
},
"app.action-bar.stop-instance": {
"message": "停止實例"
},
"app.action-bar.update": {
"message": "更新"
},
"app.action-bar.view-active-downloads": {
"message": "查看正在下載的項目"
},
@@ -33,10 +42,10 @@
"message": "查看實例"
},
"app.action-bar.view-logs": {
"message": "查看紀錄"
"message": "查看紀錄"
},
"app.appearance-settings.advanced-rendering.description": {
"message": "啟用進階繪製(如模糊效果)若無硬體加速可能會導致效能問題。"
"message": "啟用進階繪製(如模糊效果)若無硬體加速可能會導致效能問題。"
},
"app.appearance-settings.advanced-rendering.title": {
"message": "進階繪製"
@@ -78,16 +87,16 @@
"message": "最小化啟動器"
},
"app.appearance-settings.native-decorations.description": {
"message": "使用系統視窗外框(需要重新啟動應用程式)。"
"message": "使用系統內建的視窗外框(需要重應用程式)。"
},
"app.appearance-settings.native-decorations.title": {
"message": "原生裝飾"
"message": "原生視窗"
},
"app.appearance-settings.select-option": {
"message": "選擇選項"
},
"app.appearance-settings.show-play-time.description": {
"message": "顯示在該實例的遊玩時間"
"message": "顯示在該實例的遊玩時間"
},
"app.appearance-settings.show-play-time.title": {
"message": "顯示遊玩時間"
@@ -149,14 +158,17 @@
"app.browse.server.installing": {
"message": "安裝中"
},
"app.content-install.no-compatible-versions": {
"message": "沒有符合 {compatibilityLabel} 的可用版本。請選擇一個版本強制安裝。不會自動安裝相依模組。"
},
"app.creation-modal.installing-modpack.description": {
"message": "{fileName}"
},
"app.creation-modal.installing-modpack.title": {
"message": "正在安裝模組包……"
"message": "正在安裝模組包..."
},
"app.export-modal.description-placeholder": {
"message": "輸入模組包描述……"
"message": "輸入模組包描述..."
},
"app.export-modal.export-button": {
"message": "匯出"
@@ -255,7 +267,7 @@
"message": "確定要移除「{name}」嗎?"
},
"app.instance.worlds.search-worlds-placeholder": {
"message": "搜尋 {count} 個世界……"
"message": "搜尋 {count} 個世界..."
},
"app.instance.worlds.this-server": {
"message": "這個伺服器"
@@ -327,25 +339,46 @@
"message": "資源管理"
},
"app.skins.add-button": {
"message": "新增皮膚"
"message": "新增外觀"
},
"app.skins.add-button.drag-and-drop": {
"message": "拖曳"
},
"app.skins.apply-button": {
"message": "確認"
"message": "套用"
},
"app.skins.delete-button": {
"message": "刪除皮膚"
"message": "刪除外觀"
},
"app.skins.delete-modal.description": {
"message": "這會永久刪除所選的外觀,這個動作無法復原。"
},
"app.skins.delete-modal.title": {
"message": "你確定要刪除這個皮膚嗎"
"message": "你確定要刪除這個外觀嗎?"
},
"app.skins.dropped-file-error.text": {
"message": "無法讀取拖曳的檔案。"
},
"app.skins.dropped-file-error.title": {
"message": "處理檔案時發生錯誤"
},
"app.skins.edit-button": {
"message": "編輯皮膚"
"message": "編輯外觀"
},
"app.skins.modal.add-skin-button": {
"message": "新增皮膚"
"message": "新增外觀"
},
"app.skins.modal.add-title": {
"message": "正在新增皮膚"
"message": "新增外觀"
},
"app.skins.modal.arm-style-section": {
"message": "手臂身形"
},
"app.skins.modal.arm-style-slim": {
"message": "苗條"
},
"app.skins.modal.arm-style-wide": {
"message": "寬"
},
"app.skins.modal.cape-fallback-name": {
"message": "披風"
@@ -354,7 +387,10 @@
"message": "披風"
},
"app.skins.modal.edit-title": {
"message": "編輯皮膚"
"message": "編輯外觀"
},
"app.skins.modal.make-edit-first-tooltip": {
"message": "請先對外觀進行編輯!"
},
"app.skins.modal.no-cape-tooltip": {
"message": "無披風"
@@ -363,46 +399,91 @@
"message": "無"
},
"app.skins.modal.replace-texture-button": {
"message": "覆蓋材質"
"message": "取代材質"
},
"app.skins.modal.save-skin-button": {
"message": "儲存皮膚"
"message": "儲存外觀"
},
"app.skins.modal.saving-tooltip": {
"message": "正在儲存"
"message": "正在儲存..."
},
"app.skins.modal.texture-section": {
"message": "材質"
"message": "材質"
},
"app.skins.modal.upload-skin-first-tooltip": {
"message": "馬上新增一個皮膚"
"message": "請先上傳外觀檔案!"
},
"app.skins.preview.edit-button": {
"message": "編輯皮膚"
"message": "編輯外觀"
},
"app.skins.previewing-badge": {
"message": "正在預覽"
"message": "預覽"
},
"app.skins.rate-limit.text": {
"message": "你變更外觀的頻率太過頻繁。Mojang 伺服器已暫時封鎖後續請求,請稍等片刻,然後再試一次。"
},
"app.skins.rate-limit.title": {
"message": "慢一點"
"message": "慢一點"
},
"app.skins.reorder-error.text": {
"message": "無法儲存你的外觀排序。"
},
"app.skins.reorder-error.title": {
"message": "重新排序外觀失敗"
},
"app.skins.section.builders-and-biomes": {
"message": "建造者&生態域"
},
"app.skins.section.chase-the-skies": {
"message": "追逐天空"
},
"app.skins.section.default-skins": {
"message": "預設皮膚"
"message": "預設外觀"
},
"app.skins.section.minecon-earth-2017": {
"message": "MINECON Earth 2017"
},
"app.skins.section.modrinth": {
"message": "Modrinth"
},
"app.skins.section.modrinth-pride": {
"message": "Modrinth 驕傲"
},
"app.skins.section.modrinth-pride.tooltip": {
"message": "這些外觀是你在驕傲月期間捐款給 Modrinth 驕傲月籌款活動所獲得的。"
},
"app.skins.section.mounts-of-mayhem": {
"message": "群騎紛爭"
},
"app.skins.section.saved-skins": {
"message": "皮膚已儲存"
"message": "已儲存外觀"
},
"app.skins.section.striding-hero": {
"message": "Striding Hero"
},
"app.skins.section.the-copper-age": {
"message": "銅器時代"
},
"app.skins.section.the-garden-awakens": {
"message": "蒼園覺醒"
},
"app.skins.section.tiny-takeover": {
"message": "小鬼當家"
},
"app.skins.sign-in.button": {
"message": "登入"
},
"app.skins.sign-in.description": {
"message": "請登入你的 Minecraft 帳號以使用 Modrinth App 的外觀管理功能。"
},
"app.skins.sign-in.rinthbot-alt": {
"message": "興奮的 Modrinth 機器人"
},
"app.skins.sign-in.title": {
"message": "請登入"
},
"app.skins.title": {
"message": "皮膚選擇"
"message": "外觀選擇"
},
"app.update-popup.body.download-complete": {
"message": "Modrinth App v{version} 已完成下載!立即重新載入以更新,或在關閉 Modrinth App 時自動更新。"
@@ -423,7 +504,7 @@
"message": "下載完成"
},
"app.update-popup.reload": {
"message": "重新載入"
"message": "重新載入即可更新"
},
"app.update-popup.title": {
"message": "有可用的更新"
@@ -620,6 +701,30 @@
"instance.settings.tabs.general.name": {
"message": "名稱"
},
"instance.settings.tabs.general.update-channel": {
"message": "更新通道"
},
"instance.settings.tabs.general.update-channel.alpha": {
"message": "Alpha 版"
},
"instance.settings.tabs.general.update-channel.alpha.description": {
"message": "相容的正式版、Beta 版和 Alpha 版本都會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.beta": {
"message": "Beta 版"
},
"instance.settings.tabs.general.update-channel.beta.description": {
"message": "相容的正式版和 Beta 版本都會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.release": {
"message": "正式版"
},
"instance.settings.tabs.general.update-channel.release.description": {
"message": "僅有正式版會顯示在可用更新中。"
},
"instance.settings.tabs.general.update-channel.select": {
"message": "選擇更新通道"
},
"instance.settings.tabs.hooks": {
"message": "啟動掛勾"
},
+24 -6
View File
@@ -90,6 +90,7 @@ import {
useVIntl,
versionChangesGameVersion,
} from '@modrinth/ui'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { open } from '@tauri-apps/plugin-dialog'
import { openUrl } from '@tauri-apps/plugin-opener'
@@ -153,6 +154,7 @@ const { formatMessage } = useVIntl()
const { handleError, addNotification } = injectNotificationManager()
const { installingItems } = injectContentInstall()
const router = useRouter()
const queryClient = useQueryClient()
const debug = useDebugLogger('Mods:ContentUpdate')
const props = defineProps<{
@@ -212,6 +214,13 @@ const contentUpdaterModal = ref<InstanceType<typeof ContentUpdaterModal> | null>
const modpackContentModal = ref<InstanceType<typeof ModpackContentModal> | null>()
const modpackUpdateConfirmModal = ref<InstanceType<typeof ConfirmModpackUpdateModal> | null>()
const modpackContentQueryKey = computed(() => ['linkedModpackContent', props.instance.path])
const modpackContentQuery = useQuery({
queryKey: modpackContentQueryKey,
queryFn: () => get_linked_modpack_content(props.instance.path),
enabled: computed(() => !!props.instance?.path && !!props.instance?.linked_data),
})
// TODO: Extract content operation and updater modal state into composables; this page currently owns file mutations, dependency installs, busy flags, and version selection flow.
const updatingProject = ref<ContentItem | null>(null)
const updatingProjectVersions = ref<Labrinth.Versions.v2.Version[]>([])
@@ -740,13 +749,19 @@ async function handleModpackContentBulkToggle(items: ContentItem[]) {
async function handleModpackContent() {
if (!props.instance?.path) return
if (modpackContentQuery.data.value !== undefined) {
modpackContentModal.value?.show(modpackContentQuery.data.value)
return
}
modpackContentModal.value?.showLoading()
const contentItems = await get_linked_modpack_content(props.instance.path).catch(handleError)
const { data, error } = await modpackContentQuery.refetch()
if (contentItems) {
modpackContentModal.value?.show(contentItems)
if (data !== undefined) {
modpackContentModal.value?.show(data)
} else {
if (error) handleError(error)
modpackContentModal.value?.hide()
}
}
@@ -754,9 +769,12 @@ async function handleModpackContent() {
async function refreshModpackContentItems(cacheBehaviour?: CacheBehaviour) {
if (!props.instance?.path) return
const contentItems = await get_linked_modpack_content(props.instance.path, cacheBehaviour).catch(
handleError,
)
const contentItems = await queryClient
.fetchQuery({
queryKey: modpackContentQueryKey.value,
queryFn: () => get_linked_modpack_content(props.instance.path, cacheBehaviour),
})
.catch(handleError)
if (contentItems) {
modpackContentModal.value?.setItems(contentItems)
+1 -1
View File
@@ -113,7 +113,7 @@ pub async fn login<R: Runtime>(
},
)?),
)
.title("Sign into Modrinth")
.title("Sign into AstralRinth")
.always_on_top(true)
.center()
.build()?;
-2
View File
@@ -35,13 +35,11 @@ pub async fn init_update_launcher(
download_url: &str,
filename: &str,
os_type: &str,
auto_update_supported: bool,
) -> Result<()> {
let _ = utils::init_update_launcher(
download_url,
filename,
os_type,
auto_update_supported,
)
.await;
Ok(())
+1 -1
View File
@@ -381,7 +381,7 @@ fn main() {
DialogBuilder::message()
.set_level(MessageLevel::Error)
.set_title("Initialization error")
.set_text("Your Microsoft Edge WebView2 installation is corrupt.\n\nMicrosoft Edge WebView2 is required to run Modrinth App.\n\nLearn how to repair it at https://support.modrinth.com/en/articles/8797765-corrupted-microsoft-edge-webview2-installation")
.set_text("Your Microsoft Edge WebView2 installation is corrupt.\n\nMicrosoft Edge WebView2 is required to run AstralRinth App.\n\nLearn how to repair it at https://support.modrinth.com/en/articles/8797765-corrupted-microsoft-edge-webview2-installation")
.alert()
.show()
.unwrap();
+1 -1
View File
@@ -63,7 +63,7 @@
]
},
"productName": "AstralRinth App",
"version": "0.14.702",
"version": "0.14.801",
"mainBinaryName": "AstralRinth App",
"identifier": "AstralRinthApp",
"plugins": {
@@ -89,6 +89,8 @@ import {
} from '@modrinth/ui'
import { ref } from 'vue'
import { generateUrlSlug } from '~/utils/slugs'
import CreateLimitAlert from './CreateLimitAlert.vue'
const router = useNativeRouter()
@@ -148,7 +150,7 @@ async function createOrganization(): Promise<void> {
const value = {
name: name.value.trim(),
description: description.value.trim(),
slug: slug.value.trim().replace(/ +/g, ''),
slug: slug.value.trim(),
}
const result: any = await useBaseFetch('organization', {
@@ -183,12 +185,7 @@ function hide(): void {
function updateSlug(): void {
if (!manualSlug.value) {
slug.value = name.value
.trim()
.toLowerCase()
.replaceAll(' ', '-')
.replaceAll(/[^a-zA-Z0-9!@$()`.+,_"-]/g, '')
.replaceAll(/--+/gm, '-')
slug.value = generateUrlSlug(name.value)
}
}
@@ -145,6 +145,8 @@ import {
} from '@modrinth/ui'
import { computed, defineAsyncComponent, h } from 'vue'
import { generateUrlSlug } from '~/utils/slugs'
import CreateLimitAlert from './CreateLimitAlert.vue'
type ProjectTypes = 'server' | 'project'
@@ -461,12 +463,7 @@ async function show(event?: MouseEvent, options?: ShowOptions) {
function updatedName() {
if (!manualSlug.value) {
slug.value = name.value
.trim()
.toLowerCase()
.replaceAll(' ', '-')
.replaceAll(/[^a-zA-Z0-9!@$()`.+,_"-]/g, '')
.replaceAll(/--+/gm, '-')
slug.value = generateUrlSlug(name.value)
}
}
</script>
@@ -8,6 +8,105 @@
"admin.billing.error.not-found": {
"message": "المستخدم غير موجود"
},
"analytics.action.add": {
"message": "إضافة"
},
"analytics.action.cancel": {
"message": "إلغاء"
},
"analytics.action.refresh": {
"message": "تحديث"
},
"analytics.action.reset": {
"message": "إعادة ضبط"
},
"analytics.breakdown.country": {
"message": "الدولة"
},
"analytics.breakdown.download-reason": {
"message": "سبب التنزيل"
},
"analytics.breakdown.download-source": {
"message": "مصدر التنزيل"
},
"analytics.breakdown.game-version": {
"message": "إصدار اللُعبة"
},
"analytics.breakdown.project": {
"message": "المشروع"
},
"analytics.breakdown.project-status": {
"message": "حالة المشروع"
},
"analytics.breakdown.project-version": {
"message": "إصدار المشروع"
},
"analytics.chart.action.show-all": {
"message": "أظهار الكل"
},
"analytics.chart.axis.playtime-hours": {
"message": "{hours} ساعة"
},
"analytics.chart.controls.display": {
"message": "عرض"
},
"analytics.chart.tooltip.total": {
"message": "مجموع"
},
"analytics.downloads.suffix": {
"message": "التنزيلات"
},
"analytics.filter.game-version-type.all": {
"message": "الكل"
},
"analytics.group-by.week": {
"message": "الأسبوع"
},
"analytics.group-by.year": {
"message": "السنة"
},
"analytics.loading.fetching-results": {
"message": "جلب البيانات..."
},
"analytics.options.loading": {
"message": "تحميل..."
},
"analytics.project-event.project-approved": {
"message": "المشروع قُبل"
},
"analytics.project-event.project-private": {
"message": "وضع المشروع خاص"
},
"analytics.project-event.project-status-changed": {
"message": "تم تغيير حالة المشروع"
},
"analytics.project-status.draft": {
"message": "مسودة"
},
"analytics.project-status.private": {
"message": "خاص"
},
"analytics.project-status.rejected": {
"message": "رُفض المشروع"
},
"analytics.project.your": {
"message": "مشاريعك"
},
"analytics.query.label.project": {
"message": "المشروع:"
},
"analytics.stat.downloads": {
"message": "التنزيلات"
},
"analytics.stat.playtime": {
"message": "وقت اللعب"
},
"analytics.stat.playtime-hours": {
"message": "{hours} ساعة"
},
"analytics.table.search.placeholder": {
"message": "بحث..."
},
"app-marketing.download.description": {
"message": "تطبيقنا لسطح المكتب متاح على جميع المنصات، اختر الإصدار الذي تريده."
},
@@ -71,6 +71,9 @@
"analytics.chart.controls.annotations": {
"message": "Anotace"
},
"analytics.chart.controls.button": {
"message": "Ovládání"
},
"app-marketing.download.description": {
"message": "Naše aplikace je dostupná pro všechny počítačové platformy, vyberte si požadovanou verzi."
},
+33 -3
View File
@@ -350,6 +350,12 @@
"analytics.project.select": {
"message": "Projekte auswählen"
},
"analytics.project.user": {
"message": "Projekte von {username}"
},
"analytics.project.your": {
"message": "Deine Projekte"
},
"analytics.query.filter.add": {
"message": "Filter hinzufügen"
},
@@ -1443,7 +1449,7 @@
"message": "Unverifizierte E-Mail"
},
"dashboard.creator-withdraw-modal.tremendous-details.unverified-email-message": {
"message": "Die Zustellungs-E-Mail die du eingegeben hast ist nicht mit deinem Modrinth-Konto verbunden. Belohnungen, welche an eine falsche E-Mail-Adresse gesendet werden können von Modrinth nicht wiederhergestellt werden."
"message": "Die Zustellungs-E-Mail, die du eingegeben hast, ist nicht mit deinem Modrinth-Konto verbunden. Belohnungen, welche an eine falsche E-Mail-Adresse gesendet werden, können von Modrinth nicht wiederhergestellt werden."
},
"dashboard.creator-withdraw-modal.tremendous-details.usd-paypal-warning-header": {
"message": "Weniger Gebühren verfügbar"
@@ -1460,6 +1466,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Du hast dein Auszahlungslimit von <b>{withdrawLimit}</b> aufgebraucht. Du musst ein Steuerformular ausfüllen, um merh abzuheben."
},
"dashboard.discord-roles.banner.body": {
"message": "Du bist für {roles} qualifiziert. Verknüpfe dein Discord-Konto über Modrinth, dann synchronisieren wir diese automatisch."
},
"dashboard.discord-roles.banner.cta": {
"message": "Discord verbinden"
},
"dashboard.discord-roles.banner.title": {
"message": "Hol dir deine Discord-Rollen"
},
"dashboard.discord-roles.role.big-creator": {
"message": "1M+ Downloads"
},
"dashboard.discord-roles.role.creator": {
"message": "Ersteller"
},
"dashboard.discord-roles.role.pride": {
"message": "Pride 2026"
},
"dashboard.head-title": {
"message": "Dashboard"
},
@@ -1512,7 +1536,7 @@
"message": "Benachrichtigungen werden geladen..."
},
"dashboard.projects.bulk-edit-hint": {
"message": "Du kannst mehrere Projekte gleichzeitig bearbeiten, indem du sie unden auswählst."
"message": "Du kannst mehrere Projekte gleichzeitig bearbeiten, indem du sie unten auswählst."
},
"dashboard.projects.bulk-edit.server-disabled": {
"message": "Serverprojekte understützen Massenbearbeitung nicht"
@@ -2291,6 +2315,9 @@
"landing.subheading": {
"message": "Entdecke, Spiele und teile Minecraft-Inhalte durch unsere quelloffene Platform, welche für die Community entwickelt wird."
},
"layout.action.analytics-events": {
"message": "Analytics-Events"
},
"layout.action.change-theme": {
"message": "Farbschema ändern"
},
@@ -2846,6 +2873,9 @@
"profile.bio.fallback.user": {
"message": "Ein Modrinth Benutzer."
},
"profile.button.analytics": {
"message": "Benutzeranalysen anzeigen"
},
"profile.button.billing": {
"message": "Nutzer Zahlungen verwalten"
},
@@ -3138,7 +3168,7 @@
"message": "Platform: {platform}"
},
"project.download.platform-error": {
"message": "Fehler: Keine Platform gefunden"
"message": "Fehler: Keine Plattform gefunden"
},
"project.download.platform-tooltip": {
"message": "{title} ist nur für {platform} verfügbar"
+34 -1
View File
@@ -216,7 +216,7 @@
"message": "Keine Projekte für die Analyse verfügbar"
},
"analytics.empty.select-project": {
"message": "Wählen mindestens ein Projekt aus, um Daten anzuzeigen"
"message": "Wähle mindestens ein Projekt aus, um Daten anzuzeigen"
},
"analytics.filter.game-version-type": {
"message": "Art der Spielversion"
@@ -350,6 +350,12 @@
"analytics.project.select": {
"message": "Projekte auswählen"
},
"analytics.project.user": {
"message": "Projekte von {username}"
},
"analytics.project.your": {
"message": "Deine Projekte"
},
"analytics.query.filter.add": {
"message": "Filter hinzufügen"
},
@@ -1460,6 +1466,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Du hast dein Auszahlungslimit von <b>{withdrawLimit}</b> aufgebraucht. Du musst ein Steuerformular ausfüllen, um merh abzuheben."
},
"dashboard.discord-roles.banner.body": {
"message": "Du bist für {roles} qualifiziert. Verknüpfe dein Discord-Konto über Modrinth, dann synchronisieren wir diese automatisch."
},
"dashboard.discord-roles.banner.cta": {
"message": "Discord verbinden"
},
"dashboard.discord-roles.banner.title": {
"message": "Hol dir deine Discord-Rollen"
},
"dashboard.discord-roles.role.big-creator": {
"message": "1M+ Downloads"
},
"dashboard.discord-roles.role.creator": {
"message": "Ersteller"
},
"dashboard.discord-roles.role.pride": {
"message": "Pride 2026"
},
"dashboard.head-title": {
"message": "Dashboard"
},
@@ -2291,6 +2315,9 @@
"landing.subheading": {
"message": "Entdecke, spiele und teile Minecraft-Inhalte über unsere Open-Source-Plattform, die für die Community entwickelt wurde."
},
"layout.action.analytics-events": {
"message": "Analytics-Events"
},
"layout.action.change-theme": {
"message": "Farbschema ändern"
},
@@ -2846,6 +2873,9 @@
"profile.bio.fallback.user": {
"message": "Ein Modrinth-Benutzer."
},
"profile.button.analytics": {
"message": "Benutzeranalysen anzeigen"
},
"profile.button.billing": {
"message": "Benutzerabrechnungen verwalten"
},
@@ -3392,6 +3422,9 @@
"project.stats.downloads-label": {
"message": "{count, plural, one {Download} other {Downloads}}"
},
"project.stats.followers-label": {
"message": "{count, plural, one {Follower} other {Follower}}"
},
"project.status.archived.message": {
"message": "{title} wurde archiviert. {title} wird keine weiteren Updates erhalten, es sei denn, der Autor entscheidet sich das Projekt zu dearchivieren."
},
+46 -16
View File
@@ -15,10 +15,10 @@
"message": "Cancelar"
},
"analytics.action.refresh": {
"message": "Refrescar"
"message": "Recargar"
},
"analytics.action.reset": {
"message": "Reiniciar"
"message": "Restablecer"
},
"analytics.breakdown.country": {
"message": "País"
@@ -75,16 +75,16 @@
"message": "Anotaciones"
},
"analytics.chart.controls.aria": {
"message": "Control de gráficos de las estadísticas, {activeCount}"
"message": "Controles de gráficos de las estadísticas, {activeCount}"
},
"analytics.chart.controls.button": {
"message": "Controles"
},
"analytics.chart.controls.dialog-aria": {
"message": "Controles de gráficos de Estadísticas"
"message": "Controles de gráficos de estadísticas"
},
"analytics.chart.controls.display": {
"message": "Visualizador"
"message": "Mostrar"
},
"analytics.chart.controls.modrinth-events": {
"message": "Eventos de Modrinth"
@@ -102,7 +102,7 @@
"message": "Eventos del proyecto"
},
"analytics.chart.controls.ratio": {
"message": "Intervalo"
"message": "Proporción"
},
"analytics.chart.empty.select-table-items": {
"message": "Selecciona ítems de la tabla inferior para ver tus datos."
@@ -165,7 +165,7 @@
"message": "(per. prev.)"
},
"analytics.chart.tooltip.show-entry": {
"message": "Mostrar {name} en el gráfico"
"message": "Mostrar {name} en la gráfica"
},
"analytics.chart.tooltip.total": {
"message": "Total"
@@ -222,7 +222,7 @@
"message": "Todo"
},
"analytics.filter.game-version-type.release": {
"message": "Lanzamiento"
"message": "Estable"
},
"analytics.filter.search.countries": {
"message": "Buscar países..."
@@ -342,11 +342,17 @@
"message": "{count, plural, one {# proyecto} other {# proyectos}}"
},
"analytics.project.icon-alt": {
"message": "{name} Icono"
"message": "Icono de {name}"
},
"analytics.project.select": {
"message": "Seleccionar proyectos"
},
"analytics.project.user": {
"message": "Proyectos de {username}"
},
"analytics.project.your": {
"message": "Tus proyectos"
},
"analytics.query.filter.add": {
"message": "Añadir filtro"
},
@@ -528,7 +534,7 @@
"message": "Sigue proyectos"
},
"app-marketing.features.importing.description": {
"message": "Importa todos tus perfiles favoritos desde el launcher que usabas antes y ¡empieza a usar la Modrinth App en segundos!"
"message": "Importa todos tus perfiles favoritos desde el launcher que usabas antes ¡y empieza a usar la Modrinth App en segundos!"
},
"app-marketing.features.importing.gdlauncher-alt": {
"message": "GDLauncher"
@@ -582,7 +588,7 @@
"message": "% CPU"
},
"app-marketing.features.performance.description": {
"message": "Modrinth app tiene un mejor rendimiento que muchos de los gestores de mods más populares, ¡usando solo 150 MB de RAM!"
"message": "Modrinth App tiene un mejor rendimiento que muchos de los gestores de mods más populares, ¡usando solo 150 MB de RAM!"
},
"app-marketing.features.performance.discord": {
"message": "Discord"
@@ -951,13 +957,13 @@
"message": "Error al enviar el mensaje"
},
"conversation-thread.reply-editor.placeholder.reply": {
"message": "Respuesta al hilo..."
"message": "Responder al hilo..."
},
"conversation-thread.reply-editor.placeholder.send": {
"message": "Envía un mensaje..."
"message": "Enviar un mensaje..."
},
"conversation-thread.reply-modal.confirmation.description": {
"message": "Confirmación de que moderadores no monitorean esto activamente"
"message": "Confirmo que los moderadores no monitorean esto activamente"
},
"conversation-thread.reply-modal.confirmation.label": {
"message": "Comprendo que los moderadores no monitorean este hilo activamente."
@@ -1245,7 +1251,7 @@
"message": "Llena el formulario fiscal"
},
"dashboard.creator-withdraw-modal.continue-with-limit": {
"message": "Continuar con el límite"
"message": "Continuar con límites"
},
"dashboard.creator-withdraw-modal.details-label": {
"message": "Detalles"
@@ -1455,7 +1461,25 @@
"message": "Límite de retiro"
},
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Has agotado tu límite de retiro <b>{withdrawLimit}</b>. Debes completar un formulario de impuestos para retirar más."
"message": "Has agotado tu límite de retiro de <b>{withdrawLimit}</b>. Debes completar un formulario de impuestos para retirar más."
},
"dashboard.discord-roles.banner.body": {
"message": "Cumples los requisitos para los roles {roles}. Vincula tu cuenta de Discord a través de Modrinth y los sincronizaremos automáticamente."
},
"dashboard.discord-roles.banner.cta": {
"message": "Vincular Discord"
},
"dashboard.discord-roles.banner.title": {
"message": "Reclama tus roles de Discord"
},
"dashboard.discord-roles.role.big-creator": {
"message": "+1 M de descargas"
},
"dashboard.discord-roles.role.creator": {
"message": "Creador"
},
"dashboard.discord-roles.role.pride": {
"message": "Orgullo 2026"
},
"dashboard.head-title": {
"message": "Panel de control"
@@ -2288,6 +2312,9 @@
"landing.subheading": {
"message": "Descubre, juega y comparte contenido de Minecraft a través de nuestra plataforma de código abierto creada para la comunidad."
},
"layout.action.analytics-events": {
"message": "Eventos de analítica"
},
"layout.action.change-theme": {
"message": "Cambiar tema"
},
@@ -2843,6 +2870,9 @@
"profile.bio.fallback.user": {
"message": "Un usuario de Modrinth."
},
"profile.button.analytics": {
"message": "Ver estadísticas de usuario"
},
"profile.button.billing": {
"message": "Gestionar la facturación de usuario"
},
+33 -3
View File
@@ -36,7 +36,7 @@
"message": "Desglose"
},
"analytics.breakdown.loader": {
"message": "Cargador"
"message": "Loader"
},
"analytics.breakdown.monetization": {
"message": "Monetización"
@@ -347,6 +347,12 @@
"analytics.project.select": {
"message": "Seleccionar proyectos"
},
"analytics.project.user": {
"message": "Proyectos de {username}"
},
"analytics.project.your": {
"message": "Tus proyectos"
},
"analytics.query.filter.add": {
"message": "Añadir filtro"
},
@@ -1454,6 +1460,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Has agotado tu límite de retiro de <b>{withdrawLimit}</b>. Debes completar un formulario de impuestos para retirar más."
},
"dashboard.discord-roles.banner.body": {
"message": "Cumples los requisitos para los roles {roles}. Vincula tu cuenta de Discord a través de Modrinth y los sincronizaremos automáticamente."
},
"dashboard.discord-roles.banner.cta": {
"message": "Vincular Discord"
},
"dashboard.discord-roles.banner.title": {
"message": "Reclama tus roles de Discord"
},
"dashboard.discord-roles.role.big-creator": {
"message": "+1 M de descargas"
},
"dashboard.discord-roles.role.creator": {
"message": "Creador"
},
"dashboard.discord-roles.role.pride": {
"message": "Orgullo 2026"
},
"dashboard.head-title": {
"message": "Panel de control"
},
@@ -1632,7 +1656,7 @@
"message": "Haz clic para aprender como Modrinth gestiona tus ingresos."
},
"dashboard.revenue.estimated-with-date": {
"message": "{date} estimada"
"message": "Fecha estimada: {date}"
},
"dashboard.revenue.processing": {
"message": "Procesando"
@@ -2282,6 +2306,9 @@
"landing.subheading": {
"message": "Descubre, juega y comparte contenido de Minecraft a través de nuestra plataforma de código abierto creada para la comunidad."
},
"layout.action.analytics-events": {
"message": "Eventos de analítica"
},
"layout.action.change-theme": {
"message": "Cambiar tema"
},
@@ -2837,6 +2864,9 @@
"profile.bio.fallback.user": {
"message": "Usuario de Modrinth."
},
"profile.button.analytics": {
"message": "Ver analíticas de usuario"
},
"profile.button.billing": {
"message": "Gestionar la facturación de los usuarios"
},
@@ -3765,7 +3795,7 @@
"message": "La versión del juego es proporcionada por el servidor"
},
"search.filter.locked.server-loader.title": {
"message": "El cargador es proporcionado por el servidor"
"message": "Loader proporcionado por el servidor"
},
"search.filter.locked.server.sync": {
"message": "Sincronizar con el servidor"
@@ -80,6 +80,9 @@
"analytics.chart.controls.ratio": {
"message": "Ratio"
},
"analytics.chart.tooltip.pinned-aria": {
"message": "Naka-pin"
},
"analytics.chart.tooltip.total": {
"message": "Kabuoan"
},
@@ -92,6 +95,9 @@
"analytics.chart.view.line": {
"message": "Line"
},
"analytics.download-reason.update": {
"message": "Pag-update"
},
"analytics.filter.game-version-type.all": {
"message": "Lahat"
},
@@ -119,6 +125,9 @@
"analytics.project-status.draft": {
"message": "Burador"
},
"analytics.project-status.other": {
"message": "Iba pa"
},
"analytics.stat.unavailable": {
"message": "N/A"
},
@@ -128,6 +137,9 @@
"analytics.value.none": {
"message": "Wala"
},
"analytics.value.other": {
"message": "Iba pa"
},
"app-marketing.download.description": {
"message": "Magagamit ang aming desktop app sa ibat-ibang plataporma, piliin ang iyong gugustohing bersiyon."
},
@@ -524,6 +536,9 @@
"collection.title": {
"message": "{name} - Koleksiyon"
},
"conversation-thread.action.reject": {
"message": "Tanggihan"
},
"conversation-thread.action.reply": {
"message": "Tugunan"
},
@@ -1706,6 +1721,9 @@
"layout.banner.build-fail.description": {
"message": "Itong pinakalat na frontend ng Modrinth ay bigong makabuo ng estado galing sa API. Maaaring dahil ito sa isang kawalan o kamalian sa kumpigurasyon. Muling magpatayo kapag magagamit na muli ang API. Mga code ng kamalian: {errors}; Ang kasalukuyang URL ng API ay: {url}"
},
"layout.banner.build-fail.ignore": {
"message": "Baliwalain"
},
"layout.banner.build-fail.title": {
"message": "Error sa pagbubuo ng estado galing sa API habang nagpapatayo."
},
+31 -1
View File
@@ -344,6 +344,12 @@
"analytics.project.select": {
"message": "Sélectionner des projets"
},
"analytics.project.user": {
"message": "Projets de {username}"
},
"analytics.project.your": {
"message": "Vos projets"
},
"analytics.query.filter.add": {
"message": "Ajouter un filtre"
},
@@ -1454,6 +1460,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Vous avez atteint votre limite de retrait de <b>{withdrawLimit}</b>. Vous devez remplir un formulaire fiscal pour effectuer un retrait supplémentaire."
},
"dashboard.discord-roles.banner.body": {
"message": "Vous êtes éligible aux rôles {roles}. Associez votre compte Discord via Modrinth et nous les synchroniserons automatiquement."
},
"dashboard.discord-roles.banner.cta": {
"message": "Associer Discord"
},
"dashboard.discord-roles.banner.title": {
"message": "Récupérez vos rôles Discord"
},
"dashboard.discord-roles.role.big-creator": {
"message": "+1 M de téléchargements"
},
"dashboard.discord-roles.role.creator": {
"message": "Créateur"
},
"dashboard.discord-roles.role.pride": {
"message": "Pride 2026"
},
"dashboard.head-title": {
"message": "Tableau de bord"
},
@@ -1488,7 +1512,7 @@
"message": "Marquer tout comme lu"
},
"dashboard.overview.notifications.button.view-history": {
"message": "Voire l'historique"
"message": "Voir l'historique"
},
"dashboard.overview.notifications.empty.no-unread": {
"message": "Vous n'avez pas de notifications non lues."
@@ -2285,6 +2309,9 @@
"landing.subheading": {
"message": "Découvrez, jouez et partagez du contenu Minecraft grâce à notre plateforme open-source conçue pour la communauté."
},
"layout.action.analytics-events": {
"message": "Événements d'analyse"
},
"layout.action.change-theme": {
"message": "Changer le thème"
},
@@ -2840,6 +2867,9 @@
"profile.bio.fallback.user": {
"message": "Un utilisateur Modrinth."
},
"profile.button.analytics": {
"message": "Voir les analyses de l'utilisateur"
},
"profile.button.billing": {
"message": "Gérer la facturation de lutilisateur"
},
+61 -1
View File
@@ -134,6 +134,12 @@
"analytics.chart.render-limit.header": {
"message": "Megjelenítse a grafikonon mind a {count} sort?"
},
"analytics.chart.table-selection.limited": {
"message": "Mutatás {limit} {itemType, select, project {{limit, plural, one {project} other {projects}}} country {{limit, plural, one {country} other {countries}}} monetization {{limit, plural, one {monetization value} other {monetization values}}} downloadSource {{limit, plural, one {download source} other {download sources}}} downloadReason {{limit, plural, one {download reason} other {download reasons}}} projectVersion {{limit, plural, one {project version} other {project versions}}} loader {{limit, plural, one {loader} other {loaders}}} gameVersion {{limit, plural, one {game version} other {game versions}}} other {{limit, plural, one {item} other {items}}}} from table"
},
"analytics.chart.table-selection.top": {
"message": "Mutatás top {count} {itemType, select, project {{count, plural, one {project} other {projects}}} country {{count, plural, one {country} other {countries}}} monetization {{count, plural, one {monetization value} other {monetization values}}} downloadSource {{count, plural, one {download source} other {download sources}}} downloadReason {{count, plural, one {download reason} other {download reasons}}} projectVersion {{count, plural, one {project version} other {project versions}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {game version} other {game versions}}} other {{count, plural, one {item} other {items}}}} from table"
},
"analytics.chart.tooltip.hide-entry": {
"message": "{name} elrejtése a grafikonon"
},
@@ -167,6 +173,9 @@
"analytics.download-reason.modpack": {
"message": "Modcsomag"
},
"analytics.download-reason.standalone": {
"message": "Önálló"
},
"analytics.download-reason.update": {
"message": "Frissítés"
},
@@ -191,6 +200,9 @@
"analytics.empty.no-projects-for-analytics": {
"message": "Nincs elérhető projekt a statisztikához"
},
"analytics.empty.select-project": {
"message": "Válassz ki legalább egy projektet hogy megnézhesd az adatait"
},
"analytics.filter.game-version-type": {
"message": "Játékverzió típusa"
},
@@ -317,6 +329,21 @@
"analytics.project.icon-alt": {
"message": "{name} ikon"
},
"analytics.project.select": {
"message": "Projektek kiválasztása"
},
"analytics.project.user": {
"message": "{username} projektjei"
},
"analytics.project.your": {
"message": "A te projektjeid"
},
"analytics.query.filter.add": {
"message": "Szűrő hozzáadása"
},
"analytics.query.label.breakdown": {
"message": "Lebontás:"
},
"analytics.query.label.grouped-by": {
"message": "Csoportosítás"
},
@@ -335,15 +362,45 @@
"analytics.stat.monetization-banner.learn-more": {
"message": "Tudj meg többet"
},
"analytics.stat.monetization-banner.title": {
"message": "Hogyan működik a monetizáció?"
},
"analytics.stat.playtime": {
"message": "Játékidő"
},
"analytics.stat.playtime-hours": {
"message": "{hours} óra"
},
"analytics.stat.revenue": {
"message": "Bevétel"
},
"analytics.stat.revenue-value": {
"message": "${value}"
},
"analytics.stat.unavailable": {
"message": "N/A"
},
"analytics.stat.views": {
"message": "Megtekintések"
},
"analytics.table.csv.date-range": {
"message": "{start}-tól {end}-ig"
},
"analytics.table.csv.header.playtime-seconds": {
"message": "Játékidő (másodpercben)"
},
"analytics.table.duration.days": {
"message": "{count, plural, one {# day} other {# days}}"
},
"analytics.table.duration.hours": {
"message": "{count, plural, one {# hour} other {# hours}}"
},
"analytics.table.duration.minutes": {
"message": "{count, plural, one {# minute} other {# minutes}}"
},
"analytics.table.export-csv": {
"message": "CSV exportálása"
},
"analytics.table.search.placeholder": {
"message": "Keresés..."
},
@@ -524,6 +581,9 @@
"app-marketing.hero.download-modrinth-app": {
"message": "Modrinth App letöltése"
},
"app-marketing.hero.download-modrinth-app-for-os": {
"message": "Modrinth App letöltése {os} rendszerre"
},
"app-marketing.hero.minecraft-screenshot-alt": {
"message": "A Cobblemon profil főmenüjének képernyőképe."
},
@@ -1011,7 +1071,7 @@
"message": "Gyűjteményeid"
},
"dashboard.collections.placeholder.search": {
"message": "Gyűjtemények keresése"
"message": "Gyűjtemények keresése..."
},
"dashboard.collections.sort.name-ascending": {
"message": "Név (A-Z)"
@@ -122,6 +122,99 @@
"analytics.chart.tooltip.hide-entry": {
"message": "Sembunyikan {name} di grafik"
},
"analytics.chart.tooltip.pinned-aria": {
"message": "Disematkan"
},
"analytics.chart.tooltip.previous-period-short": {
"message": "(sblm.)"
},
"analytics.chart.tooltip.show-entry": {
"message": "Tampilkan {name} di grafik"
},
"analytics.chart.tooltip.total": {
"message": "Jumlah"
},
"analytics.chart.view.area": {
"message": "Luas"
},
"analytics.chart.view.bar": {
"message": "Batang"
},
"analytics.chart.view.line": {
"message": "Garis"
},
"analytics.download-reason.dependency": {
"message": "Dependensi"
},
"analytics.download-reason.modpack": {
"message": "Paket Mod"
},
"analytics.download-reason.standalone": {
"message": "Mod Mandiri"
},
"analytics.download-reason.update": {
"message": "Pembaruan"
},
"analytics.download-source.app": {
"message": "Modrinth App"
},
"analytics.download-source.website": {
"message": "Situs Web Modrinth"
},
"analytics.downloads.suffix": {
"message": "unduhan"
},
"analytics.empty.no-data": {
"message": "Data tidak tersedia"
},
"analytics.empty.no-data-for-analytics": {
"message": "Tidak ada data untuk dianalisis"
},
"analytics.empty.no-projects": {
"message": "Proyek tidak tersedia"
},
"analytics.empty.no-projects-for-analytics": {
"message": "Tidak ada proyek untuk dianalisis"
},
"analytics.empty.select-project": {
"message": "Pilih setidaknya satu proyek untuk melihat data"
},
"analytics.filter.game-version-type": {
"message": "Jenis versi permainan"
},
"analytics.filter.game-version-type.all": {
"message": "Semua"
},
"analytics.filter.game-version-type.release": {
"message": "Rilisan"
},
"analytics.filter.search.countries": {
"message": "Cari negara..."
},
"analytics.filter.search.download-sources": {
"message": "Cari sumber pengunduhan..."
},
"analytics.filter.search.project-versions": {
"message": "Cari versi proyek..."
},
"analytics.filter.search.versions": {
"message": "Cari versi..."
},
"analytics.group-by.1h": {
"message": "1j"
},
"analytics.group-by.6h": {
"message": "6j"
},
"analytics.group-by.date": {
"message": "Tanggal"
},
"analytics.group-by.day": {
"message": "Hari"
},
"analytics.group-by.month": {
"message": "Bulan"
},
"app-marketing.download.description": {
"message": "Aplikasi destop kami tersedia pada semua platform, pilih versi yang Anda inginkan."
},
+67 -40
View File
@@ -33,7 +33,7 @@
"message": "Versione del gioco"
},
"analytics.breakdown.generic": {
"message": "Scomposizione"
"message": "Ripartizione"
},
"analytics.breakdown.loader": {
"message": "Loader"
@@ -42,7 +42,7 @@
"message": "Monetizzazione"
},
"analytics.breakdown.none.selected": {
"message": "Nessuna scomposizione"
"message": "Nessuna ripartizione"
},
"analytics.breakdown.project": {
"message": "Progetto"
@@ -54,7 +54,7 @@
"message": "Versione del progetto"
},
"analytics.breakdown.selected": {
"message": "Scomposizione per {breakdown}"
"message": "Ripartizione per {breakdown}"
},
"analytics.chart.action.show-all": {
"message": "Mostra tutti"
@@ -65,9 +65,6 @@
"analytics.chart.action.show-top-eight": {
"message": "Mostra i primi 8"
},
"analytics.chart.axis.playtime-hours": {
"message": "{hours} ora"
},
"analytics.chart.controls.annotations": {
"message": "Annotazioni"
},
@@ -114,25 +111,25 @@
"message": "Vedi l'annuncio"
},
"analytics.chart.legend.monetization-details.aria": {
"message": "Mostra i dettagli delle analitiche sulla monetizzazione"
"message": "Mostra i dettagli sulla monetizzazione"
},
"analytics.chart.legend.monetization-details.description": {
"message": "Solo le visualizzazioni e i download da Modrinth contano per la monetizzazione e, per gli ultimi, gli utenti devono aver effettuato l'accesso."
"message": "Per la monetizzazione contano solo le visualizzazioni e i download da Modrinth. Per gli ultimi, gli utenti devono aver effettuato l'accesso."
},
"analytics.chart.legend.monetization-details.title": {
"message": "Dettagli monetizzazione"
"message": "Dettagli sulla monetizzazione"
},
"analytics.chart.legend.previous-period-suffix": {
"message": "{name} (Prec.)"
"message": "{name} (prec.)"
},
"analytics.chart.render-limit.description": {
"message": "Mostrare tutte le linee selezionate dalla tabella potrebbe rallentare la pagina."
"message": "Mostrarle tutte potrebbe rallentare la pagina."
},
"analytics.chart.render-limit.header": {
"message": "Mostrare tutte le {count} linee nel grafico?"
},
"analytics.chart.table-selection.all": {
"message": "Mostrando {itemType, select, project {{count, plural, one {il progetto} other {tutti i progetti}}} country {{count, plural, one {il paese} other {tutti i paesi}}} monetization {{count, plural, one {il valore di monetizzazione} other {tutti i valori di monetizzazione}}} downloadSource {{count, plural, one {la fonte} other {tutte le fonti}}} downloadReason {{count, plural, one {il motivo} other {tutti i motivi}}} projectVersion {{count, plural, one {la versione del progetto} other {tutte le versioni del progetto}}} loader {{count, plural, one {il loader} other {tutti i loader}}} gameVersion {{count, plural, one {la versione di gioco} other {tutte le versioni di gioco}}} other {{count, plural, one {l'elemento} other {tutti gli elementi}}}} dalla tabella"
"message": "Mostrando {itemType, select, project {{count, plural, one {l'unico progetto} other {tutti i progetti}}} country {{count, plural, one {l'unico paese} other {tutti i paesi}}} monetization {{count, plural, one {l'unico valore} other {tutti i valori}} di monetizzazione} downloadSource {{count, plural, one {l'unica fonte} other {tutte le fonti}}} downloadReason {{count, plural, one {l'unico motivo} other {tutti i motivi}} di download} projectVersion {{count, plural, one {l'unica versione} other {tutte le versioni}} del progetto} loader {{count, plural, one {l'unico loader} other {tutti i loader}}} gameVersion {{count, plural, one {l'unica versione} other {tutte le versioni}} del gioco} other {{count, plural, one {l'unico elemento} other {tutti gli elementi}}}} dalla tabella"
},
"analytics.chart.table-selection.count": {
"message": "Mostrando {count} {itemType, select, project {{count, plural, one {progetto} other {progetti}}} country {{count, plural, one {paese} other {paesi}}} monetization {{count, plural, one {valore di monetizzazione} other {valori di monetizzazione}}} downloadSource {{count, plural, one {fonte} other {fonti}}} downloadReason {{count, plural, one {motivo} other {motivi}}} projectVersion {{count, plural, one {versione del progetto} other {versioni del progetto}}} loader {loader} gameVersion {{count, plural, one {versione di gioco} other {versioni di gioco}}} other {{count, plural, one {elemento} other {elementi}}}} dalla tabella"
@@ -171,7 +168,7 @@
"message": "Totale"
},
"analytics.chart.view.area": {
"message": "Area"
"message": "Aree"
},
"analytics.chart.view.bar": {
"message": "Barre"
@@ -186,7 +183,7 @@
"message": "Pacchetto di mod"
},
"analytics.download-reason.standalone": {
"message": "Autonomo"
"message": "Indipendente"
},
"analytics.download-reason.update": {
"message": "Aggiornamento"
@@ -204,25 +201,25 @@
"message": "Nessun dato disponibile"
},
"analytics.empty.no-data-for-analytics": {
"message": "Nessun dato disponibile per le analitiche"
"message": "Nessun dato disponibile"
},
"analytics.empty.no-projects": {
"message": "Nessun progetto disponibile"
},
"analytics.empty.no-projects-for-analytics": {
"message": "Nessun progetto disponibile per le analitiche"
"message": "Nessun progetto disponibile"
},
"analytics.empty.select-project": {
"message": "Seleziona almeno un progetto per vederne i dati"
},
"analytics.filter.game-version-type": {
"message": "Tipo di versione di gioco"
"message": "Tipo di versione del gioco"
},
"analytics.filter.game-version-type.all": {
"message": "Tutte"
},
"analytics.filter.game-version-type.release": {
"message": "Stabile"
"message": "Stabili"
},
"analytics.filter.search.countries": {
"message": "Cerca tra i paesi..."
@@ -234,7 +231,7 @@
"message": "Cerca tra le versioni del progetto..."
},
"analytics.filter.search.versions": {
"message": "Cerca tra le versioni..."
"message": "Cerca tra le versioni del gioco..."
},
"analytics.graph.title.downloads": {
"message": "Download nel tempo"
@@ -249,10 +246,10 @@
"message": "Visualizzazioni nel tempo"
},
"analytics.group-by.1h": {
"message": "1o"
"message": "1 ora"
},
"analytics.group-by.6h": {
"message": "6o"
"message": "6 ore"
},
"analytics.group-by.date": {
"message": "Data"
@@ -288,7 +285,7 @@
"message": "Anno"
},
"analytics.loading.fetching-results": {
"message": "Ottenendo i risultati..."
"message": "Recupero dei risultati..."
},
"analytics.options.loading": {
"message": "Caricamento..."
@@ -347,11 +344,17 @@
"analytics.project.select": {
"message": "Seleziona i progetti"
},
"analytics.project.user": {
"message": "Progetti di {username}"
},
"analytics.project.your": {
"message": "I tuoi progetti"
},
"analytics.query.filter.add": {
"message": "Aggiungi filtro"
},
"analytics.query.label.breakdown": {
"message": "Scomposizione:"
"message": "Ripartizione:"
},
"analytics.query.label.grouped-by": {
"message": "Raggruppato per"
@@ -366,7 +369,7 @@
"message": "Download"
},
"analytics.stat.monetization-banner.body": {
"message": "Ai fini della monetizzazione, sono considerate solo le visualizzazioni e i download da Modrinth che superano i controlli antifrode. I download dalla Modrinth App richiedono anche l'accesso dell'utente. Poiché tutti i progetti presentano una percentuale simile di download monetizzati, sommando tutti i download i ricavi non subirebbero variazioni significative."
"message": "Ai fini della monetizzazione, sono considerate solo le visualizzazioni e i download da Modrinth che superano i controlli antifrode. I download dalla Modrinth App richiedono inoltre l'autenticazione dell'utente. Poiché tutti i progetti presentano una percentuale simile di download monetizzati, anche sommando tutti i download i ricavi non subirebbero variazioni significative."
},
"analytics.stat.monetization-banner.learn-more": {
"message": "Scopri di più"
@@ -381,10 +384,10 @@
"message": "{hours} ore"
},
"analytics.stat.previous-period-comparison": {
"message": "rispetto per. prec."
"message": "rispetto al prec."
},
"analytics.stat.previous-period-comparison-short": {
"message": "rispetto prec."
"message": "vs prec."
},
"analytics.stat.revenue": {
"message": "Guadagni"
@@ -393,10 +396,10 @@
"message": "${value}"
},
"analytics.stat.unavailable": {
"message": "Non disponibile"
"message": "N.D."
},
"analytics.stat.unavailable-tooltip": {
"message": "Nessuna statistica trovata"
"message": "Non disponibile per questa ricerca"
},
"analytics.stat.views": {
"message": "Visualizzazioni"
@@ -405,7 +408,7 @@
"message": "{start} al {end}"
},
"analytics.table.csv.filename": {
"message": "Scomposizione analitica per {breakdown} - {dateRange}"
"message": "Modrinth - Analitiche per {breakdown} - {dateRange}"
},
"analytics.table.csv.header.playtime-seconds": {
"message": "Secondi di gioco"
@@ -423,7 +426,7 @@
"message": "{count, plural, one {# minuto} other {# minuti}}"
},
"analytics.table.empty.no-matching-rows": {
"message": "Nessuna riga corrispondete"
"message": "Nessuna riga corrispondente"
},
"analytics.table.export-csv": {
"message": "Esporta CSV"
@@ -441,28 +444,28 @@
"message": "Cerca..."
},
"analytics.threshold.countries-above": {
"message": "Paesi sopra"
"message": "Paesi con oltre"
},
"analytics.threshold.country-downloads-aria": {
"message": "Soglia dei download del paese"
"message": "Soglia dei download per paese"
},
"analytics.threshold.game-version-downloads-aria": {
"message": "Soglia dei download della versione di gioco"
"message": "Soglia dei download per la versione del gioco"
},
"analytics.threshold.game-versions-above": {
"message": "Versioni del gioco sopra"
"message": "Versioni del gioco con oltre"
},
"analytics.threshold.project-downloads-aria": {
"message": "Soglia dei download del progetto"
"message": "Soglia dei download per il progetto"
},
"analytics.threshold.project-version-downloads-aria": {
"message": "Soglia dei download della versione del progetto"
"message": "Soglia dei download per la versione del progetto"
},
"analytics.threshold.project-versions-above": {
"message": "Versioni del progetto sopra"
"message": "Versioni del progetto con oltre"
},
"analytics.threshold.projects-above": {
"message": "Progetti sopra"
"message": "Progetti con oltre"
},
"analytics.title": {
"message": "Analitiche"
@@ -996,7 +999,7 @@
"message": "I file aggiuntivi sono per risorse di supporto come il codice sorgente, non per versioni alternative o varianti."
},
"create.collection.collection-info": {
"message": "La tua nuova raccolta sarà pubblica e verrà creata {count, plural, =0 {senza progetti} one {con un progetto} other {con # progetti}}."
"message": "La nuova raccolta sarà pubblica e verrà creata {count, plural, =0 {senza progetti} one {con # progetto} other {con # progetti}}."
},
"create.collection.create-collection": {
"message": "Crea raccolta"
@@ -1454,6 +1457,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Hai consumato l'intero tuo limite di <b>{withdrawLimit}</b>. Dovrai compilare un modulo fiscale per prelevare di più."
},
"dashboard.discord-roles.banner.body": {
"message": "Puoi rivendicare i seguenti ruoli: {roles}. Collega il tuo account Discord a Modrinth per sincronizzarli."
},
"dashboard.discord-roles.banner.cta": {
"message": "Collega Discord"
},
"dashboard.discord-roles.banner.title": {
"message": "Ottieni dei ruoli su Discord"
},
"dashboard.discord-roles.role.big-creator": {
"message": "1M+ Downloads"
},
"dashboard.discord-roles.role.creator": {
"message": "Creator"
},
"dashboard.discord-roles.role.pride": {
"message": "Pride 2026"
},
"dashboard.head-title": {
"message": "Bacheca"
},
@@ -2276,6 +2297,9 @@
"landing.subheading": {
"message": "Scopri, gioca e condividi contenuti per Minecraft attraverso la nostra piattaforma open source creata per la comunità."
},
"layout.action.analytics-events": {
"message": "Eventi"
},
"layout.action.change-theme": {
"message": "Cambia tema"
},
@@ -2322,7 +2346,7 @@
"message": "Revisione tecnica"
},
"layout.avatar.alt": {
"message": "Il tuo avatar"
"message": "La tua foto profilo"
},
"layout.banner.account-action": {
"message": "Necessaria azione per l'account"
@@ -2831,6 +2855,9 @@
"profile.bio.fallback.user": {
"message": "Utente di Modrinth."
},
"profile.button.analytics": {
"message": "Mostra analitiche"
},
"profile.button.billing": {
"message": "Gestisci pagamento utente"
},
@@ -32,12 +32,18 @@
"analytics.breakdown.game-version": {
"message": "Wersja gry"
},
"analytics.breakdown.generic": {
"message": "Rozkład"
},
"analytics.breakdown.loader": {
"message": "Loader"
},
"analytics.breakdown.monetization": {
"message": "Monetyzacja"
},
"analytics.breakdown.none.selected": {
"message": "Brak rozkładu"
},
"analytics.breakdown.project": {
"message": "Projekt"
},
@@ -47,6 +53,9 @@
"analytics.breakdown.project-version": {
"message": "Wersja projektu"
},
"analytics.breakdown.selected": {
"message": "Rozkład wg.: {breakdown}"
},
"analytics.chart.action.show-all": {
"message": "Pokaż wszystko"
},
@@ -59,6 +68,21 @@
"analytics.chart.axis.playtime-hours": {
"message": "{hours} godz"
},
"analytics.chart.controls.active-count": {
"message": "Aktywnych: {count}"
},
"analytics.chart.controls.annotations": {
"message": "Adnotacje"
},
"analytics.chart.controls.aria": {
"message": "Opcje grafu, {activeCount}"
},
"analytics.chart.controls.button": {
"message": "Opcje"
},
"analytics.chart.controls.dialog-aria": {
"message": "Opcje grafu"
},
"analytics.chart.controls.display": {
"message": "Wyświetlanie"
},
@@ -77,6 +101,12 @@
"analytics.chart.controls.project-events": {
"message": "Wydarzenia projektów"
},
"analytics.chart.controls.ratio": {
"message": "Proporcjonalnie"
},
"analytics.chart.empty.select-table-items": {
"message": "Wybierz coś w tabeli poniżej, by wizualizować dane."
},
"analytics.chart.events.project-title": {
"message": "<project>{projectName}</project>: {title}"
},
@@ -86,6 +116,9 @@
"analytics.chart.legend.monetization-details.description": {
"message": "Tylko wyświetlenia i pobrania z Modrinth liczą się do monetyzacji, gdzie pobrania wymagają, że użytkownik jest zalogowany."
},
"analytics.chart.legend.previous-period-suffix": {
"message": "{name} (poprz.)"
},
"analytics.chart.render-limit.description": {
"message": "Pokazanie wszystkich wybranych linii z tabeli może wpłynąć negatywnie na wydajność strony."
},
@@ -104,6 +137,9 @@
"analytics.chart.tooltip.pinned-aria": {
"message": "Przypięte"
},
"analytics.chart.tooltip.previous-period-short": {
"message": "(poprz.)"
},
"analytics.chart.tooltip.show-entry": {
"message": "Pokaż {name} na wykresie"
},
@@ -215,6 +251,9 @@
"analytics.group-by.year": {
"message": "Rok"
},
"analytics.loading.fetching-results": {
"message": "Pobieranie wyników..."
},
"analytics.options.loading": {
"message": "Ładowanie..."
},
@@ -269,9 +308,18 @@
"analytics.project.select": {
"message": "Wybierz projekty"
},
"analytics.project.user": {
"message": "Projekty {username}"
},
"analytics.project.your": {
"message": "Twoje projekty"
},
"analytics.query.filter.add": {
"message": "Dodaj filtr"
},
"analytics.query.label.breakdown": {
"message": "Rozkład:"
},
"analytics.query.label.grouped-by": {
"message": "Grupowane wg."
},
@@ -302,6 +350,15 @@
"analytics.stat.revenue": {
"message": "Przychód"
},
"analytics.stat.revenue-value": {
"message": "${value}"
},
"analytics.stat.unavailable": {
"message": "Nie dotyczy"
},
"analytics.stat.unavailable-tooltip": {
"message": "Statystyka niedostępna dla tego zapytania"
},
"analytics.stat.views": {
"message": "Wyświetlenia"
},
@@ -1346,6 +1403,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Przekroczono limit wypłat (<b>{withdrawLimit}</b>). Musisz wypełnić formularz podatkowy, by wypłacić więcej."
},
"dashboard.discord-roles.banner.body": {
"message": "Możesz otrzymać role: {roles}. Połącz swoje konto Discord poprzez Modrinth, a my automatycznie Ci je damy."
},
"dashboard.discord-roles.banner.cta": {
"message": "Połącz z Discord"
},
"dashboard.discord-roles.banner.title": {
"message": "Odbierz role na Discord"
},
"dashboard.discord-roles.role.big-creator": {
"message": "1M+ pobrań"
},
"dashboard.discord-roles.role.creator": {
"message": "Twórca"
},
"dashboard.discord-roles.role.pride": {
"message": "Miesiąc dumy 2026"
},
"dashboard.head-title": {
"message": "Kokpit"
},
@@ -1718,6 +1793,9 @@
"discover.install.heading.reset-modpack": {
"message": "Wybieranie paczki modów do zainstalowania po zresetowaniu"
},
"discover.seo.description": {
"message": "Przeglądaj {projectType} Minecraft na Modrinth z natychmiastowymi, trafnymi wynikami wyszukiwania. Nasze filtry pomogą Ci znaleźć najlepsze {projectType} Minecraft."
},
"discover.seo.title": {
"message": "Szukaj {projectType}"
},
@@ -2720,6 +2798,9 @@
"profile.bio.fallback.user": {
"message": "Użytkownik Modrinth."
},
"profile.button.analytics": {
"message": "Pokaż dane analityczne użytkownika"
},
"profile.button.billing": {
"message": "Zarządzaj rozliczaniem użytkownika"
},
+49 -19
View File
@@ -15,7 +15,7 @@
"message": "Cancelar"
},
"analytics.action.refresh": {
"message": "Recarregar"
"message": "Atualizar"
},
"analytics.action.reset": {
"message": "Redefinir"
@@ -33,16 +33,16 @@
"message": "Versão do jogo"
},
"analytics.breakdown.generic": {
"message": "Detalhamento"
"message": "Detalhes"
},
"analytics.breakdown.loader": {
"message": "Carregador"
"message": "Loader"
},
"analytics.breakdown.monetization": {
"message": "Monetização"
},
"analytics.breakdown.none.selected": {
"message": "Sem detalhamento"
"message": "Sem detalhes"
},
"analytics.breakdown.project": {
"message": "Projeto"
@@ -54,7 +54,7 @@
"message": "Versão do projeto"
},
"analytics.breakdown.selected": {
"message": "Detalhamento por {breakdown}"
"message": "Detalhes por {breakdown}"
},
"analytics.chart.action.show-all": {
"message": "Exibir tudo"
@@ -87,7 +87,7 @@
"message": "Mostrar"
},
"analytics.chart.controls.modrinth-events": {
"message": "Eventos Modrinth"
"message": "Eventos do Modrinth"
},
"analytics.chart.controls.no-modrinth-events": {
"message": "Sem eventos do Modrinth no gráfico."
@@ -102,7 +102,7 @@
"message": "Eventos do projeto"
},
"analytics.chart.controls.ratio": {
"message": "Razão"
"message": "Proporção"
},
"analytics.chart.empty.select-table-items": {
"message": "Selecione itens da tabela abaixo para visualizar seus dados."
@@ -114,16 +114,16 @@
"message": "<project>{projectName}</project>: {title}"
},
"analytics.chart.events.see-announcement": {
"message": "Veja o anúncio"
"message": "Ver anúncio"
},
"analytics.chart.legend.monetization-details.aria": {
"message": "Ver detalhes das análises monetizada"
"message": "Ver detalhes das análises de monetização"
},
"analytics.chart.legend.monetization-details.description": {
"message": "Apenas as visualizações e os downloads feitos através do Modrinth contam para a monetização, e os downloads exigem que os usuários estejam logados."
"message": "Somente as visualizações e downloads feitas através do Modrinth contam na monetização, e downloads exigem que usuários estejam registrados."
},
"analytics.chart.legend.monetization-details.title": {
"message": "Detalhes de análises monetizadas"
"message": "Detalhes de análises de monetização"
},
"analytics.chart.legend.previous-period-suffix": {
"message": "{name} (Ant.)"
@@ -135,16 +135,16 @@
"message": "Exibir todas as {count} linhas no gráfico?"
},
"analytics.chart.table-selection.all": {
"message": "Exibindo {itemType, select,project {{count, plural, one {todo o projeto} other {todos os projetos}}}country {{count, plural, one {todo o país} other {todos os países}}}monetization {{count, plural, one {todo o valor de monetização} other {todos os valores de monetização}}}downloadSource {{count, plural, one {toda a fonte de download} other {todas as fontes de download}}} downloadReason {{count, plural, one {todo o motivo de download} other {todos os motivos de download}}} projectVersion {{count, plural, one {toda a versão do projeto} other {todas as versões do projeto}}} loader {{count, plural, one {todo o carregador} other {todos os carregadores}}} gameVersion {{count, plural, one {toda a versão do jogo} other {todas as versões do jogo}}} other {{count, plural, one {todo o item} other {todos os itens}}}\n} da tabela"
"message": "Exibindo {itemType, select,project {{count, plural, one {todo o projeto} other {todos os projetos}}}country {{count, plural, one {todo o país} other {todos os países}}}monetization {{count, plural, one {todo o valor de monetização} other {todos os valores de monetização}}}downloadSource {{count, plural, one {toda a fonte de download} other {todas as fontes de download}}} downloadReason {{count, plural, one {todo o motivo de download} other {todos os motivos de download}}} projectVersion {{count, plural, one {toda a versão do projeto} other {todas as versões do projeto}}} loader {{count, plural, one {todo loader} other {todos os loaders}}} gameVersion {{count, plural, one {toda a versão do jogo} other {todas as versões do jogo}}} other {{count, plural, one {todo o item} other {todos os itens}}}\n} da tabela"
},
"analytics.chart.table-selection.count": {
"message": "Exibindo {count} {itemType, select, project {{count, plural, one {projeto} other {projetos}}} country {{count, plural, one {país} other {países}}} monetization {{count, plural, one {valor de monetização} other {valores de monetização}}} downloadSource {{count, plural, one {fonte de download} other {fontes de download}}} downloadReason {{count, plural, one {motivo de download} other {motivos de download}}} projectVersion {{count, plural, one {versão do projeto} other {versões do projeto}}} loader {{count, plural, one {carregador} other {carregadores}}} gameVersion {{count, plural, one {versão do jogo} other {versões do jogo}}} other {{count, plural, one {item} other {itens}}}} da tabela"
"message": "Exibindo {count} {itemType, select, project {{count, plural, one {projeto} other {projetos}}} country {{count, plural, one {país} other {países}}} monetization {{count, plural, one {valor de monetização} other {valores de monetização}}} downloadSource {{count, plural, one {fonte de download} other {fontes de download}}} downloadReason {{count, plural, one {motivo de download} other {motivos de download}}} projectVersion {{count, plural, one {versão do projeto} other {versões do projeto}}} loader {{count, plural, one {loader} other {loaders}}} gameVersion {{count, plural, one {versão do jogo} other {versões do jogo}}} other {{count, plural, one {item} other {itens}}}} da tabela"
},
"analytics.chart.table-selection.limited": {
"message": "Exibindo {limit} {itemType, select, project {{limit, plural, one {projeto} other {projetos}}} country {{limit, plural, one {país} other {países}}} monetization {{limit, plural, one {valor de monetização} other {valores de monetização}}} downloadSource {{limit, plural, one {fonte de download} other {fontes de download}}} downloadReason {{limit, plural, one {motivo de download} other {motivos de download}}} projectVersion {{limit, plural, one {versão do projeto} other {versões do projeto}}} loader {{limit, plural, one {carregador} other {carregadores}}} gameVersion {{limit, plural, one {versão do jogo} other {versões do jogo}}} other {{limit, plural, one {item} other {itens}}}} da tabela"
"message": "Exibindo {limit} {itemType, select, project {{limit, plural, one {projeto} other {projetos}}} country {{limit, plural, one {país} other {países}}} monetization {{limit, plural, one {valor de monetização} other {valores de monetização}}} downloadSource {{limit, plural, one {fonte de download} other {fontes de download}}} downloadReason {{limit, plural, one {motivo de download} other {motivos de download}}} projectVersion {{limit, plural, one {versão do projeto} other {versões do projeto}}} loader {{limit, plural, one {loader} other {loaders}}} gameVersion {{limit, plural, one {versão do jogo} other {versões do jogo}}} other {{limit, plural, one {item} other {itens}}}} da tabela"
},
"analytics.chart.table-selection.top": {
"message": "Exibindo os {count} {itemType, select,\nproject {{count, plural, one {projeto principal} other {principais projetos}}}\ncountry {{count, plural, one {país principal} other {principais países}}}\nmonetization {{count, plural, one {valor de monetização principal} other {principais valores de monetização}}}\ndownloadSource {{count, plural, one {fonte de download principal} other {principais fontes de download}}}\ndownloadReason {{count, plural, one {motivo de download principal} other {principais motivos de download}}}\nprojectVersion {{count, plural, one {versão principal do projeto} other {principais versões do projeto}}}\nloader {{count, plural, one {carregador principal} other {principais carregadores}}}\ngameVersion {{count, plural, one {versão principal do jogo} other {principais versões do jogo}}}\nother {{count, plural, one {item principal} other {principais itens}}}\n} da tabela"
"message": "Exibindo os {count} {itemType, select,\nproject {{count, plural, one {projeto principal} other {principais projetos}}}\ncountry {{count, plural, one {país principal} other {principais países}}}\nmonetization {{count, plural, one {valor de monetização principal} other {principais valores de monetização}}}\ndownloadSource {{count, plural, one {fonte de download principal} other {principais fontes de download}}}\ndownloadReason {{count, plural, one {motivo de download principal} other {principais motivos de download}}}\nprojectVersion {{count, plural, one {versão principal do projeto} other {principais versões do projeto}}}\nloader {{count, plural, one {loader principal} other {loaders principais}}}\ngameVersion {{count, plural, one {versão principal do jogo} other {principais versões do jogo}}}\nother {{count, plural, one {item principal} other {principais itens}}}\n} da tabela"
},
"analytics.chart.tooltip.duration.days": {
"message": "{count, plural, one {# dia} other {# dias}}"
@@ -156,7 +156,7 @@
"message": "{count, plural, one {# minuto} other {# minutos}}"
},
"analytics.chart.tooltip.hide-entry": {
"message": "Esconder {name} no gráfico"
"message": "Ocultar {name} no gráfico"
},
"analytics.chart.tooltip.pinned": {
"message": "Dica de ferramenta do gráfico fixada"
@@ -225,7 +225,7 @@
"message": "Tudo"
},
"analytics.filter.game-version-type.release": {
"message": "Estável"
"message": "Release"
},
"analytics.filter.search.countries": {
"message": "Buscar países..."
@@ -240,7 +240,7 @@
"message": "Buscar versões..."
},
"analytics.graph.title.downloads": {
"message": "Download ao logo do tempo"
"message": "Downloads ao longo do tempo"
},
"analytics.graph.title.playtime": {
"message": "Tempo jogado ao longo do tempo"
@@ -350,6 +350,12 @@
"analytics.project.select": {
"message": "Selecione projetos"
},
"analytics.project.user": {
"message": "Projetos de {username}"
},
"analytics.project.your": {
"message": "Seus projetos"
},
"analytics.query.filter.add": {
"message": "Adicionar filtro"
},
@@ -1457,6 +1463,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Você esgotou seu limite de retirada <b>{withdrawLimit}</b>. Você deve completar o formulário fiscal para retirar mais."
},
"dashboard.discord-roles.banner.body": {
"message": "Você é elegível para {roles}. Vincule sua conta do Discord através do Modrinth para podermos sincronizá-las automaticamente."
},
"dashboard.discord-roles.banner.cta": {
"message": "Vincular Discord"
},
"dashboard.discord-roles.banner.title": {
"message": "Reivindicar seus cargos do Discord"
},
"dashboard.discord-roles.role.big-creator": {
"message": "+1 mi de downloads"
},
"dashboard.discord-roles.role.creator": {
"message": "Criador"
},
"dashboard.discord-roles.role.pride": {
"message": "Orgulho 2026"
},
"dashboard.head-title": {
"message": "Painel de controle"
},
@@ -2288,6 +2312,9 @@
"landing.subheading": {
"message": "Descubra, jogue e compartilhe conteúdo de Minecraft por meio de nossa plataforma de código aberto criada para a comunidade."
},
"layout.action.analytics-events": {
"message": "Eventos de analítica"
},
"layout.action.change-theme": {
"message": "Mudar tema"
},
@@ -2843,6 +2870,9 @@
"profile.bio.fallback.user": {
"message": "Usuário comum."
},
"profile.button.analytics": {
"message": "Ver estatísticas do usuário"
},
"profile.button.billing": {
"message": "Gerenciar cobrança do usuário"
},
@@ -4521,7 +4551,7 @@
"message": "Desenvolvedor"
},
"settings.sidebar.label.display": {
"message": "Mostrar"
"message": "Interface"
},
"ui.latest-news-row.latest-news": {
"message": "Notícias recentes do Modrinth"
+31 -1
View File
@@ -213,7 +213,7 @@
"message": "Динамика скачиваний"
},
"analytics.graph.title.playtime": {
"message": "Динамика игрового времени"
"message": "Динамика времени игры"
},
"analytics.graph.title.revenue": {
"message": "Динамика дохода"
@@ -317,6 +317,12 @@
"analytics.project.select": {
"message": "Выберите проекты"
},
"analytics.project.user": {
"message": "Проекты {username}"
},
"analytics.project.your": {
"message": "Ваши проекты"
},
"analytics.query.filter.add": {
"message": "Добавить фильтр"
},
@@ -1409,6 +1415,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "Достигнут лимит вывода в <b>{withdrawLimit}</b>. Для снятия лимита заполните налоговую форму."
},
"dashboard.discord-roles.banner.body": {
"message": "Привяжите ваш аккаунт Discord через Modrinth для синхронизации следующих ролей: {roles}."
},
"dashboard.discord-roles.banner.cta": {
"message": "Привязать Discord"
},
"dashboard.discord-roles.banner.title": {
"message": "Получите ваши роли Discord"
},
"dashboard.discord-roles.role.big-creator": {
"message": ">1 млн скачиваний"
},
"dashboard.discord-roles.role.creator": {
"message": "Автор"
},
"dashboard.discord-roles.role.pride": {
"message": "Pride 2026"
},
"dashboard.head-title": {
"message": "Панель управления"
},
@@ -2234,6 +2258,9 @@
"landing.subheading": {
"message": "Находите, играйте и делитесь творениями для Minecraft на платформе с открытым кодом, созданной для сообщества."
},
"layout.action.analytics-events": {
"message": "События аналитики"
},
"layout.action.change-theme": {
"message": "Изменить тему"
},
@@ -2789,6 +2816,9 @@
"profile.bio.fallback.user": {
"message": "Пользователь Modrinth."
},
"profile.button.analytics": {
"message": "Посмотреть аналитику"
},
"profile.button.billing": {
"message": "Управление платежами"
},
File diff suppressed because it is too large Load Diff
@@ -350,6 +350,12 @@
"analytics.project.select": {
"message": "Projeleri seç"
},
"analytics.project.user": {
"message": "{username} adlı kullanıcının projeleri"
},
"analytics.project.your": {
"message": "Senin projelerin"
},
"analytics.query.filter.add": {
"message": "Filtre ekle"
},
@@ -1457,6 +1463,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "<b>{withdrawLimit}</b> çekme limitinizi kadarını kullandınız. Daha fazla çekmek için vergi formunu gerekir."
},
"dashboard.discord-roles.banner.body": {
"message": "{roles} için uygunsun. Discord hesabını Modrinth üzerinden bağla, bizde bunları otomatik olarak senkronize edelim."
},
"dashboard.discord-roles.banner.cta": {
"message": "Discord bağla"
},
"dashboard.discord-roles.banner.title": {
"message": "Discord rollerini al"
},
"dashboard.discord-roles.role.big-creator": {
"message": "1M+ İndirme"
},
"dashboard.discord-roles.role.creator": {
"message": "Oluşturucu"
},
"dashboard.discord-roles.role.pride": {
"message": "Onur 2026"
},
"dashboard.head-title": {
"message": "Kontrol Paneli"
},
@@ -2285,6 +2309,9 @@
"landing.subheading": {
"message": "Topluluk odaklı açık kaynak platformumuzda Minecraft içerikleri keşfedin, oynayın ve paylaşın."
},
"layout.action.analytics-events": {
"message": "Analitik olayları"
},
"layout.action.change-theme": {
"message": "Tema değiştir"
},
@@ -2840,6 +2867,9 @@
"profile.bio.fallback.user": {
"message": "Bir Modrinth kullanıcısı."
},
"profile.button.analytics": {
"message": "Kullanıcı analizlerini görüntüle"
},
"profile.button.billing": {
"message": "Kullanıcı faturasını yönet"
},
+38 -11
View File
@@ -171,7 +171,7 @@
"message": "总计"
},
"analytics.chart.view.area": {
"message": "地区"
"message": "面积图"
},
"analytics.chart.view.bar": {
"message": "柱状图"
@@ -344,6 +344,12 @@
"analytics.project.select": {
"message": "选择项目"
},
"analytics.project.user": {
"message": "{username}的项目"
},
"analytics.project.your": {
"message": "你的项目"
},
"analytics.query.filter.add": {
"message": "添加过滤条件"
},
@@ -1445,6 +1451,24 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "你已用尽<b>{withdrawLimit}</b>的提现额度。若需继续提现,请先完成税务表格填写。"
},
"dashboard.discord-roles.banner.body": {
"message": "你符合获取 {roles} 的条件。通过 Modrinth 关联你的 Discord 账户,我们将自动同步这些身份。"
},
"dashboard.discord-roles.banner.cta": {
"message": "关联 Discord 账户"
},
"dashboard.discord-roles.banner.title": {
"message": "领取你的 Discord 身分组"
},
"dashboard.discord-roles.role.big-creator": {
"message": "破百万下载"
},
"dashboard.discord-roles.role.creator": {
"message": "创作者"
},
"dashboard.discord-roles.role.pride": {
"message": "2026 骄傲月"
},
"dashboard.head-title": {
"message": "仪表盘"
},
@@ -2267,6 +2291,9 @@
"landing.subheading": {
"message": "在我们为社区打造的开源平台上,探索、畅玩并分享 Minecraft 的无限可能。"
},
"layout.action.analytics-events": {
"message": "分析活动"
},
"layout.action.change-theme": {
"message": "更改界面主题"
},
@@ -2822,6 +2849,9 @@
"profile.bio.fallback.user": {
"message": "一位 Modrinth 用户。"
},
"profile.button.analytics": {
"message": "查看用户分析"
},
"profile.button.billing": {
"message": "管理用户财务"
},
@@ -3344,18 +3374,15 @@
"project.settings.permissions.learn-more": {
"message": "了解详情"
},
"project.settings.permissions.search-placeholder": {
"message": "搜索 {count}{count, plural,one {个外部项目}other {个外部项目}}……"
},
"project.settings.title": {
"message": "设置"
},
"project.settings.visit-dashboard": {
"message": "访问项目的控制面板"
},
"project.stats.downloads-label": {
"message": "{count, plural,one {下载}other {下载}}"
},
"project.stats.followers-label": {
"message": "{count, plural,one {关注者}other {关注者}}"
},
"project.status.archived.message": {
"message": "{title} 已归档。除非作者决定取消归档,否则 {title} 将不再更新。"
},
@@ -3438,7 +3465,7 @@
"message": "你正在举报哪种类型的内容?"
},
"report.question.report-reason": {
"message": "该项目{item}违反了 Modrinth 的哪条规则?"
"message": "该项目{item}违反了 Modrinth 的哪条规则?"
},
"report.report-content": {
"message": "向管理员举报内容"
@@ -3945,10 +3972,10 @@
"message": "在登录时为您的账户增加一层额外的安全保障。"
},
"settings.account.security.two-factor.modal.remove.header": {
"message": "移除重验证"
"message": "移除2重验证"
},
"settings.account.security.two-factor.modal.setup.header": {
"message": "设置重验证"
"message": "设置2重验证"
},
"settings.account.security.two-factor.title": {
"message": "双因素认证"
@@ -4404,7 +4431,7 @@
"message": "创建个人访问令牌"
},
"settings.pats.description": {
"message": "个人访问令牌PAT可用于访问 Modrinth 的 API。这些令牌可随时创建和撤销。更多信息,请参阅 <doc-link>Modrinth API 文档</doc-link>。"
"message": "个人访问令牌PAT可用于访问 Modrinth 的 API。这些令牌可随时创建和撤销。更多信息,请参阅 <doc-link>Modrinth API 文档</doc-link>。"
},
"settings.pats.modal.create.action": {
"message": "创建个人访问令牌"
+328 -28
View File
@@ -39,7 +39,7 @@
"message": "載入器"
},
"analytics.breakdown.monetization": {
"message": "營利"
"message": "營利情形"
},
"analytics.breakdown.none.selected": {
"message": "未選取細項"
@@ -68,18 +68,33 @@
"analytics.chart.axis.playtime-hours": {
"message": "{hours} 小時"
},
"analytics.chart.controls.active-count": {
"message": "{count} 項啟用中"
},
"analytics.chart.controls.annotations": {
"message": "註釋"
},
"analytics.chart.controls.aria": {
"message": "分析圖表控制項,{activeCount}"
},
"analytics.chart.controls.button": {
"message": "控制"
},
"analytics.chart.controls.dialog-aria": {
"message": "分析圖表控制項"
},
"analytics.chart.controls.display": {
"message": "顯示"
},
"analytics.chart.controls.modrinth-events": {
"message": "Modrinth 事件"
},
"analytics.chart.controls.no-modrinth-events": {
"message": "圖表中沒有 Modrinth 事件。"
},
"analytics.chart.controls.no-project-events": {
"message": "圖表中沒有專案事件。"
},
"analytics.chart.controls.previous-period": {
"message": "上一期"
},
@@ -89,6 +104,9 @@
"analytics.chart.controls.ratio": {
"message": "百分比"
},
"analytics.chart.empty.select-table-items": {
"message": "從表格選取項目來視覺化你的數據。"
},
"analytics.chart.events.project-title": {
"message": "<project>{projectName}</project>{title}"
},
@@ -98,18 +116,30 @@
"analytics.chart.legend.monetization-details.aria": {
"message": "檢視營利分析詳情"
},
"analytics.chart.legend.monetization-details.description": {
"message": "只有透過 Modrinth 產生的瀏覽量與下載量才會計入營利,且使用者必須登入才能計算下載量。"
},
"analytics.chart.legend.monetization-details.title": {
"message": "營利分析詳情"
},
"analytics.chart.legend.previous-period-suffix": {
"message": "{name}(上一期)"
},
"analytics.chart.tooltip.hide-entry": {
"message": "在圖表中隱藏 {name}"
},
"analytics.chart.tooltip.pinned": {
"message": "圖表工具提示已固定"
},
"analytics.chart.tooltip.pinned-aria": {
"message": "釘選"
},
"analytics.chart.tooltip.previous-period-short": {
"message": "(上一期)"
},
"analytics.chart.tooltip.show-entry": {
"message": "在圖表中顯示 {name}"
},
"analytics.chart.tooltip.total": {
"message": "總計"
},
@@ -140,6 +170,18 @@
"analytics.download-source.website": {
"message": "Modrinth 網站"
},
"analytics.downloads.suffix": {
"message": "次"
},
"analytics.empty.no-data": {
"message": "暫無可用數據"
},
"analytics.empty.no-projects": {
"message": "暫無可用專案"
},
"analytics.empty.no-projects-for-analytics": {
"message": "暫無可供分析的專案"
},
"analytics.filter.game-version-type": {
"message": "遊戲版本類型"
},
@@ -152,6 +194,9 @@
"analytics.filter.search.countries": {
"message": "搜尋國家或地區..."
},
"analytics.filter.search.download-sources": {
"message": "搜尋下載來源..."
},
"analytics.filter.search.project-versions": {
"message": "搜尋專案版本..."
},
@@ -162,7 +207,7 @@
"message": "下載量趨勢"
},
"analytics.graph.title.playtime": {
"message": "遊玩時趨勢"
"message": "遊玩時趨勢"
},
"analytics.graph.title.revenue": {
"message": "收益趨勢"
@@ -209,15 +254,54 @@
"analytics.group-by.year": {
"message": "年"
},
"analytics.loading.fetching-results": {
"message": "正在取得結果..."
},
"analytics.options.loading": {
"message": "載入中..."
},
"analytics.project-event.project-approved": {
"message": "專案獲核准"
},
"analytics.project-event.project-private": {
"message": "專案已設為私人"
},
"analytics.project-event.project-status-changed": {
"message": "專案狀態變更"
},
"analytics.project-event.project-unlisted": {
"message": "專案已設為不公開"
},
"analytics.project-event.version-released": {
"message": "{version} 版本發布"
},
"analytics.project-event.version-uploaded": {
"message": "版本上傳"
},
"analytics.project-status.approved": {
"message": "已核准"
},
"analytics.project-status.archived": {
"message": "已封存"
},
"analytics.project-status.draft": {
"message": "草稿"
},
"analytics.project-status.other": {
"message": "其他"
},
"analytics.project-status.private": {
"message": "私人"
},
"analytics.project-status.rejected": {
"message": "已拒絕"
},
"analytics.project-status.unlisted": {
"message": "不公開"
},
"analytics.project-status.withheld": {
"message": "擱置"
},
"analytics.project.all": {
"message": "所有專案"
},
@@ -227,6 +311,12 @@
"analytics.project.select": {
"message": "選取專案"
},
"analytics.project.user": {
"message": "{username} 的專案"
},
"analytics.project.your": {
"message": "你的專案"
},
"analytics.query.filter.add": {
"message": "新增篩選條件"
},
@@ -234,10 +324,28 @@
"message": "細項:"
},
"analytics.query.label.grouped-by": {
"message": "分組依據"
"message": "分組依據"
},
"analytics.query.label.project": {
"message": "專案:"
},
"analytics.query.label.timeframe": {
"message": "時間範圍:"
},
"analytics.stat.downloads": {
"message": "下載量"
},
"analytics.stat.monetization-banner.body": {
"message": "只有透過 Modrinth 產生的瀏覽量與下載量才會計入營利,且必須通過防詐欺篩選機制。此外,使用者必須登入才能在 Modrinth App 下載專案。由於所有專案中可營利的下載比例大致相同,因此即使計入所有下載量,你的收益也不會產生顯著變化。"
},
"analytics.stat.monetization-banner.learn-more": {
"message": "了解更多"
},
"analytics.stat.monetization-banner.title": {
"message": "盈利機制是如何運作的?"
},
"analytics.stat.playtime": {
"message": "遊玩時"
"message": "遊玩時"
},
"analytics.stat.playtime-hours": {
"message": "{hours} 小時"
@@ -245,33 +353,96 @@
"analytics.stat.previous-period-comparison": {
"message": "vs 上一期"
},
"analytics.stat.previous-period-comparison-short": {
"message": "vs 上一期"
},
"analytics.stat.revenue": {
"message": "收益"
},
"analytics.stat.revenue-value": {
"message": "${value}"
},
"analytics.stat.unavailable": {
"message": "N/A"
},
"analytics.stat.unavailable-tooltip": {
"message": "目前查詢沒有可用的統計資訊"
},
"analytics.stat.views": {
"message": "瀏覽量"
},
"analytics.table.csv.date-range": {
"message": "{start}至{end}"
},
"analytics.table.csv.filename": {
"message": "Modrinth 數據分析 - 按{breakdown} - {dateRange}"
},
"analytics.table.csv.header.playtime-seconds": {
"message": "遊玩時(秒)"
"message": "遊玩時(秒)"
},
"analytics.table.csv.selected-range": {
"message": "選取特定範圍"
"message": "選取範圍"
},
"analytics.table.empty.no-matching-rows": {
"message": "沒有符合條件的數據"
},
"analytics.table.export-csv": {
"message": "匯出 CSV"
},
"analytics.table.export.cumulative": {
"message": "累計"
},
"analytics.table.export.grouped": {
"message": "按{groupBy}分組"
},
"analytics.table.pagination.summary": {
"message": "顯示第 {start} 項至第 {end} 項,共 {total} 項"
},
"analytics.table.search.placeholder": {
"message": "搜尋..."
},
"analytics.threshold.countries-above": {
"message": "國家/地區下載量多於"
},
"analytics.threshold.country-downloads-aria": {
"message": "國家/地區下載量閾值"
},
"analytics.threshold.game-version-downloads-aria": {
"message": "遊戲版本下載量閾值"
},
"analytics.threshold.game-versions-above": {
"message": "遊戲版本下載量多於"
},
"analytics.threshold.project-downloads-aria": {
"message": "專案下載量閾值"
},
"analytics.threshold.project-version-downloads-aria": {
"message": "專案版本下載量閾值"
},
"analytics.threshold.project-versions-above": {
"message": "專案版本下載量多於"
},
"analytics.threshold.projects-above": {
"message": "專案下載量多於"
},
"analytics.title": {
"message": "數據分析"
},
"analytics.value.monetized": {
"message": "可營利"
},
"analytics.value.none": {
"message": "無"
},
"analytics.value.other": {
"message": "其他"
},
"analytics.value.unknown": {
"message": "未知"
},
"analytics.value.unmonetized": {
"message": "非營利"
},
"app-marketing.download.description": {
"message": "我們的桌面應用程式可在所有平臺上使用,請選擇你所需的版本。"
},
@@ -671,12 +842,84 @@
"collection.title": {
"message": "{name} - 收藏"
},
"conversation-thread.action.add-private-note": {
"message": "新增私人附註"
},
"conversation-thread.action.approve": {
"message": "核准"
},
"conversation-thread.action.approve-with-reply": {
"message": "回覆並核准"
},
"conversation-thread.action.close-thread": {
"message": "關閉討論串"
},
"conversation-thread.action.close-with-reply": {
"message": "回覆並關閉"
},
"conversation-thread.action.reject": {
"message": "拒絕"
},
"conversation-thread.action.reject-with-reply": {
"message": "回覆並拒絕"
},
"conversation-thread.action.reopen-thread": {
"message": "重新開啟討論串"
},
"conversation-thread.action.reply": {
"message": "回覆"
},
"conversation-thread.action.reply-to-thread": {
"message": "回覆討論串"
},
"conversation-thread.action.resubmit-for-review": {
"message": "重新提交審查"
},
"conversation-thread.action.resubmit-for-review-with-reply": {
"message": "回覆並重新提交審查"
},
"conversation-thread.action.send": {
"message": "傳送"
},
"conversation-thread.action.send-to-review": {
"message": "送交審查"
},
"conversation-thread.action.send-to-review-with-reply": {
"message": "回覆並送交審查"
},
"conversation-thread.action.set-to-draft": {
"message": "設為草稿"
},
"conversation-thread.action.set-to-draft-with-reply": {
"message": "回覆並設為草稿"
},
"conversation-thread.action.withhold": {
"message": "保留"
},
"conversation-thread.action.withhold-with-reply": {
"message": "回覆並保留"
},
"conversation-thread.closed-thread.description": {
"message": "這個討論串已關閉,無法再傳送新訊息。"
},
"conversation-thread.error.closing-report": {
"message": "關閉檢舉時發生錯誤"
},
"conversation-thread.error.reopening-report": {
"message": "重新開啟檢舉時發生錯誤"
},
"conversation-thread.error.sending-message": {
"message": "傳送訊息時發生錯誤"
},
"conversation-thread.reply-editor.placeholder.reply": {
"message": "回覆討論串..."
},
"conversation-thread.reply-editor.placeholder.send": {
"message": "傳送訊息..."
},
"conversation-thread.reply-modal.header": {
"message": "回覆討論串"
},
"conversation-thread.resubmit-modal.header.resubmitting": {
"message": "重新提交審查"
},
@@ -1139,8 +1382,29 @@
"dashboard.creator-withdraw-modal.withdraw-limit-used": {
"message": "你已用盡你的提領額度:<b>{withdrawLimit}</b>。你必須填寫一份稅務表單才能提領更多金額。"
},
"dashboard.discord-roles.banner.body": {
"message": "你符合以下身分組的領取資格:{roles}。透過 Modrinth 連結你的 Discord 帳號,我們將自動為你同步。"
},
"dashboard.discord-roles.banner.cta": {
"message": "連結 Discord 帳號"
},
"dashboard.discord-roles.banner.title": {
"message": "領取你的 Discord 身分組"
},
"dashboard.discord-roles.role.big-creator": {
"message": "破百萬下載"
},
"dashboard.discord-roles.role.creator": {
"message": "創作者"
},
"dashboard.discord-roles.role.pride": {
"message": "2026年驕傲月"
},
"dashboard.head-title": {
"message": "儀表板"
"message": "資訊主頁"
},
"dashboard.notifications.empty.no-unread": {
"message": "你沒有未讀通知。"
},
"dashboard.notifications.link.see-all": {
"message": "查看全部"
@@ -1157,14 +1421,14 @@
"dashboard.organizations.error.fetch": {
"message": "無法取得組織資訊"
},
"dashboard.organizations.member-count": {
"message": "{count} {count, plural,one {成員} other {成員}}"
},
"dashboard.organizations.title": {
"message": "組織"
},
"dashboard.overview.notifications.button.mark-all-as-read": {
"message": "全部標記為已讀"
},
"dashboard.overview.notifications.button.view-history": {
"message": "檢視紀錄"
"message": "查看紀錄"
},
"dashboard.overview.notifications.empty.no-unread": {
"message": "你沒有任何未讀通知。"
@@ -1179,34 +1443,34 @@
"message": "通知紀錄"
},
"dashboard.overview.notifications.loading": {
"message": "正在載入通知……"
"message": "正在載入通知..."
},
"dashboard.projects.bulk-edit-hint": {
"message": "可以透過在下方選擇項目來同時編輯多個項目。"
"message": "可以透過在下方選擇專案來同時編輯多個專案。"
},
"dashboard.projects.bulk-edit.server-disabled": {
"message": "伺服器專案不支援大量編輯"
},
"dashboard.projects.empty": {
"message": "目前還沒有任何項目。點擊上方綠色按鈕開始。"
"message": "目前還沒有任何專案。點擊上方綠色按鈕開始。"
},
"dashboard.projects.head-title": {
"message": "專案"
},
"dashboard.projects.links.and-more": {
"message": "{count}還有更多…"
"message": "還有另外 {count} 個..."
},
"dashboard.projects.links.button.clear-link": {
"message": "清連結"
"message": "清連結"
},
"dashboard.projects.links.button.edit": {
"message": "編輯連結"
},
"dashboard.projects.links.description": {
"message": "在下方指定的任何連結都將覆蓋到每個選定的項目中。留空的連結將被忽略。可以使用垃圾桶按鈕從所有選定的項目中清除連結。"
"message": "在下方指定的任何連結都將覆蓋到每個選定的專案中。留空的連結將被忽略。可以使用垃圾桶按鈕從所有選定的專案中清除連結。"
},
"dashboard.projects.links.discord-invite.description": {
"message": "的 Discord 伺服器邀請連結。"
"message": "的 Discord 伺服器邀請連結。"
},
"dashboard.projects.links.discord-invite.label": {
"message": "Discord 邀請"
@@ -1616,6 +1880,9 @@
"frog.title": {
"message": "青蛙"
},
"hosting-marketing.available-locations": {
"message": "服務範圍遍及北美、歐洲、東南亞與澳洲,提供廣泛的區域覆蓋。"
},
"hosting-marketing.billing.monthly": {
"message": "按月付費"
},
@@ -1676,6 +1943,9 @@
"hosting-marketing.faq.location": {
"message": "Modrinth Hosting 伺服器位於哪裡?我可以選擇區域嗎?"
},
"hosting-marketing.faq.location.answer": {
"message": "我們目前在北美、歐洲、東南亞與澳洲設有伺服器,你可以在購買時選擇。未來還會推出更多區域!如果你想切換區域,請聯絡支援團隊。"
},
"hosting-marketing.faq.versions-loaders": {
"message": "可以使用哪些 Minecraft 版本與載入器?"
},
@@ -1848,7 +2118,7 @@
"message": "多元生態系"
},
"landing.creator.feature.monetization.description": {
"message": "從你的專案頁面獲得廣告收入,並隨時提領你的資金"
"message": "從你的專案頁面獲得廣告收入,並隨時提領你的款項"
},
"landing.creator.feature.monetization.title": {
"message": "營利"
@@ -1949,6 +2219,9 @@
"landing.subheading": {
"message": "透過我們為社群打造的開源平臺,探索、遊玩並分享 Minecraft 內容。"
},
"layout.action.analytics-events": {
"message": "數據分析事件"
},
"layout.action.change-theme": {
"message": "更換主題"
},
@@ -2006,6 +2279,9 @@
"layout.banner.add-email.description": {
"message": "為了安全起見,Modrinth 需要你為帳號註冊電子郵件地址。"
},
"layout.banner.build-fail.always-ignore": {
"message": "一律忽略"
},
"layout.banner.build-fail.description": {
"message": "Modrinth 的前端無法從 API 取得資料,導致本次部署失敗。這可能是服務中斷或設定錯誤所致。請在 API 恢復可用時,再重新嘗試。錯誤碼:{errors};目前的 API 網址是:{url}"
},
@@ -2490,7 +2766,7 @@
"message": "加密貨幣 (USDC)"
},
"muralpay.warning.wallet-address": {
"message": "請仔細檢查你的錢包帳戶。發送到錯誤帳戶的資金將無法追回。"
"message": "請仔細檢查你的錢包帳戶。發送到錯誤帳戶的款項將無法追回。"
},
"profile.bio.fallback.creator": {
"message": "一位 Modrinth 創作者。"
@@ -2498,6 +2774,9 @@
"profile.bio.fallback.user": {
"message": "一位 Modrinth 使用者。"
},
"profile.button.analytics": {
"message": "檢視使用者數據分析"
},
"profile.button.billing": {
"message": "管理使用者帳務"
},
@@ -2852,6 +3131,15 @@
"project.moderation.admonition.under-review.header": {
"message": "專案審查中"
},
"project.moderation.admonition.withheld.header": {
"message": "由工作人員設為不公開"
},
"project.moderation.thread.moderator-see-user-ui-toggle": {
"message": "顯示成員 UI"
},
"project.moderation.thread.title": {
"message": "審核訊息"
},
"project.moderation.title": {
"message": "審查"
},
@@ -2859,7 +3147,7 @@
"message": "更新日誌"
},
"project.notification.icon-updated.message": {
"message": "你的專案圖示已更新。"
"message": "你的專案圖示已更新。"
},
"project.notification.icon-updated.title": {
"message": "專案圖示已更新"
@@ -2975,6 +3263,9 @@
"report.already-reported": {
"message": "你已檢舉 {title}"
},
"report.already-reported-description": {
"message": "你已針對這個{item}提交了一份待處理的檢舉。如果你有更多資訊可以補充,你可以將細節新增到你的檢舉中。"
},
"report.back-to-item": {
"message": "回到{item}"
},
@@ -2985,7 +3276,10 @@
"message": "請提供檢舉的更多背景資訊"
},
"report.checking": {
"message": "正在檢查 {item}……"
"message": "正在檢查{item}..."
},
"report.could-not-find": {
"message": "找不到{item}"
},
"report.for.violation": {
"message": "違反 Modrinth《<rules-link>內容規範</rules-link>》或《<terms-link>使用條款</terms-link>》"
@@ -3026,9 +3320,15 @@
"report.please-report": {
"message": "請檢舉:"
},
"report.question.content-id": {
"message": "這個{item}的 ID 是什麼?"
},
"report.question.content-type": {
"message": "你要檢舉什麼類型的內容?"
},
"report.question.report-reason": {
"message": "這個{item}違反了 Modrinth 的哪一條規則?"
},
"report.report-content": {
"message": "向管理員檢舉內容"
},
@@ -3543,10 +3843,10 @@
"message": "雙重驗證"
},
"settings.account.two-factor.backup.intro": {
"message": "請下載這些備用碼並將其儲存在安全的地方。若你失去裝置的存取權,可以使用這些備用碼替代 2FA 驗證碼請務必像保護密碼一樣妥善保管這些碼。"
"message": "請下載並將這些備份碼妥善儲存在安全的地方。如果日後無法存取你的裝置,可以使用這些備份碼來替代雙重驗證碼請務必像保護密碼一樣妥善保管這些備份碼。"
},
"settings.account.two-factor.backup.single-use": {
"message": "備碼只能使用一次。"
"message": "備碼只能使用一次。"
},
"settings.account.two-factor.error.incorrect-code": {
"message": "輸入的驗證碼不正確!"
@@ -3663,7 +3963,7 @@
"message": "應用程式資訊"
},
"settings.applications.notification.icon-updated.description": {
"message": "你的應用程式圖示已更新。"
"message": "你的應用程式圖示已更新。"
},
"settings.applications.notification.icon-updated.title": {
"message": "圖示已更新"
@@ -3735,7 +4035,7 @@
"message": "福利"
},
"settings.billing.midas.save-per-year": {
"message": "改用年繳計費,每年現省 {amount}"
"message": "改用年繳方案,每年現省 {amount}"
},
"settings.billing.midas.status.cancelled.line1": {
"message": "你已取消訂閱。"
@@ -3783,13 +4083,13 @@
"message": "新增付款方式"
},
"settings.billing.payment_method.action.history": {
"message": "查看交易紀錄"
"message": "查看支付紀錄"
},
"settings.billing.payment_method.action.primary": {
"message": "設為主要付款方式"
},
"settings.billing.payment_method.card_expiry": {
"message": "到期日 {month}/{year}"
"message": "有效期限:{month}/{year}"
},
"settings.billing.payment_method.none": {
"message": "你尚未新增任何付款方式。"
+2 -68
View File
@@ -21,7 +21,6 @@
:server-name="`${auth?.user?.username}'s server`"
:out-of-stock-url="outOfStockUrl"
:fetch-capacity-statuses="fetchCapacityStatuses"
:pings="regionPings"
:regions="regions"
:refresh-payment-methods="fetchPaymentData"
:fetch-stock="fetchStock"
@@ -1233,81 +1232,16 @@ const planQuery = async () => {
}
const regions = ref([])
const regionPings = ref([])
function pingRegions() {
function fetchRegions() {
client.archon.servers_v1.getRegions().then((res) => {
regions.value = res
regions.value.forEach((region) => {
runPingTest(region)
})
})
}
const PING_COUNT = 20
const PING_INTERVAL = 200
const MAX_PING_TIME = 1000
const initialIndex = {
'eu-lim': 31,
}
function runPingTest(region, index = initialIndex[region.shortcode] ?? 1) {
if (index > (initialIndex[region.shortcode] ?? 1) + 10) {
regionPings.value.push({
region: region.shortcode,
ping: -1,
})
return
}
const wsUrl = `wss://${region.shortcode}${index}.${region.zone}/pingtest`
try {
const socket = new WebSocket(wsUrl)
const pings = []
socket.onopen = () => {
for (let i = 0; i < PING_COUNT; i++) {
setTimeout(() => {
socket.send(performance.now())
}, i * PING_INTERVAL)
}
setTimeout(
() => {
socket.close()
const median = Math.round([...pings].sort((a, b) => a - b)[Math.floor(pings.length / 2)])
if (median) {
regionPings.value.push({
region: region.shortcode,
ping: median,
})
}
},
PING_COUNT * PING_INTERVAL + MAX_PING_TIME,
)
}
socket.onmessage = (event) => {
pings.push(performance.now() - event.data)
}
socket.onerror = (event) => {
console.error(
`Failed to connect pingtest WebSocket with ${wsUrl}, trying index ${index + 1}:`,
event,
)
runPingTest(region, index + 1)
}
} catch (error) {
console.error(`Failed to connect pingtest WebSocket with ${wsUrl}:`, error)
}
}
onMounted(() => {
startTyping()
planQuery()
pingRegions()
fetchRegions()
})
watch(customer, (newCustomer) => {
+10
View File
@@ -0,0 +1,10 @@
const PROJECT_SLUG_UNSAFE_CHARS = /[^a-zA-Z0-9._-]/g
export function generateUrlSlug(value: string) {
return value
.trim()
.toLowerCase()
.replaceAll(' ', '-')
.replaceAll(PROJECT_SLUG_UNSAFE_CHARS, '')
.replaceAll(/--+/gm, '-')
}
@@ -0,0 +1,39 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n WIDTH_BUCKET(\n EXTRACT(EPOCH FROM created)::bigint,\n EXTRACT(EPOCH FROM $1::timestamp with time zone AT TIME ZONE 'UTC')::bigint,\n EXTRACT(EPOCH FROM $2::timestamp with time zone AT TIME ZONE 'UTC')::bigint,\n $3::integer\n ) AS \"bucket?\",\n CASE WHEN $5 THEN affiliate_code_source ELSE 0 END AS \"affiliate_code_source?\",\n SUM(amount) AS \"amount_sum?\"\n FROM payouts_values\n WHERE\n user_id = $4\n AND payouts_values.affiliate_code_source IS NOT NULL\n AND created >= $1\n AND created < $2\n AND (cardinality($6::bigint[]) = 0 OR affiliate_code_source = ANY($6))\n GROUP BY 1, 2\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "bucket?",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "affiliate_code_source?",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "amount_sum?",
"type_info": "Numeric"
}
],
"parameters": {
"Left": [
"Timestamptz",
"Timestamptz",
"Int4",
"Int8",
"Bool",
"Int8Array"
]
},
"nullable": [
null,
null,
null
]
},
"hash": "52431524eac4f9d821fd0ee0ca5acbc88c191dd7660d5ef7cd6376f36f3a49af"
}
@@ -0,0 +1,39 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n WIDTH_BUCKET(\n EXTRACT(EPOCH FROM usa.created_at)::bigint,\n EXTRACT(EPOCH FROM $1::timestamp with time zone AT TIME ZONE 'UTC')::bigint,\n EXTRACT(EPOCH FROM $2::timestamp with time zone AT TIME ZONE 'UTC')::bigint,\n $3::integer\n ) AS \"bucket?\",\n CASE WHEN $5 THEN affiliate_code ELSE 0 END AS \"affiliate_code?\",\n COUNT(*) AS \"conversions?\"\n FROM users_subscriptions_affiliations usa\n INNER JOIN affiliate_codes ac ON ac.id = usa.affiliate_code\n INNER JOIN users_subscriptions us ON us.id = usa.subscription_id\n INNER JOIN charges c ON c.subscription_id = us.id\n WHERE\n ac.affiliate = $4\n AND usa.created_at >= $1\n AND usa.created_at < $2\n AND c.status = 'succeeded'\n AND (cardinality($6::bigint[]) = 0 OR affiliate_code = ANY($6))\n GROUP BY 1, 2\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "bucket?",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "affiliate_code?",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "conversions?",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Timestamptz",
"Timestamptz",
"Int4",
"Int8",
"Bool",
"Int8Array"
]
},
"nullable": [
null,
null,
null
]
},
"hash": "8c2bc4b2b4c659091415058357eebd31e3546e74ff948ddbc76484178dc2956c"
}
@@ -0,0 +1,28 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT t.id team_id, m.id project_id FROM organizations o\n INNER JOIN mods m ON m.organization_id = o.id\n INNER JOIN teams t ON t.id = m.team_id\n WHERE o.id = $1 AND $1 IS NOT NULL\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "team_id",
"type_info": "Int8"
},
{
"ordinal": 1,
"name": "project_id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false,
false
]
},
"hash": "98e3a6418947b037d45bf3b9b1b24ee0fb3c8b499b8f794c151e0c655b8f9365"
}
@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT m.id\n FROM mods m\n INNER JOIN team_members tm ON tm.team_id = m.team_id\n WHERE\n m.id = ANY($1)\n AND tm.user_id = $2\n AND tm.accepted\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8Array",
"Int8"
]
},
"nullable": [
false
]
},
"hash": "a47f1d4140d9b93d99934c0b2af0ab0ae9119509d6fc72760e5f0390bfe40543"
}
@@ -0,0 +1,46 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n WIDTH_BUCKET(\n EXTRACT(EPOCH FROM created)::bigint,\n EXTRACT(EPOCH FROM $1::timestamp with time zone AT TIME ZONE 'UTC')::bigint,\n EXTRACT(EPOCH FROM $2::timestamp with time zone AT TIME ZONE 'UTC')::bigint,\n $3::integer\n ) AS \"bucket?\",\n mod_id AS \"mod_id?\",\n CASE\n WHEN $5 AND ($6 OR mod_id = ANY($7)) THEN user_id\n ELSE 0\n END AS \"user_id?\",\n SUM(amount) AS \"amount_sum?\"\n FROM payouts_values\n WHERE\n -- only project revenue is counted here\n -- for affiliate code revenue, see `affiliate_code_revenue`\n payouts_values.mod_id IS NOT NULL\n AND payouts_values.mod_id = ANY($4)\n AND created >= $1\n AND created < $2\n GROUP BY 1, 2, 3\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "bucket?",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "mod_id?",
"type_info": "Int8"
},
{
"ordinal": 2,
"name": "user_id?",
"type_info": "Int8"
},
{
"ordinal": 3,
"name": "amount_sum?",
"type_info": "Numeric"
}
],
"parameters": {
"Left": [
"Timestamptz",
"Timestamptz",
"Int4",
"Int8Array",
"Bool",
"Bool",
"Int8Array"
]
},
"nullable": [
null,
true,
null,
null
]
},
"hash": "c2e40b00e2764be89a162feb1f1cc927e873e51a0d3b6e3e142efa3ba9e0e76f"
}
@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT t.id FROM organizations o\n INNER JOIN mods m ON m.organization_id = o.id\n INNER JOIN teams t ON t.id = m.team_id\n WHERE o.id = $1 AND $1 IS NOT NULL\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Int8"
]
},
"nullable": [
false
]
},
"hash": "d3c5adda017df70a88983baa82e3feb0a3eb432ed2b9d3be0e7a0bc6b2421cdd"
}
+4
View File
@@ -25,3 +25,7 @@
- `Modrinth-Admin: feedbeef` as admin key
- If some steps require you to create a project/mod or version for testing, ask the user to go into the web frontend and manually create a project/version
- When using `sqlx::query` etc. always use the macro form like `sqlx::query!` or `sqlx::query_scalar!` - never the plain function form. Avoid using `query_as!`.
- Do not run `cargo test`, even for a single specific test, unless explicitly prompted to by the user, since it takes a long time to run.
- You can force a search reindex by:
- Running `cd apps/labrinth && cargo run -p labrinth -- --run-background-task index-search` (prefer this if backend is running locally)
- Hitting the force reindex admin endpoint
+89 -57
View File
@@ -60,15 +60,24 @@ struct TiltifyMeta {
subscription_source_type: String,
}
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Clone, Debug, Serialize, Deserialize, utoipa::ToSchema)]
pub struct CampaignInfo {
total_donations_usd: Decimal,
target_usd: Decimal,
num_donators: usize,
cached_at: DateTime<Utc>,
}
const CAMPAIGN_INFO_CACHE_NAMESPACE: &str = "campaign_info";
const CAMPAIGN_INFO_CACHE_TTL_SECONDS: i64 = 15 * 60;
const CAMPAIGN_INFO_CACHE_STALE_SECONDS: i64 = 15 * 60;
const CAMPAIGN_INFO_CACHE_TTL_SECONDS: i64 = 24 * 60 * 60;
impl CampaignInfo {
fn is_stale(&self) -> bool {
Utc::now().signed_duration_since(self.cached_at)
>= Duration::seconds(CAMPAIGN_INFO_CACHE_STALE_SECONDS)
}
}
#[derive(Debug, Deserialize)]
struct TiltifyCampaignResponse {
@@ -305,66 +314,89 @@ pub async fn pride_26(
.await
.wrap_internal_err("connecting to redis")?;
if let Some(cached) = redis_connection
.get(CAMPAIGN_INFO_CACHE_NAMESPACE, campaign_id)
.await
.wrap_internal_err("getting cached campaign info")?
{
let campaign_info = serde_json::from_str::<CampaignInfo>(&cached)
.wrap_internal_err("parsing cached campaign info")?;
return Ok(web::Json(campaign_info));
}
let access_token = tiltify
.access_token()
.await
.wrap_internal_err("fetching Tiltify access token")?;
let url = format!(
"https://v5api.tiltify.com/api/public/team_campaigns/{campaign_id}",
);
let response = http
.get(url)
.bearer_auth(&access_token)
.send()
.await
.wrap_internal_err("fetching campaign from Tiltify")?
.error_for_status()
.wrap_internal_err("fetching campaign from Tiltify")?
.json::<TiltifyCampaignResponse>()
.await
.wrap_internal_err("parsing Tiltify response")?;
let raised_currency = &response.data.total_amount_raised.currency;
if raised_currency != "USD" {
return Err(ApiError::Internal(eyre!(
"total amount raised is in {raised_currency}, must be USD"
)));
}
let goal_currency = &response.data.goal.currency;
if goal_currency != "USD" {
return Err(ApiError::Internal(eyre!(
"goal amount is in {goal_currency}, must be USD"
)));
}
let campaign_info = CampaignInfo {
total_donations_usd: response.data.total_amount_raised.value,
target_usd: response.data.goal.value,
num_donators: num_donators(&http, &access_token, campaign_id).await?,
};
redis_connection
.set_serialized_to_json(
let cached = redis_connection
.get_deserialized_from_json::<CampaignInfo>(
CAMPAIGN_INFO_CACHE_NAMESPACE,
campaign_id,
&campaign_info,
Some(CAMPAIGN_INFO_CACHE_TTL_SECONDS),
)
.await
.wrap_internal_err("caching campaign info")?;
.wrap_internal_err("getting cached campaign info")?;
Ok(web::Json(campaign_info))
if let Some(cached) = &cached
&& !cached.is_stale()
{
return Ok(web::Json(cached.clone()));
}
let result = async {
let access_token = tiltify
.access_token()
.await
.wrap_internal_err("fetching Tiltify access token")?;
let url = format!(
"https://v5api.tiltify.com/api/public/team_campaigns/{campaign_id}",
);
let response = http
.get(url)
.bearer_auth(&access_token)
.send()
.await
.wrap_internal_err("fetching campaign from Tiltify")?
.error_for_status()
.wrap_internal_err("fetching campaign from Tiltify")?
.json::<TiltifyCampaignResponse>()
.await
.wrap_internal_err("parsing Tiltify response")?;
let raised_currency = &response.data.total_amount_raised.currency;
if raised_currency != "USD" {
return Err(ApiError::Internal(eyre!(
"total amount raised is in {raised_currency}, must be USD"
)));
}
let goal_currency = &response.data.goal.currency;
if goal_currency != "USD" {
return Err(ApiError::Internal(eyre!(
"goal amount is in {goal_currency}, must be USD"
)));
}
let campaign_info = CampaignInfo {
total_donations_usd: response.data.total_amount_raised.value,
target_usd: response.data.goal.value,
num_donators: num_donators(&http, &access_token, campaign_id)
.await?,
cached_at: Utc::now(),
};
redis_connection
.set_serialized_to_json(
CAMPAIGN_INFO_CACHE_NAMESPACE,
campaign_id,
&campaign_info,
Some(CAMPAIGN_INFO_CACHE_TTL_SECONDS),
)
.await
.wrap_internal_err("caching campaign info")?;
Ok(campaign_info)
}
.await;
match result {
Ok(campaign_info) => Ok(web::Json(campaign_info)),
Err(error) => {
if let Some(cached) = cached {
warn!(
"Failed to refresh campaign info from Tiltify: {error:?}"
);
Ok(web::Json(cached))
} else {
Err(error)
}
}
}
}
async fn num_donators(
+2 -2
View File
@@ -184,14 +184,14 @@ pub async fn projects_list(
#[derive(Serialize, Deserialize, Validate, utoipa::ToSchema)]
pub struct EditUser {
#[validate(length(min = 1, max = 39), regex(path = *crate::util::validate::RE_USERNAME))]
#[validate(length(min = 1, max = 39), regex(path = *crate::util::validate::RE_URL_SAFE))]
pub username: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option"
)]
#[validate(length(min = 1, max = 64), regex(path = *crate::util::validate::RE_USERNAME))]
#[validate(length(min = 1, max = 64), regex(path = *crate::util::validate::RE_URL_SAFE))]
pub name: Option<Option<String>>,
#[serde(
default,
@@ -36,7 +36,7 @@ pub struct InitialVersionData {
pub file_parts: Vec<String>,
#[validate(
length(min = 1, max = 32),
regex(path = *crate::util::validate::RE_URL_SAFE)
regex(path = *crate::util::validate::RE_URL_SAFE_RELAXED)
)]
pub version_number: String,
#[validate(
+1 -1
View File
@@ -314,7 +314,7 @@ pub struct EditVersion {
pub name: Option<String>,
#[validate(
length(min = 1, max = 32),
regex(path = *crate::util::validate::RE_URL_SAFE)
regex(path = *crate::util::validate::RE_URL_SAFE_RELAXED)
)]
pub version_number: Option<String>,
#[validate(length(max = 65536))]
@@ -1,6 +1,5 @@
use futures::StreamExt;
use serde::{Deserialize, Serialize};
use sqlx::Row;
use crate::{
database::{
@@ -59,16 +58,17 @@ pub(crate) async fn fetch(
.iter()
.map(|id| DBAffiliateCodeId::from(*id).0)
.collect::<Vec<_>>();
let mut rows = sqlx::query(
"SELECT
let mut rows = sqlx::query!(
r#"
SELECT
WIDTH_BUCKET(
EXTRACT(EPOCH FROM usa.created_at)::bigint,
EXTRACT(EPOCH FROM $1::timestamp with time zone AT TIME ZONE 'UTC')::bigint,
EXTRACT(EPOCH FROM $2::timestamp with time zone AT TIME ZONE 'UTC')::bigint,
$3::integer
) AS bucket,
CASE WHEN $5 THEN affiliate_code ELSE 0 END AS affiliate_code,
COUNT(*) AS conversions
) AS "bucket?",
CASE WHEN $5 THEN affiliate_code ELSE 0 END AS "affiliate_code?",
COUNT(*) AS "conversions?"
FROM users_subscriptions_affiliations usa
INNER JOIN affiliate_codes ac ON ac.id = usa.affiliate_code
INNER JOIN users_subscriptions us ON us.id = usa.subscription_id
@@ -79,22 +79,21 @@ pub(crate) async fn fetch(
AND usa.created_at < $2
AND c.status = 'succeeded'
AND (cardinality($6::bigint[]) = 0 OR affiliate_code = ANY($6))
GROUP BY bucket, affiliate_code",
)
.bind(req.time_range.start)
.bind(req.time_range.end)
.bind(num_time_slices as i64)
.bind(user_id as DBUserId)
.bind(
GROUP BY 1, 2
"#,
req.time_range.start,
req.time_range.end,
num_time_slices as i64,
user_id as DBUserId,
metrics
.bucket_by
.contains(&AffiliateCodeConversionsField::AffiliateCodeId),
&filter_affiliate_code_ids,
)
.bind(&filter_affiliate_code_ids)
.fetch(pool);
while let Some(row) = rows.next().await.transpose()? {
let bucket = row
.try_get::<Option<i32>, _>("bucket")?
.bucket
.wrap_internal_err("bucket should be non-null - query bug!")?;
let bucket = usize::try_from(bucket).wrap_internal_err_with(|| {
eyre::eyre!(
@@ -102,12 +101,10 @@ pub(crate) async fn fetch(
)
})?;
let affiliate_code = row.try_get::<Option<i64>, _>("affiliate_code")?;
let conversion_count = row.try_get::<Option<i64>, _>("conversions")?;
let source_affiliate_code = AffiliateCodeId::from(DBAffiliateCodeId(
affiliate_code.unwrap_or_default(),
row.affiliate_code.unwrap_or_default(),
));
let conversions = u64::try_from(conversion_count.unwrap_or_default())
let conversions = u64::try_from(row.conversions.unwrap_or_default())
.unwrap_or(u64::MAX);
add_to_time_slice(
@@ -1,7 +1,6 @@
use futures::StreamExt;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use sqlx::Row;
use crate::{
database::{
@@ -57,16 +56,17 @@ pub(crate) async fn fetch(
.iter()
.map(|id| DBAffiliateCodeId::from(*id).0)
.collect::<Vec<_>>();
let mut rows = sqlx::query(
"SELECT
let mut rows = sqlx::query!(
r#"
SELECT
WIDTH_BUCKET(
EXTRACT(EPOCH FROM created)::bigint,
EXTRACT(EPOCH FROM $1::timestamp with time zone AT TIME ZONE 'UTC')::bigint,
EXTRACT(EPOCH FROM $2::timestamp with time zone AT TIME ZONE 'UTC')::bigint,
$3::integer
) AS bucket,
CASE WHEN $5 THEN affiliate_code_source ELSE 0 END AS affiliate_code_source,
SUM(amount) amount_sum
) AS "bucket?",
CASE WHEN $5 THEN affiliate_code_source ELSE 0 END AS "affiliate_code_source?",
SUM(amount) AS "amount_sum?"
FROM payouts_values
WHERE
user_id = $4
@@ -74,22 +74,21 @@ pub(crate) async fn fetch(
AND created >= $1
AND created < $2
AND (cardinality($6::bigint[]) = 0 OR affiliate_code_source = ANY($6))
GROUP BY bucket, affiliate_code_source",
)
.bind(req.time_range.start)
.bind(req.time_range.end)
.bind(num_time_slices as i64)
.bind(user_id as DBUserId)
.bind(
GROUP BY 1, 2
"#,
req.time_range.start,
req.time_range.end,
num_time_slices as i64,
user_id as DBUserId,
metrics
.bucket_by
.contains(&AffiliateCodeRevenueField::AffiliateCodeId),
&filter_affiliate_code_ids,
)
.bind(&filter_affiliate_code_ids)
.fetch(pool);
while let Some(row) = rows.next().await.transpose()? {
let bucket = row
.try_get::<Option<i32>, _>("bucket")?
.bucket
.wrap_internal_err("bucket should be non-null - query bug!")?;
let bucket = usize::try_from(bucket).wrap_internal_err_with(|| {
eyre::eyre!(
@@ -97,14 +96,10 @@ pub(crate) async fn fetch(
)
})?;
let affiliate_code_source =
row.try_get::<Option<i64>, _>("affiliate_code_source")?;
let source_affiliate_code = AffiliateCodeId::from(DBAffiliateCodeId(
affiliate_code_source.unwrap_or_default(),
row.affiliate_code_source.unwrap_or_default(),
));
let revenue = row
.try_get::<Option<Decimal>, _>("amount_sum")?
.unwrap_or_default();
let revenue = row.amount_sum.unwrap_or_default();
add_to_time_slice(
time_slices,
@@ -581,6 +581,7 @@ static DOWNLOAD_SOURCE_PATTERNS: LazyLock<Vec<(Regex, DownloadSourcePattern)>> =
(r"nothub/mrpack-install", P::Named("mrpack-install")),
(r"^(packwiz-installer|packwiz/)", P::Named("Packwiz")),
(r"^mrpack4server", P::Named("mrpack4server")),
(r"^DawnLauncher/", P::Named("Dawn")),
(
r"^(Mozilla/|Chrome/|Chromium/|Firefox/|Safari/|AppleWebKit/|Edg/|OPR/)",
P::Website,
@@ -1,17 +1,21 @@
use futures::StreamExt;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use sqlx::Row;
use ariadne::ids::UserId;
use crate::{
database::{PgPool, models::DBProjectId},
database::{
PgPool,
models::{DBProjectId, DBUserId},
},
models::ids::ProjectId,
routes::ApiError,
util::error::Context,
};
use super::super::{TimeSlice, add_to_time_slice};
use super::{AnalyticsData, ProjectAnalytics, ProjectMetrics};
use super::{AnalyticsData, Metrics, ProjectAnalytics, ProjectMetrics};
/// Fields for [`super::ReturnMetrics::project_revenue`].
#[derive(
@@ -21,6 +25,13 @@ use super::{AnalyticsData, ProjectAnalytics, ProjectMetrics};
pub enum ProjectRevenueField {
/// Project ID.
ProjectId,
/// User ID.
///
/// You can only bucket by user if you are a member on the project.
/// If you are a member of the parent organization (and have view analytics
/// permissions), but not a member of the project, you cannot bucket by
/// user.
UserId,
}
/// Filters for [`super::ReturnMetrics::project_revenue`].
@@ -30,6 +41,9 @@ pub struct ProjectRevenueFilters {}
/// [`super::ReturnMetrics::project_revenue`].
#[derive(Debug, Clone, Default, Serialize, Deserialize, utoipa::ToSchema)]
pub struct ProjectRevenue {
/// User these metrics are for.
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<UserId>,
/// Total revenue for this bucket.
pub(crate) revenue: Decimal,
}
@@ -40,17 +54,28 @@ pub(crate) async fn fetch(
req: &super::super::GetRequest,
num_time_slices: usize,
project_id_values: &[i64],
user_id_bucket_project_ids: &[i64],
can_view_all_revenue_splits: bool,
metrics: &Metrics<ProjectRevenueField, ProjectRevenueFilters>,
) -> Result<(), ApiError> {
let mut rows = sqlx::query(
"SELECT
let bucket_by_user_id =
metrics.bucket_by.contains(&ProjectRevenueField::UserId);
let mut rows = sqlx::query!(
r#"
SELECT
WIDTH_BUCKET(
EXTRACT(EPOCH FROM created)::bigint,
EXTRACT(EPOCH FROM $1::timestamp with time zone AT TIME ZONE 'UTC')::bigint,
EXTRACT(EPOCH FROM $2::timestamp with time zone AT TIME ZONE 'UTC')::bigint,
$3::integer
) AS bucket,
mod_id,
SUM(amount) amount_sum
) AS "bucket?",
mod_id AS "mod_id?",
CASE
WHEN $5 AND ($6 OR mod_id = ANY($7)) THEN user_id
ELSE 0
END AS "user_id?",
SUM(amount) AS "amount_sum?"
FROM payouts_values
WHERE
-- only project revenue is counted here
@@ -59,16 +84,20 @@ pub(crate) async fn fetch(
AND payouts_values.mod_id = ANY($4)
AND created >= $1
AND created < $2
GROUP BY bucket, mod_id",
GROUP BY 1, 2, 3
"#,
req.time_range.start,
req.time_range.end,
num_time_slices as i64,
project_id_values,
bucket_by_user_id,
can_view_all_revenue_splits,
user_id_bucket_project_ids,
)
.bind(req.time_range.start)
.bind(req.time_range.end)
.bind(num_time_slices as i64)
.bind(project_id_values)
.fetch(pool);
while let Some(row) = rows.next().await.transpose()? {
let bucket = row
.try_get::<Option<i32>, _>("bucket")?
.bucket
.wrap_internal_err("bucket should be non-null - query bug!")?;
let bucket = usize::try_from(bucket).wrap_internal_err_with(|| {
eyre::eyre!(
@@ -76,11 +105,9 @@ pub(crate) async fn fetch(
)
})?;
let mod_id = row.try_get::<Option<i64>, _>("mod_id")?;
let amount_sum = row.try_get::<Option<Decimal>, _>("amount_sum")?;
if let Some(source_project) =
mod_id.map(DBProjectId).map(ProjectId::from)
&& let Some(revenue) = amount_sum
row.mod_id.map(DBProjectId).map(ProjectId::from)
&& let Some(revenue) = row.amount_sum
{
add_to_time_slice(
time_slices,
@@ -88,6 +115,11 @@ pub(crate) async fn fetch(
AnalyticsData::Project(ProjectAnalytics {
source_project,
metrics: ProjectMetrics::Revenue(ProjectRevenue {
user_id: row
.user_id
.filter(|id| bucket_by_user_id && *id != 0)
.map(DBUserId)
.map(UserId::from),
revenue,
}),
}),
@@ -16,7 +16,7 @@ use std::{
num::NonZeroU64,
};
use crate::database::PgPool;
use crate::database::{PgPool, models::DBUserId};
use actix_web::{HttpRequest, post, web};
use chrono::{DateTime, TimeDelta, Utc};
use eyre::eyre;
@@ -355,17 +355,39 @@ pub async fn fetch_analytics(
.await?;
}
if req.return_metrics.project_revenue.is_some() {
if let Some(metrics) = &req.return_metrics.project_revenue {
if !scopes.contains(Scopes::PAYOUTS_READ) {
return Err(AuthenticationError::InvalidCredentials.into());
}
let user_id_bucket_project_ids = sqlx::query!(
"
SELECT m.id
FROM mods m
INNER JOIN team_members tm ON tm.team_id = m.team_id
WHERE
m.id = ANY($1)
AND tm.user_id = $2
AND tm.accepted
",
&project_id_values,
DBUserId::from(user.id).0,
)
.fetch_all(&**pool)
.await?
.into_iter()
.map(|row| row.id)
.collect::<Vec<_>>();
metrics::fetch_project_revenue(
&pool,
&mut time_slices,
&req,
num_time_slices,
&project_id_values,
&user_id_bucket_project_ids,
user.role.is_mod(),
metrics,
)
.await?;
}
@@ -965,6 +987,7 @@ mod tests {
TimeSlice(vec![AnalyticsData::Project(ProjectAnalytics {
source_project: test_project_3,
metrics: ProjectMetrics::Revenue(ProjectRevenue {
user_id: None,
revenue: Decimal::new(20000, 2),
}),
})]),
+23 -3
View File
@@ -732,9 +732,9 @@ pub async fn organization_delete(
// Handle projects- every project that is in this organization needs to have its owner changed the organization owner
// Now, no project should have an owner if it is in an organization, and also
// the owner of an organization should not be a team member in any project
let organization_project_teams = sqlx::query!(
let organization_projects = sqlx::query!(
"
SELECT t.id FROM organizations o
SELECT t.id team_id, m.id project_id FROM organizations o
INNER JOIN mods m ON m.organization_id = o.id
INNER JOIN teams t ON t.id = m.team_id
WHERE o.id = $1 AND $1 IS NOT NULL
@@ -742,9 +742,22 @@ pub async fn organization_delete(
organization.id as database::models::ids::DBOrganizationId
)
.fetch(&mut transaction)
.map_ok(|c| database::models::DBTeamId(c.id))
.map_ok(|c| {
(
database::models::DBTeamId(c.team_id),
database::models::DBProjectId(c.project_id),
)
})
.try_collect::<Vec<_>>()
.await?;
let organization_project_teams = organization_projects
.iter()
.map(|(team_id, _)| *team_id)
.collect::<Vec<_>>();
let organization_project_ids = organization_projects
.iter()
.map(|(_, project_id)| *project_id)
.collect::<Vec<_>>();
for organization_project_team in &organization_project_teams {
let new_id = crate::database::models::ids::generate_team_member_id(
@@ -786,6 +799,13 @@ pub async fn organization_delete(
database::models::DBTeamMember::clear_cache(*team_id, &redis).await?;
}
for project_id in organization_project_ids {
database::models::DBProject::clear_cache(
project_id, None, None, &redis,
)
.await?;
}
if !organization_project_teams.is_empty() {
database::models::DBUser::clear_project_cache(&[owner_id], &redis)
.await?;
@@ -101,6 +101,7 @@ impl ResponseError for CreateError {
#[derive(Debug, Clone, Serialize, Deserialize, Validate, utoipa::ToSchema)]
pub struct ProjectCreate {
#[validate(nested)]
pub base: exp::base::Project,
#[serde(flatten)]
#[validate(nested)]
@@ -338,3 +339,59 @@ pub async fn create(
Ok(web::Json(project_id))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::projects::ProjectStatus;
fn project_create_with_slug(slug: &str) -> ProjectCreate {
ProjectCreate {
base: exp::base::Project {
name: "test project".into(),
slug: slug.into(),
summary: "test summary".into(),
description: String::new(),
requested_status: ProjectStatus::Approved,
organization_id: None,
},
components: exp::ProjectEdit {
minecraft_mod: None,
minecraft_server: None,
minecraft_java_server: None,
minecraft_bedrock_server: None,
},
}
}
fn assert_project_slug_validation(slug: &str, expected_valid: bool) {
let result = project_create_with_slug(slug).validate();
assert_eq!(
result.is_ok(),
expected_valid,
"unexpected validation result for slug `{slug}`"
);
}
#[test]
fn project_create_accepts_url_safe_base_slugs() {
for slug in ["valid-slug", "valid_slug", "valid.slug", "valid123"] {
assert_project_slug_validation(slug, true);
}
}
#[test]
fn project_create_rejects_unsafe_base_slugs() {
for slug in [
"invalid/slug",
"../invalid",
r#"invalid"slug"#,
"invalid$slug",
"invalid slug",
"invalid#slug",
] {
assert_project_slug_validation(slug, false);
}
}
}
+1 -1
View File
@@ -642,7 +642,7 @@ pub async fn orgs_list(
#[derive(Serialize, Deserialize, Validate)]
pub struct EditUser {
#[validate(length(min = 1, max = 39), regex(path = *crate::util::validate::RE_USERNAME))]
#[validate(length(min = 1, max = 39), regex(path = *crate::util::validate::RE_URL_SAFE))]
pub username: Option<String>,
#[serde(
default,
@@ -56,7 +56,7 @@ pub struct InitialVersionData {
pub file_parts: Vec<String>,
#[validate(
length(min = 1, max = 32),
regex(path = *crate::util::validate::RE_URL_SAFE)
regex(path = *crate::util::validate::RE_URL_SAFE_RELAXED)
)]
pub version_number: String,
#[validate(
+1 -1
View File
@@ -222,7 +222,7 @@ pub struct EditVersion {
pub name: Option<String>,
#[validate(
length(min = 1, max = 32),
regex(path = *crate::util::validate::RE_URL_SAFE)
regex(path = *crate::util::validate::RE_URL_SAFE_RELAXED)
)]
pub version_number: Option<String>,
#[validate(length(max = 65536))]
@@ -359,6 +359,7 @@ pub struct TypesenseFieldSpec {
pub facet: bool,
pub sort: bool,
pub optional: bool,
pub token_separators: Option<&'static [&'static str]>,
}
impl SearchField {
@@ -370,6 +371,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::Name => TypesenseFieldSpec {
path: "name",
@@ -377,6 +379,7 @@ impl SearchField {
facet: true,
sort: false,
optional: false,
token_separators: None,
},
SearchField::Author => TypesenseFieldSpec {
path: "author",
@@ -384,6 +387,7 @@ impl SearchField {
facet: true,
sort: false,
optional: false,
token_separators: None,
},
SearchField::License => TypesenseFieldSpec {
path: "license",
@@ -391,6 +395,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::ProjectTypes => TypesenseFieldSpec {
path: "project_types",
@@ -398,6 +403,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::ProjectId => TypesenseFieldSpec {
path: "project_id",
@@ -405,6 +411,7 @@ impl SearchField {
facet: true,
sort: false,
optional: false,
token_separators: None,
},
SearchField::OpenSource => TypesenseFieldSpec {
path: "open_source",
@@ -412,6 +419,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::Environment => TypesenseFieldSpec {
path: "environment",
@@ -419,6 +427,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::GameVersions => TypesenseFieldSpec {
path: "game_versions",
@@ -426,6 +435,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: Some(&["-", "."]),
},
SearchField::ClientSide => TypesenseFieldSpec {
path: "client_side",
@@ -433,6 +443,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::ServerSide => TypesenseFieldSpec {
path: "server_side",
@@ -440,6 +451,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::MinecraftServerRegion => TypesenseFieldSpec {
path: "minecraft_server.region",
@@ -447,6 +459,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::MinecraftServerLanguages => TypesenseFieldSpec {
path: "minecraft_server.languages",
@@ -454,6 +467,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::MinecraftJavaServerContentKind => TypesenseFieldSpec {
path: "minecraft_java_server.content.kind",
@@ -461,6 +475,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::MinecraftJavaServerContentSupportedGameVersions => {
TypesenseFieldSpec {
@@ -469,6 +484,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: Some(&["-", "."]),
}
}
SearchField::MinecraftJavaServerPingData => TypesenseFieldSpec {
@@ -477,6 +493,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::DependencyProjectIds => TypesenseFieldSpec {
path: "dependency_project_ids",
@@ -484,6 +501,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
SearchField::CompatibleDependencyProjectIds => TypesenseFieldSpec {
path: "compatible_dependency_project_ids",
@@ -491,6 +509,7 @@ impl SearchField {
facet: true,
sort: false,
optional: true,
token_separators: None,
},
}
}
@@ -515,6 +534,19 @@ static TYPESENSE_SEARCH_FIELDS: LazyLock<Vec<Value>> = LazyLock::new(|| {
if spec.optional {
obj.insert("optional".to_string(), Value::Bool(true));
}
if let Some(token_separators) = spec.token_separators {
obj.insert(
"token_separators".to_string(),
Value::Array(
token_separators
.iter()
.map(|separator| {
Value::String((*separator).to_string())
})
.collect(),
),
);
}
Value::Object(obj)
})
.collect()
@@ -547,7 +579,6 @@ impl Typesense {
json!({
"name": name,
"enable_nested_fields": true,
"token_separators": ["-"],
"fields": fields,
"default_sorting_field": "log_downloads"
})
+57 -24
View File
@@ -1,6 +1,6 @@
use std::time::{Duration, Instant};
use eyre::eyre;
use eyre::{Result, eyre};
use serde::Deserialize;
use serde_json::json;
use tokio::sync::Mutex;
@@ -13,7 +13,24 @@ use crate::{
#[derive(Debug)]
pub struct TiltifyClient {
http: HttpClient,
token: Mutex<Option<TiltifyAccessToken>>,
state: Mutex<TiltifyState>,
}
#[derive(Debug)]
struct TiltifyState {
token: Option<TiltifyAccessToken>,
rate_limited_until: Instant,
rate_limit_backoff: Duration,
}
impl TiltifyState {
fn set_fetch_backoff(&mut self) {
self.rate_limited_until = Instant::now() + self.rate_limit_backoff;
self.rate_limit_backoff = self
.rate_limit_backoff
.saturating_mul(2)
.min(TILTIFY_MAX_RATE_LIMIT_BACKOFF);
}
}
#[derive(Debug)]
@@ -28,38 +45,34 @@ struct TiltifyTokenResponse {
expires_in: u64,
}
const TILTIFY_INITIAL_RATE_LIMIT_BACKOFF: Duration = Duration::from_secs(60);
const TILTIFY_MAX_RATE_LIMIT_BACKOFF: Duration = Duration::from_secs(15 * 60);
impl TiltifyClient {
pub fn new(http: HttpClient) -> Self {
Self {
http,
token: Mutex::new(None),
state: Mutex::new(TiltifyState {
token: None,
rate_limited_until: Instant::now(),
rate_limit_backoff: TILTIFY_INITIAL_RATE_LIMIT_BACKOFF,
}),
}
}
pub async fn access_token(&self) -> eyre::Result<String> {
let mut token = self.token.lock().await;
pub async fn access_token(&self) -> Result<String> {
let mut state = self.state.lock().await;
if let Some(token) = token.as_ref()
if let Some(token) = state.token.as_ref()
&& token.expires_at > Instant::now()
{
return Ok(token.access_token.clone());
}
let response = self.fetch_access_token().await?;
let expires_at = Instant::now()
+ Duration::from_secs(response.expires_in)
.saturating_sub(Duration::from_secs(60));
let access_token = response.access_token;
if state.rate_limited_until > Instant::now() {
return Err(eyre!("waiting for rate limit to reset"));
}
*token = Some(TiltifyAccessToken {
access_token: access_token.clone(),
expires_at,
});
Ok(access_token)
}
async fn fetch_access_token(&self) -> eyre::Result<TiltifyTokenResponse> {
if ENV.TILTIFY_CLIENT_ID.is_empty()
|| ENV.TILTIFY_CLIENT_SECRET.is_empty()
{
@@ -79,13 +92,33 @@ impl TiltifyClient {
}))
.send()
.await
.wrap_err("fetching OAuth token")?
.error_for_status()
.wrap_err("fetching OAuth token")?
.inspect_err(|_| state.set_fetch_backoff())
.wrap_err("fetching OAuth token")?;
let response = match response.error_for_status() {
Ok(response) => response,
Err(error) => {
state.set_fetch_backoff();
return Err(error).wrap_err("fetching OAuth token");
}
};
let response = response
.json::<TiltifyTokenResponse>()
.await
.wrap_err("parsing OAuth token response")?;
Ok(response)
let expires_at = Instant::now()
+ Duration::from_secs(response.expires_in)
.saturating_sub(Duration::from_secs(60));
let access_token = response.access_token;
state.token = Some(TiltifyAccessToken {
access_token: access_token.clone(),
expires_at,
});
state.rate_limit_backoff = TILTIFY_INITIAL_RATE_LIMIT_BACKOFF;
Ok(access_token)
}
}
+72 -3
View File
@@ -7,9 +7,12 @@ use validator::{ValidationErrors, ValidationErrorsKind};
use crate::models::pats::Scopes;
pub static RE_URL_SAFE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"^[a-zA-Z0-9!@$()`.+,_"-]*$"#).unwrap());
pub static RE_USERNAME: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"^[a-zA-Z0-9_-]*$"#).unwrap());
LazyLock::new(|| Regex::new(r#"^[a-zA-Z0-9._-]+$"#).unwrap());
// only used for versions
// TODO: percent-encode version names in URLs instead of treating them as slugs
pub static RE_URL_SAFE_RELAXED: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"^[a-zA-Z0-9!@$()`.+,_"-]+$"#).unwrap());
//TODO: In order to ensure readability, only the first error is printed, this may need to be expanded on in the future!
pub fn validation_errors_to_string(
@@ -159,4 +162,70 @@ mod tests {
let result = validate_name(" ");
assert!(result.is_err());
}
fn assert_url_safe_regex(
regex: &LazyLock<Regex>,
value: &str,
expected_valid: bool,
) {
assert_eq!(
regex.is_match(value),
expected_valid,
"unexpected URL-safe validation result for `{value}`"
);
}
fn assert_url_safe_slug(slug: &str, expected_valid: bool) {
assert_url_safe_regex(&RE_URL_SAFE, slug, expected_valid);
}
fn assert_url_safe_version(version: &str, expected_valid: bool) {
assert_url_safe_regex(&RE_URL_SAFE_RELAXED, version, expected_valid);
}
#[test]
fn url_safe_regex_accepts_allowed_slug_punctuation() {
for slug in ["valid-slug", "valid_slug", "valid.slug", "valid123"] {
assert_url_safe_slug(slug, true);
}
}
#[test]
fn url_safe_regex_rejects_unsafe_slug_punctuation() {
for slug in [
"invalid/slug",
"../invalid",
r#"invalid"slug"#,
"invalid$slug",
"invalid slug",
"invalid#slug",
] {
assert_url_safe_slug(slug, false);
}
}
#[test]
fn url_safe_relaxed_regex_accepts_legacy_version_punctuation() {
for version in [
"1.0.0",
"1.0.0+build",
"version$beta",
r#"version"quoted"#,
"version!@$()`.+,_-",
] {
assert_url_safe_version(version, true);
}
}
#[test]
fn url_safe_relaxed_regex_rejects_non_version_safe_punctuation() {
for version in [
"invalid/version",
"../invalid",
"invalid space",
"invalid#version",
] {
assert_url_safe_version(version, false);
}
}
}
+2
View File
@@ -1,3 +1,5 @@
[tools]
node = "24.16.0"
pnpm = "10.33.2"
rust = "1.95.0"
turbo = "2.9.18"
@@ -1,6 +1,7 @@
import { AbstractFeature, type FeatureConfig } from '../core/abstract-feature'
import { ModrinthApiError } from '../core/errors'
import type { RequestContext } from '../types/request'
import { getNodeBaseUrl } from '../utils/node-url'
/**
* Node authentication credentials
@@ -107,7 +108,7 @@ export class NodeAuthFeature extends AbstractFeature {
}
private applyAuth(context: RequestContext, auth: NodeAuth): void {
const baseUrl = `https://${auth.url.replace(/\/modrinth\/v\d+\/fs\/?$/, '')}`
const baseUrl = getNodeBaseUrl(auth.url)
context.url = this.buildUrl(context.path, baseUrl, context.options.version)
context.options.headers = {
+2
View File
@@ -43,6 +43,8 @@ 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 { getNodeWebSocketUrl } from './utils/node-url'
export { pingWebSocketUrl, type WebSocketPingOptions } from './utils/pingtest'
export {
type ParsedSseEvent,
type ParsedSseItem,
@@ -1,5 +1,6 @@
import { AbstractModule } from '../../../core/abstract-module'
import type { UploadHandle, UploadProgress } from '../../../types/upload'
import { getNodeBaseUrl } from '../../../utils/node-url'
import type { Archon } from '../../archon/types'
import type { Kyros } from '../types'
@@ -11,7 +12,7 @@ export class KyrosFilesV0Module extends AbstractModule {
}
private getNodeBaseUrl(auth: NodeFsAuth): string {
return `https://${auth.url.replace(/\/modrinth\/v\d+\/fs\/?$/, '')}`
return getNodeBaseUrl(auth.url)
}
/**
@@ -2,6 +2,7 @@ import mitt from 'mitt'
import { AbstractWebSocketClient, type WebSocketConnection } from '../core/abstract-websocket'
import type { Archon } from '../modules/archon/types'
import { getNodeWebSocketUrl } from '../utils/node-url'
type WSEventMap = {
[K in Archon.Websocket.v0.WSEvent as `${string}:${K['event']}`]: K
@@ -19,7 +20,7 @@ export class GenericWebSocketClient extends AbstractWebSocketClient {
return new Promise((resolve, reject) => {
try {
const ws = new WebSocket(`wss://${auth.url}`)
const ws = new WebSocket(getNodeWebSocketUrl(auth.url))
const connection: WebSocketConnection = {
serverId,
+18
View File
@@ -0,0 +1,18 @@
const NODE_FS_PATH_REGEX = /\/modrinth\/v\d+\/fs\/?$/
const HTTP_SCHEME_REGEX = /^https?:\/\//i
const WS_SCHEME_REGEX = /^wss?:\/\//i
const HTTP_SECURE_SCHEME_REGEX = /^https:\/\//i
const HTTP_INSECURE_SCHEME_REGEX = /^http:\/\//i
export function getNodeBaseUrl(url: string): string {
const baseUrl = url.replace(NODE_FS_PATH_REGEX, '')
return HTTP_SCHEME_REGEX.test(baseUrl) ? baseUrl : `https://${baseUrl}`
}
export function getNodeWebSocketUrl(url: string): string {
if (WS_SCHEME_REGEX.test(url)) return url
if (HTTP_SECURE_SCHEME_REGEX.test(url)) return url.replace(HTTP_SECURE_SCHEME_REGEX, 'wss://')
if (HTTP_INSECURE_SCHEME_REGEX.test(url)) return url.replace(HTTP_INSECURE_SCHEME_REGEX, 'ws://')
return `wss://${url}`
}
+97
View File
@@ -0,0 +1,97 @@
export interface WebSocketPingOptions {
count?: number
intervalMs?: number
settleDelayMs?: number
timeoutMs?: number
signal?: AbortSignal
}
export async function pingWebSocketUrl(
url: string,
options: WebSocketPingOptions = {},
): Promise<number> {
const count = options.count ?? 5
const intervalMs = options.intervalMs ?? 200
const settleDelayMs = options.settleDelayMs ?? 1000
const timeoutMs = options.timeoutMs ?? count * intervalMs + settleDelayMs + 1000
if (options.signal?.aborted) return -1
return await new Promise<number>((resolve) => {
const samples: number[] = []
const timers = new Set<ReturnType<typeof setTimeout>>()
let socket: WebSocket | undefined
let settled = false
const setTrackedTimeout = (callback: () => void, ms: number) => {
const timer = setTimeout(() => {
timers.delete(timer)
callback()
}, ms)
timers.add(timer)
return timer
}
const cleanup = () => {
for (const timer of timers) clearTimeout(timer)
timers.clear()
options.signal?.removeEventListener('abort', abort)
if (
socket &&
(socket.readyState === WebSocket.CONNECTING || socket.readyState === WebSocket.OPEN)
) {
socket.close()
}
}
const finish = (ping: number) => {
if (settled) return
settled = true
cleanup()
resolve(ping)
}
const abort = () => finish(-1)
options.signal?.addEventListener('abort', abort, { once: true })
try {
socket = new WebSocket(url)
} catch {
finish(-1)
return
}
setTrackedTimeout(() => finish(-1), timeoutMs)
socket.onopen = () => {
for (let i = 0; i < count; i++) {
setTrackedTimeout(() => {
if (socket?.readyState === WebSocket.OPEN) {
socket.send(String(performance.now()))
}
}, i * intervalMs)
}
setTrackedTimeout(
() => {
const ping =
samples.length > 0
? Math.round([...samples].sort((a, b) => a - b)[Math.floor(samples.length / 2)])
: -1
finish(ping)
},
count * intervalMs + settleDelayMs,
)
}
socket.onmessage = (event) => {
samples.push(performance.now() - Number(event.data))
}
socket.onerror = () => finish(-1)
socket.onclose = () => {
if (!settled && samples.length === 0) finish(-1)
}
})
}
@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT COUNT(*) AS \"count!: i64\" FROM sqlite_master WHERE type = 'table' AND name IN ('custom_minecraft_skins', 'minecraft_users')",
"describe": {
"columns": [
{
"name": "count!: i64",
"ordinal": 0,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false
]
},
"hash": "01d62695f1845e5258d7e39fd80a61cff39b759f832b89e6491d29dc51c18714"
}
+68 -37
View File
@@ -4,11 +4,12 @@ use tokio::fs::File as AsyncFile;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;
const EMPTY_OPEN_ARGS: &[&str] = &[];
pub(crate) async fn get_resource(
download_url: &str,
local_filename: &str,
os_type: &str,
auto_update_supported: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let download_dir = dirs::download_dir()
.ok_or("[AR] • Failed to determine download directory")?;
@@ -20,23 +21,23 @@ pub(crate) async fn get_resource(
dest_file.write_all(&bytes).await?;
tracing::info!("[AR] • File downloaded to: {:?}", full_path);
if auto_update_supported {
let result = match os_type.to_lowercase().as_str() {
"windows" => handle_windows_file(&full_path).await,
"macos" => open_macos_file(&full_path).await,
_ => open_default(&full_path).await,
};
let result = match os_type.to_lowercase().as_str() {
"windows" => handle_windows_file(&full_path).await,
"macos" => open_macos_file(&full_path).await,
_ => open_default(&full_path).await,
};
match result {
Ok(_) => tracing::info!("[AR] • File opened successfully!"),
Err(e) => tracing::info!("[AR] • Failed to open file: {e}"),
}
match result {
Ok(_) => tracing::info!("[AR] • File opened successfully!"),
Err(e) => tracing::info!("[AR] • Failed to open file: {e}"),
}
Ok(())
}
async fn handle_windows_file(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
async fn handle_windows_file(
path: &PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
let filename = path
.file_name()
.and_then(|f| f.to_str())
@@ -51,7 +52,9 @@ async fn handle_windows_file(path: &PathBuf) -> Result<(), Box<dyn std::error::E
}
}
async fn run_windows_installer(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
async fn run_windows_installer(
path: &PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
let installer_path = path.to_str().unwrap_or_default();
let status = if installer_path.ends_with(".msi") {
@@ -76,21 +79,15 @@ async fn run_windows_installer(path: &PathBuf) -> Result<(), Box<dyn std::error:
}
}
async fn open_windows_folder(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
let folder = path.parent().unwrap_or(path);
let status = Command::new("explorer")
.arg(folder.display().to_string())
.status()
.await?;
if !status.success() {
Err(format!("Exit code: {:?}", status.code()).into())
} else {
Ok(())
}
async fn open_windows_folder(
path: &PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
open_in_file_manager(path, &[("explorer", EMPTY_OPEN_ARGS)]).await
}
async fn open_macos_file(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
async fn open_macos_file(
path: &PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
let status = Command::new("open")
.arg(path.to_str().unwrap_or_default())
.status()
@@ -103,15 +100,49 @@ async fn open_macos_file(path: &PathBuf) -> Result<(), Box<dyn std::error::Error
}
}
async fn open_default(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
let status = Command::new(".")
.arg(path.to_str().unwrap_or_default())
.status()
.await?;
if !status.success() {
Err(format!("Exit code: {:?}", status.code()).into())
} else {
Ok(())
}
async fn open_default(
path: &PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
open_in_file_manager(
path,
&[
("xdg-open", EMPTY_OPEN_ARGS),
("gio", &["open"]),
("kioclient5", &["exec"]),
("kioclient", &["exec"]),
("kde-open", EMPTY_OPEN_ARGS),
],
)
.await
}
async fn open_in_file_manager(
path: &PathBuf,
commands: &[(&str, &[&str])],
) -> Result<(), Box<dyn std::error::Error>> {
let folder = path.parent().unwrap_or(path);
let folder_path = folder.to_str().unwrap_or_default();
let mut last_error = None;
for (command, args) in commands {
let mut process = Command::new(command);
process.args(*args).arg(folder_path);
match process.status().await {
Ok(status) if status.success() => return Ok(()),
Ok(status) => {
last_error = Some(format!(
"{command} exited with code {:?}",
status.code()
));
}
Err(error) => {
last_error = Some(format!("{command} failed: {error}"));
}
}
}
Err(last_error
.unwrap_or_else(|| "No file manager command succeeded".to_string())
.into())
}
+26 -8
View File
@@ -6,8 +6,6 @@ use sqlx::{Pool, Sqlite};
use std::str::FromStr;
use std::time::Duration;
static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./migrations");
pub(crate) async fn connect(
app_identifier: &str,
) -> crate::Result<Pool<Sqlite>> {
@@ -34,8 +32,19 @@ pub(crate) async fn connect(
.connect_with(conn_options)
.await?;
MIGRATOR.run(&pool).await?;
stale_data_cleanup(&pool).await?;
if let Err(err) = stale_data_cleanup(&pool).await {
tracing::warn!(
"Failed to clean up stale data from state database before migrations: {err}"
);
}
sqlx::migrate!().run(&pool).await?;
if let Err(err) = stale_data_cleanup(&pool).await {
tracing::warn!(
"Failed to clean up stale data from state database: {err}"
);
}
Ok(pool)
}
@@ -46,11 +55,20 @@ pub(crate) async fn connect(
async fn stale_data_cleanup(pool: &Pool<Sqlite>) -> crate::Result<()> {
let mut tx = pool.begin().await?;
sqlx::query!(
"DELETE FROM custom_minecraft_skins WHERE minecraft_user_uuid NOT IN (SELECT uuid FROM minecraft_users)"
let has_skin_tables = sqlx::query!(
"SELECT COUNT(*) AS \"count!: i64\" FROM sqlite_master WHERE type = 'table' AND name IN ('custom_minecraft_skins', 'minecraft_users')",
)
.execute(&mut *tx)
.await?;
.fetch_one(&mut *tx)
.await?
.count == 2;
if has_skin_tables {
sqlx::query!(
"DELETE FROM custom_minecraft_skins WHERE minecraft_user_uuid NOT IN (SELECT uuid FROM minecraft_users)"
)
.execute(&mut *tx)
.await?;
}
tx.commit().await?;
@@ -215,18 +215,15 @@ pub async fn init_update_launcher(
download_url: &str,
local_filename: &str,
os_type: &str,
auto_update_supported: bool,
) -> Result<()> {
tracing::info!("[AR] • Initialize downloading from • {:?}", download_url);
tracing::info!("[AR] • Save local file name • {:?}", local_filename);
tracing::info!("[AR] • OS type • {}", os_type);
tracing::info!("[AR] • Auto update supported • {}", auto_update_supported);
if let Err(e) = update::get_resource(
download_url,
local_filename,
os_type,
auto_update_supported,
)
.await
{
+29
View File
@@ -10,6 +10,35 @@ export type VersionEntry = {
}
const VERSIONS: VersionEntry[] = [
{
date: `2026-06-19T20:29:45+00:00`,
product: 'app',
version: '0.14.8',
body: `## Changed
- Improved performance of the instance settings modal.
- Updated translations
## Fixed
- Fixed issue in the Content tab where pressing the "Content" button on the linked modpack card would show zero mods in the Modpack content modal for a few seconds.
- Fixed issue with modal close animations.
- Fixed not being able to click scroll bar on "game version" menu when creating new instance.`,
},
{
date: `2026-06-19T20:29:45+00:00`,
product: 'hosting',
body: `## Changed
- Improved performance of the server settings modal.
- Updated translations
## Fixed
- Fixed issue where users were not able to upgrade their Medal servers after the trial expired.`,
},
{
date: `2026-06-19T20:29:45+00:00`,
product: 'web',
body: `## Changed
- Updated translations`,
},
{
date: `2026-06-16T18:58:45+00:00`,
product: 'app',

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