mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-11 22:06:17 +00:00
Compare commits
2 Commits
v3.7.1
...
fix/HYD-86
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3928770ef8 | ||
|
|
de1dfca57e |
@@ -1,5 +1,5 @@
|
||||
MAIN_VITE_API_URL=
|
||||
MAIN_VITE_AUTH_URL=
|
||||
MAIN_VITE_API_URL=API_URL
|
||||
MAIN_VITE_AUTH_URL=AUTH_URL
|
||||
MAIN_VITE_WS_URL=
|
||||
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID=
|
||||
RENDERER_VITE_TORBOX_REFERRAL_CODE=
|
||||
|
||||
40
.github/workflows/build-renderer.yml
vendored
40
.github/workflows/build-renderer.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Build Renderer
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.18.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Build Renderer
|
||||
run: yarn build
|
||||
env:
|
||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||
|
||||
- name: Deploy to Cloudflare Pages
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
run: |
|
||||
npx --yes wrangler@3 pages deploy out/renderer \
|
||||
--project-name="hydra" \
|
||||
--commit-dirty=true \
|
||||
--branch="main"
|
||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-2022, ubuntu-latest]
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.18.3
|
||||
node-version: 20.18.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
|
||||
|
||||
- name: Build Windows
|
||||
if: matrix.os == 'windows-2022'
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: yarn build:win
|
||||
env:
|
||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.18.3
|
||||
node-version: 20.18.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -6,13 +6,13 @@ concurrency:
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-2022, ubuntu-latest]
|
||||
os: [windows-latest, ubuntu-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.18.3
|
||||
node-version: 20.18.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn --frozen-lockfile
|
||||
@@ -57,10 +57,9 @@ jobs:
|
||||
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
|
||||
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
|
||||
MAIN_VITE_RENDERER_URL: ${{ vars.MAIN_VITE_RENDERER_URL }}
|
||||
|
||||
- name: Build Windows
|
||||
if: matrix.os == 'windows-2022'
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: yarn build:win
|
||||
env:
|
||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
||||
@@ -74,7 +73,6 @@ jobs:
|
||||
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
|
||||
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
|
||||
MAIN_VITE_RENDERER_URL: ${{ vars.MAIN_VITE_RENDERER_URL }}
|
||||
|
||||
- name: Create artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from "electron-vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import svgr from "vite-plugin-svgr";
|
||||
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
loadEnv(mode);
|
||||
@@ -47,7 +48,15 @@ export default defineConfig(({ mode }) => {
|
||||
"@shared": resolve("src/shared"),
|
||||
},
|
||||
},
|
||||
plugins: [svgr(), react()],
|
||||
plugins: [
|
||||
svgr(),
|
||||
react(),
|
||||
sentryVitePlugin({
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||
org: "hydra-launcher",
|
||||
project: "hydra-renderer",
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
39
package.json
39
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydralauncher",
|
||||
"version": "3.7.1",
|
||||
"version": "3.6.0",
|
||||
"description": "Hydra",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "Los Broxas",
|
||||
@@ -32,24 +32,19 @@
|
||||
"protoc": "npx protoc --ts_out src/main/generated --proto_path proto proto/*.proto"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^3.0.2",
|
||||
"@electron-toolkit/utils": "^4.0.0",
|
||||
"@fontsource/noto-sans": "^5.2.10",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@electron-toolkit/preload": "^3.0.0",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"@fontsource/noto-sans": "^5.1.0",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@primer/octicons-react": "^19.9.0",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@reduxjs/toolkit": "^2.2.3",
|
||||
"@tiptap/extension-bold": "^3.6.2",
|
||||
"@tiptap/extension-italic": "^3.6.2",
|
||||
"@tiptap/extension-link": "^3.6.2",
|
||||
"@tiptap/extension-underline": "^3.6.2",
|
||||
"@tiptap/react": "^3.6.2",
|
||||
"@tiptap/starter-kit": "^3.6.2",
|
||||
"@sentry/react": "^8.47.0",
|
||||
"@sentry/vite-plugin": "^2.22.7",
|
||||
"auto-launch": "^5.0.6",
|
||||
"axios": "^1.12.2",
|
||||
"axios": "^1.7.9",
|
||||
"axios-cookiejar-support": "^5.0.5",
|
||||
"check-disk-space": "^3.4.0",
|
||||
"classic-level": "^2.0.0",
|
||||
"classnames": "^2.5.1",
|
||||
"color": "^4.2.3",
|
||||
@@ -57,10 +52,10 @@
|
||||
"crc": "^4.3.2",
|
||||
"create-desktop-shortcuts": "^1.11.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"electron-log": "^5.4.3",
|
||||
"dexie": "^4.0.10",
|
||||
"diskusage": "^1.2.0",
|
||||
"electron-log": "^5.2.4",
|
||||
"electron-updater": "^6.6.2",
|
||||
"embla-carousel-autoplay": "^8.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"file-type": "^20.5.0",
|
||||
"framer-motion": "^12.15.0",
|
||||
"i18next": "^23.11.2",
|
||||
@@ -68,11 +63,8 @@
|
||||
"jsdom": "^24.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-react": "^0.544.0",
|
||||
"parse-torrent": "^11.0.18",
|
||||
"rc-virtual-list": "^3.18.3",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-loading-skeleton": "^3.4.0",
|
||||
@@ -86,7 +78,6 @@
|
||||
"tar": "^7.4.3",
|
||||
"tough-cookie": "^5.1.1",
|
||||
"user-agents": "^1.1.387",
|
||||
"uuid": "^13.0.0",
|
||||
"winreg": "^1.2.5",
|
||||
"ws": "^8.18.1",
|
||||
"yaml": "^2.6.1",
|
||||
@@ -116,7 +107,7 @@
|
||||
"@types/winreg": "^1.2.36",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"electron": "^33.4.11",
|
||||
"electron": "^32.3.3",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-vite": "^3.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
@@ -130,8 +121,8 @@
|
||||
"sass-embedded": "^1.80.6",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "5.4.20",
|
||||
"vite-plugin-svgr": "^4.5.0"
|
||||
"vite": "^5.0.12",
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
import aria2p
|
||||
from aria2p.client import ClientException as DownloadNotFound
|
||||
|
||||
class HttpMultiLinkDownloader:
|
||||
def __init__(self):
|
||||
self.downloads = []
|
||||
self.completed_downloads = []
|
||||
self.total_size = None
|
||||
self.aria2 = aria2p.API(
|
||||
aria2p.Client(
|
||||
host="http://localhost",
|
||||
port=6800,
|
||||
secret=""
|
||||
)
|
||||
)
|
||||
|
||||
def start_download(self, urls: list[str], save_path: str, header: str = None, out: str = None, total_size: int = None):
|
||||
"""Add multiple URLs to download queue with same options"""
|
||||
options = {"dir": save_path}
|
||||
if header:
|
||||
options["header"] = header
|
||||
if out:
|
||||
options["out"] = out
|
||||
|
||||
# Clear any existing downloads first
|
||||
self.cancel_download()
|
||||
self.completed_downloads = []
|
||||
self.total_size = total_size
|
||||
|
||||
for url in urls:
|
||||
try:
|
||||
added_downloads = self.aria2.add(url, options=options)
|
||||
self.downloads.extend(added_downloads)
|
||||
except Exception as e:
|
||||
print(f"Error adding download for URL {url}: {str(e)}")
|
||||
|
||||
def pause_download(self):
|
||||
"""Pause all active downloads"""
|
||||
if self.downloads:
|
||||
try:
|
||||
self.aria2.pause(self.downloads)
|
||||
except Exception as e:
|
||||
print(f"Error pausing downloads: {str(e)}")
|
||||
|
||||
def cancel_download(self):
|
||||
"""Cancel and remove all downloads"""
|
||||
if self.downloads:
|
||||
try:
|
||||
# First try to stop the downloads
|
||||
self.aria2.remove(self.downloads)
|
||||
except Exception as e:
|
||||
print(f"Error removing downloads: {str(e)}")
|
||||
finally:
|
||||
# Clear the downloads list regardless of success/failure
|
||||
self.downloads = []
|
||||
self.completed_downloads = []
|
||||
|
||||
def get_download_status(self):
|
||||
"""Get status for all tracked downloads, auto-remove completed/failed ones"""
|
||||
if not self.downloads and not self.completed_downloads:
|
||||
return []
|
||||
|
||||
total_completed = 0
|
||||
current_download_speed = 0
|
||||
active_downloads = []
|
||||
to_remove = []
|
||||
|
||||
# First calculate sizes from completed downloads
|
||||
for completed in self.completed_downloads:
|
||||
total_completed += completed['size']
|
||||
|
||||
# Then check active downloads
|
||||
for download in self.downloads:
|
||||
try:
|
||||
current_download = self.aria2.get_download(download.gid)
|
||||
|
||||
# Skip downloads that are not properly initialized
|
||||
if not current_download or not current_download.files:
|
||||
to_remove.append(download)
|
||||
continue
|
||||
|
||||
# Add to completed size and speed calculations
|
||||
total_completed += current_download.completed_length
|
||||
current_download_speed += current_download.download_speed
|
||||
|
||||
# If download is complete, move it to completed_downloads
|
||||
if current_download.status == 'complete':
|
||||
self.completed_downloads.append({
|
||||
'name': current_download.name,
|
||||
'size': current_download.total_length
|
||||
})
|
||||
to_remove.append(download)
|
||||
else:
|
||||
active_downloads.append({
|
||||
'name': current_download.name,
|
||||
'size': current_download.total_length,
|
||||
'completed': current_download.completed_length,
|
||||
'speed': current_download.download_speed
|
||||
})
|
||||
|
||||
except DownloadNotFound:
|
||||
to_remove.append(download)
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"Error getting download status: {str(e)}")
|
||||
continue
|
||||
|
||||
# Clean up completed/removed downloads from active list
|
||||
for download in to_remove:
|
||||
try:
|
||||
if download in self.downloads:
|
||||
self.downloads.remove(download)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Return aggregate status
|
||||
if self.total_size or active_downloads or self.completed_downloads:
|
||||
# Use the first active download's name as the folder name, or completed if none active
|
||||
folder_name = None
|
||||
if active_downloads:
|
||||
folder_name = active_downloads[0]['name']
|
||||
elif self.completed_downloads:
|
||||
folder_name = self.completed_downloads[0]['name']
|
||||
|
||||
if folder_name and '/' in folder_name:
|
||||
folder_name = folder_name.split('/')[0]
|
||||
|
||||
# Use provided total size if available, otherwise sum from downloads
|
||||
total_size = self.total_size
|
||||
if not total_size:
|
||||
total_size = sum(d['size'] for d in active_downloads) + sum(d['size'] for d in self.completed_downloads)
|
||||
|
||||
# Calculate completion status based on total downloaded vs total size
|
||||
is_complete = len(active_downloads) == 0 and total_completed >= (total_size * 0.99) # Allow 1% margin for size differences
|
||||
|
||||
# If all downloads are complete, clear the completed_downloads list to prevent status updates
|
||||
if is_complete:
|
||||
self.completed_downloads = []
|
||||
|
||||
return [{
|
||||
'folderName': folder_name,
|
||||
'fileSize': total_size,
|
||||
'progress': total_completed / total_size if total_size > 0 else 0,
|
||||
'downloadSpeed': current_download_speed,
|
||||
'numPeers': 0,
|
||||
'numSeeds': 0,
|
||||
'status': 'complete' if is_complete else 'active',
|
||||
'bytesDownloaded': total_completed,
|
||||
}]
|
||||
|
||||
return []
|
||||
@@ -3,8 +3,8 @@ import sys, json, urllib.parse, psutil
|
||||
from torrent_downloader import TorrentDownloader
|
||||
from http_downloader import HttpDownloader
|
||||
from profile_image_processor import ProfileImageProcessor
|
||||
from http_multi_link_downloader import HttpMultiLinkDownloader
|
||||
import libtorrent as lt
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@@ -25,15 +25,7 @@ if start_download_payload:
|
||||
initial_download = json.loads(urllib.parse.unquote(start_download_payload))
|
||||
downloading_game_id = initial_download['game_id']
|
||||
|
||||
if isinstance(initial_download['url'], list):
|
||||
# Handle multiple URLs using HttpMultiLinkDownloader
|
||||
http_multi_downloader = HttpMultiLinkDownloader()
|
||||
downloads[initial_download['game_id']] = http_multi_downloader
|
||||
try:
|
||||
http_multi_downloader.start_download(initial_download['url'], initial_download['save_path'], initial_download.get('header'), initial_download.get("out"))
|
||||
except Exception as e:
|
||||
print("Error starting multi-link download", e)
|
||||
elif initial_download['url'].startswith('magnet'):
|
||||
if initial_download['url'].startswith('magnet'):
|
||||
torrent_downloader = TorrentDownloader(torrent_session)
|
||||
downloads[initial_download['game_id']] = torrent_downloader
|
||||
try:
|
||||
@@ -71,23 +63,12 @@ def status():
|
||||
return auth_error
|
||||
|
||||
downloader = downloads.get(downloading_game_id)
|
||||
if not downloader:
|
||||
if downloader:
|
||||
status = downloads.get(downloading_game_id).get_download_status()
|
||||
return jsonify(status), 200
|
||||
else:
|
||||
return jsonify(None)
|
||||
|
||||
status = downloader.get_download_status()
|
||||
if not status:
|
||||
return jsonify(None)
|
||||
|
||||
if isinstance(status, list):
|
||||
if not status: # Empty list
|
||||
return jsonify(None)
|
||||
|
||||
# For multi-link downloader, use the aggregated status
|
||||
# The status will already be aggregated by the HttpMultiLinkDownloader
|
||||
return jsonify(status[0]), 200
|
||||
|
||||
return jsonify(status), 200
|
||||
|
||||
@app.route("/seed-status", methods=["GET"])
|
||||
def seed_status():
|
||||
auth_error = validate_rpc_password()
|
||||
@@ -101,24 +82,10 @@ def seed_status():
|
||||
continue
|
||||
|
||||
response = downloader.get_download_status()
|
||||
if not response:
|
||||
if response is None:
|
||||
continue
|
||||
|
||||
if isinstance(response, list):
|
||||
# For multi-link downloader, check if all files are complete
|
||||
if response and all(item['status'] == 'complete' for item in response):
|
||||
seed_status.append({
|
||||
'gameId': game_id,
|
||||
'status': 'complete',
|
||||
'folderName': response[0]['folderName'],
|
||||
'fileSize': sum(item['fileSize'] for item in response),
|
||||
'bytesDownloaded': sum(item['bytesDownloaded'] for item in response),
|
||||
'downloadSpeed': 0,
|
||||
'numPeers': 0,
|
||||
'numSeeds': 0,
|
||||
'progress': 1.0
|
||||
})
|
||||
elif response.get('status') == 5: # Original torrent seeding check
|
||||
if response.get('status') == 5:
|
||||
seed_status.append({
|
||||
'gameId': game_id,
|
||||
**response,
|
||||
@@ -135,14 +102,27 @@ def process_list():
|
||||
auth_error = validate_rpc_password()
|
||||
if auth_error:
|
||||
return auth_error
|
||||
|
||||
iter_list = ['exe', 'pid', 'name']
|
||||
if sys.platform != 'win32':
|
||||
iter_list.append('cwd')
|
||||
iter_list.append('environ')
|
||||
|
||||
process_list = [proc.info for proc in psutil.process_iter(iter_list)]
|
||||
return jsonify(process_list), 200
|
||||
processes = []
|
||||
|
||||
for proc in psutil.process_iter(['exe', 'cwd', 'pid', 'cmdline']):
|
||||
try:
|
||||
info = proc.info
|
||||
cmdline = info.get('cmdline') or []
|
||||
wine_launched_exe = None
|
||||
|
||||
for arg in cmdline:
|
||||
if isinstance(arg, str) and arg.lower().endswith(".exe"):
|
||||
wine_launched_exe = os.path.basename(arg)
|
||||
break
|
||||
|
||||
exe_path = info.get('exe') or ''
|
||||
info['name'] = wine_launched_exe or os.path.basename(exe_path)
|
||||
processes.append(info)
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||
continue
|
||||
|
||||
return jsonify(processes), 200
|
||||
|
||||
@app.route("/profile-image", methods=["POST"])
|
||||
def profile_image():
|
||||
@@ -177,15 +157,7 @@ def action():
|
||||
|
||||
existing_downloader = downloads.get(game_id)
|
||||
|
||||
if isinstance(url, list):
|
||||
# Handle multiple URLs using HttpMultiLinkDownloader
|
||||
if existing_downloader and isinstance(existing_downloader, HttpMultiLinkDownloader):
|
||||
existing_downloader.start_download(url, data['save_path'], data.get('header'), data.get('out'))
|
||||
else:
|
||||
http_multi_downloader = HttpMultiLinkDownloader()
|
||||
downloads[game_id] = http_multi_downloader
|
||||
http_multi_downloader.start_download(url, data['save_path'], data.get('header'), data.get('out'))
|
||||
elif url.startswith('magnet'):
|
||||
if url.startswith('magnet'):
|
||||
if existing_downloader and isinstance(existing_downloader, TorrentDownloader):
|
||||
existing_downloader.start_download(url, data['save_path'])
|
||||
else:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "تم تسجيل الدخول بنجاح"
|
||||
},
|
||||
"home": {
|
||||
"featured": "مميز",
|
||||
"surprise_me": "مفاجئني",
|
||||
"no_results": "لم يتم العثور على نتائج",
|
||||
"start_typing": "ابدأ بالكتابة للبحث...",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"language_name": "беларуская мова",
|
||||
"home": {
|
||||
"featured": "Рэкамэндаванае",
|
||||
"surprise_me": "Здзіві мяне",
|
||||
"no_results": "Няма вынікаў"
|
||||
},
|
||||
@@ -16,6 +17,7 @@
|
||||
"home": "Галоўная",
|
||||
"favorites": "Улюбленыя"
|
||||
},
|
||||
|
||||
"header": {
|
||||
"search": "Пошук",
|
||||
"home": "Галоўная",
|
||||
@@ -29,7 +31,10 @@
|
||||
"downloading_metadata": "Сцягванне мэтаданых {{title}}…",
|
||||
"downloading": "Сцягванне {{title}}… ({{percentage}} скончана) - Канчатак {{eta}} - {{speed}}"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Наступная старонка",
|
||||
"previous_page": "Папярэдняя старонка"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Адкрыць варыянты сцягвання",
|
||||
"download_options_zero": "Няма варыянтаў сцягвання",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Успешно влизане"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Препоръчани",
|
||||
"surprise_me": "Изненадай ме",
|
||||
"no_results": "Няма намерени резултати",
|
||||
"start_typing": "Започнете да пишете за търсене...",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Has entrat correctament"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Destacats",
|
||||
"surprise_me": "Sorprèn-me",
|
||||
"no_results": "No s'ha trobat res"
|
||||
},
|
||||
@@ -24,6 +25,7 @@
|
||||
},
|
||||
"header": {
|
||||
"search": "Cerca jocs",
|
||||
|
||||
"home": "Inici",
|
||||
"catalogue": "Catàleg",
|
||||
"downloads": "Baixades",
|
||||
@@ -39,7 +41,10 @@
|
||||
"calculating_eta": "Descarregant {{title}}… ({{percentage}} completat) - Calculant el temps restant…",
|
||||
"checking_files": "Comprovant els fitxers de {{title}}… ({{percentage}} completat)"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Pàgina següent",
|
||||
"previous_page": "Pàgina anterior"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Obre les opcions de baixada",
|
||||
"download_options_zero": "No hi ha opcions de baixada",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Úspěšně přihlášen"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Doporučené",
|
||||
"surprise_me": "Překvap mě",
|
||||
"no_results": "Výsledek nenalezen",
|
||||
"start_typing": "Začni psát pro vyhledávání...",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Loggede ind successfuldt"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Anbefalet",
|
||||
"surprise_me": "Overrask mig",
|
||||
"no_results": "Ingen resultater fundet",
|
||||
"start_typing": "Begynd at skrive for at søge...",
|
||||
@@ -28,6 +29,7 @@
|
||||
},
|
||||
"header": {
|
||||
"search": "Søg efter spil",
|
||||
|
||||
"home": "Hjem",
|
||||
"catalogue": "Katalog",
|
||||
"downloads": "Downloads",
|
||||
@@ -43,7 +45,10 @@
|
||||
"calculating_eta": "Downloader {{title}}… ({{percentage}} færdig) - Udregner resterende tid…",
|
||||
"checking_files": "Checker {{title}} filer… ({{percentage}} færdig)"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Næste side",
|
||||
"previous_page": "Forrige side"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Åben download muligheder",
|
||||
"download_options_zero": "Ingen download mulighed",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Erfolgreich angemeldet"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Empfohlen",
|
||||
"surprise_me": "Überrasche mich",
|
||||
"no_results": "Keine Ergebnisse gefunden",
|
||||
"start_typing": "Tippe, um zu suchen...",
|
||||
@@ -58,7 +59,9 @@
|
||||
"download_sources": "Download-Quellen",
|
||||
"result_count": "{{resultCount}} Ergebnisse",
|
||||
"filter_count": "{{filterCount}} verfügbar",
|
||||
"clear_filters": "{{filterCount}} ausgewählte löschen"
|
||||
"clear_filters": "{{filterCount}} ausgewählte löschen",
|
||||
"next_page": "Nächste Seite",
|
||||
"previous_page": "Vorherige Seite"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Download-Optionen öffnen",
|
||||
|
||||
167
src/locales/en/translation.json
Executable file → Normal file
167
src/locales/en/translation.json
Executable file → Normal file
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Successfully signed in"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Featured",
|
||||
"surprise_me": "Surprise me",
|
||||
"no_results": "No results found",
|
||||
"start_typing": "Starting typing to search...",
|
||||
@@ -27,68 +28,7 @@
|
||||
"friends": "Friends",
|
||||
"need_help": "Need help?",
|
||||
"favorites": "Favorites",
|
||||
"playable_button_title": "Show only games you can play now",
|
||||
"add_custom_game_tooltip": "Add Custom Game",
|
||||
"show_playable_only_tooltip": "Show Playable Only",
|
||||
"custom_game_modal": "Add Custom Game",
|
||||
"custom_game_modal_description": "Add a custom game to your library by selecting an executable file",
|
||||
"custom_game_modal_executable_path": "Executable Path",
|
||||
"custom_game_modal_select_executable": "Select executable file",
|
||||
"custom_game_modal_title": "Title",
|
||||
"custom_game_modal_enter_title": "Enter title",
|
||||
"custom_game_modal_browse": "Browse",
|
||||
"custom_game_modal_cancel": "Cancel",
|
||||
"custom_game_modal_add": "Add Game",
|
||||
"custom_game_modal_adding": "Adding Game...",
|
||||
"custom_game_modal_success": "Custom game added successfully",
|
||||
"custom_game_modal_failed": "Failed to add custom game",
|
||||
"custom_game_modal_executable": "Executable",
|
||||
"edit_game_modal": "Customize Assets",
|
||||
"edit_game_modal_description": "Customize game assets and details",
|
||||
"edit_game_modal_title": "Title",
|
||||
"edit_game_modal_enter_title": "Enter title",
|
||||
"edit_game_modal_image": "Image",
|
||||
"edit_game_modal_select_image": "Select image",
|
||||
"edit_game_modal_browse": "Browse",
|
||||
"edit_game_modal_image_preview": "Image preview",
|
||||
"edit_game_modal_icon": "Icon",
|
||||
"edit_game_modal_select_icon": "Select icon",
|
||||
"edit_game_modal_icon_preview": "Icon preview",
|
||||
"edit_game_modal_logo": "Logo",
|
||||
"edit_game_modal_select_logo": "Select logo",
|
||||
"edit_game_modal_logo_preview": "Logo preview",
|
||||
"edit_game_modal_hero": "Library Hero",
|
||||
"edit_game_modal_select_hero": "Select library hero image",
|
||||
"edit_game_modal_hero_preview": "Library hero image preview",
|
||||
"edit_game_modal_cancel": "Cancel",
|
||||
"edit_game_modal_update": "Update",
|
||||
"edit_game_modal_updating": "Updating...",
|
||||
"edit_game_modal_fill_required": "Please fill in all required fields",
|
||||
"edit_game_modal_success": "Assets updated successfully",
|
||||
"edit_game_modal_failed": "Failed to update assets",
|
||||
"edit_game_modal_image_filter": "Image",
|
||||
"edit_game_modal_icon_resolution": "Recommended resolution: 256x256px",
|
||||
"edit_game_modal_logo_resolution": "Recommended resolution: 640x360px",
|
||||
"edit_game_modal_hero_resolution": "Recommended resolution: 1920x620px",
|
||||
"edit_game_modal_assets": "Assets",
|
||||
"edit_game_modal_drop_icon_image_here": "Drop icon image here",
|
||||
"edit_game_modal_drop_logo_image_here": "Drop logo image here",
|
||||
"edit_game_modal_drop_hero_image_here": "Drop hero image here",
|
||||
"edit_game_modal_drop_to_replace_icon": "Drop to replace icon",
|
||||
"edit_game_modal_drop_to_replace_logo": "Drop to replace logo",
|
||||
"edit_game_modal_drop_to_replace_hero": "Drop to replace hero",
|
||||
"install_decky_plugin": "Install Decky Plugin",
|
||||
"update_decky_plugin": "Update Decky Plugin",
|
||||
"decky_plugin_installed_version": "Decky Plugin (v{{version}})",
|
||||
"install_decky_plugin_title": "Install Hydra Decky Plugin",
|
||||
"install_decky_plugin_message": "This will download and install the Hydra plugin for Decky Loader. This may require elevated permissions. Continue?",
|
||||
"update_decky_plugin_title": "Update Hydra Decky Plugin",
|
||||
"update_decky_plugin_message": "A new version of the Hydra Decky plugin is available. Would you like to update it now?",
|
||||
"decky_plugin_installed": "Decky plugin v{{version}} installed successfully",
|
||||
"decky_plugin_installation_failed": "Failed to install Decky plugin: {{error}}",
|
||||
"decky_plugin_installation_error": "Error installing Decky plugin: {{error}}",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel"
|
||||
"playable_button_title": "Show only games you can play now"
|
||||
},
|
||||
"header": {
|
||||
"search": "Search games",
|
||||
@@ -149,7 +89,6 @@
|
||||
"amount_minutes": "{{amount}} minutes",
|
||||
"accuracy": "{{accuracy}}% accuracy",
|
||||
"add_to_library": "Add to library",
|
||||
"already_in_library": "Already in library",
|
||||
"remove_from_library": "Remove from library",
|
||||
"no_downloads": "No downloads available",
|
||||
"play_time": "Played for {{amount}}",
|
||||
@@ -178,13 +117,11 @@
|
||||
"open_folder": "Open folder",
|
||||
"open_download_location": "See downloaded files",
|
||||
"create_shortcut": "Create desktop shortcut",
|
||||
"create_shortcut_simple": "Create shortcut",
|
||||
"clear": "Clear",
|
||||
"remove_files": "Remove files",
|
||||
"remove_from_library_title": "Are you sure?",
|
||||
"remove_from_library_description": "This will remove {{game}} from your library",
|
||||
"options": "Options",
|
||||
"properties": "Properties",
|
||||
"executable_section_title": "Executable",
|
||||
"executable_section_description": "Path of the file that will be executed when \"Play\" is clicked",
|
||||
"downloads_section_title": "Downloads",
|
||||
@@ -198,13 +135,6 @@
|
||||
"create_shortcut_success": "Shortcut created successfully",
|
||||
"you_might_need_to_restart_steam": "You might need to restart Steam to see the changes",
|
||||
"create_shortcut_error": "Error creating shortcut",
|
||||
"add_to_favorites": "Add to favorites",
|
||||
"remove_from_favorites": "Remove from favorites",
|
||||
"failed_update_favorites": "Failed to update favorites",
|
||||
"game_removed_from_library": "Game removed from library",
|
||||
"failed_remove_from_library": "Failed to remove from library",
|
||||
"files_removed_success": "Files removed successfully",
|
||||
"failed_remove_files": "Failed to remove files",
|
||||
"nsfw_content_title": "This game contains inappropriate content",
|
||||
"nsfw_content_description": "{{title}} contains content that may not be suitable for all ages. Are you sure you want to continue?",
|
||||
"allow_nsfw_content": "Continue",
|
||||
@@ -212,7 +142,6 @@
|
||||
"stats": "Stats",
|
||||
"download_count": "Downloads",
|
||||
"player_count": "Active players",
|
||||
"rating_count": "Rating",
|
||||
"download_error": "This download option is not available",
|
||||
"download": "Download",
|
||||
"executable_path_in_use": "Executable already in use by \"{{game}}\"",
|
||||
@@ -220,39 +149,6 @@
|
||||
"hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util it's completed. If Hydra closes before completing, you will lose your progress.",
|
||||
"achievements": "Achievements",
|
||||
"achievements_count": "Achievements {{unlockedCount}}/{{achievementsCount}}",
|
||||
"show_more": "Show more",
|
||||
"show_less": "Show less",
|
||||
"reviews": "Reviews",
|
||||
"leave_a_review": "Leave a Review",
|
||||
"write_review_placeholder": "Share your thoughts about this game...",
|
||||
"sort_newest": "Newest",
|
||||
"no_reviews_yet": "No reviews yet",
|
||||
"be_first_to_review": "Be the first to share your thoughts about this game!",
|
||||
"sort_oldest": "Oldest",
|
||||
"sort_highest_score": "Highest Score",
|
||||
"sort_lowest_score": "Lowest Score",
|
||||
"sort_most_voted": "Most Voted",
|
||||
"rating": "Rating",
|
||||
"rating_stats": "Rating",
|
||||
"rating_very_negative": "Very Negative",
|
||||
"rating_negative": "Negative",
|
||||
"rating_neutral": "Neutral",
|
||||
"rating_positive": "Positive",
|
||||
"rating_very_positive": "Very Positive",
|
||||
"submit_review": "Submit",
|
||||
"submitting": "Submitting...",
|
||||
"review_submitted_successfully": "Review submitted successfully!",
|
||||
"review_submission_failed": "Failed to submit review. Please try again.",
|
||||
"review_cannot_be_empty": "Review text field cannot be empty.",
|
||||
"review_deleted_successfully": "Review deleted successfully.",
|
||||
"review_deletion_failed": "Failed to delete review. Please try again.",
|
||||
"loading_reviews": "Loading reviews...",
|
||||
"loading_more_reviews": "Loading more reviews...",
|
||||
"load_more_reviews": "Load more reviews",
|
||||
"you_seemed_to_enjoy_this_game": "You've seemed to enjoy this game",
|
||||
"would_you_recommend_this_game": "Would you like to leave a review to this game?",
|
||||
"yes": "Yes",
|
||||
"maybe_later": "Maybe later",
|
||||
"cloud_save": "Cloud save",
|
||||
"cloud_save_description": "Save your progress in the cloud and continue playing on any device",
|
||||
"backups": "Backups",
|
||||
@@ -265,7 +161,6 @@
|
||||
"uploading_backup": "Uploading backup…",
|
||||
"no_backups": "You haven't created any backups for this game yet",
|
||||
"backup_uploaded": "Backup uploaded",
|
||||
"backup_failed": "Backup failed",
|
||||
"backup_deleted": "Backup deleted",
|
||||
"backup_restored": "Backup restored",
|
||||
"see_all_achievements": "See all achievements",
|
||||
@@ -302,20 +197,10 @@
|
||||
"download_error_gofile_quota_exceeded": "You have exceeded your Gofile monthly quota. Please await the quota to reset.",
|
||||
"download_error_real_debrid_account_not_authorized": "Your Real-Debrid account is not authorized to make new downloads. Please check your account settings and try again.",
|
||||
"download_error_not_cached_on_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.",
|
||||
"update_playtime_title": "Update playtime",
|
||||
"update_playtime_description": "Manually update the playtime for {{game}}",
|
||||
"update_playtime": "Update playtime",
|
||||
"update_playtime_success": "Playtime updated successfully",
|
||||
"update_playtime_error": "Failed to update playtime",
|
||||
"update_game_playtime": "Update game playtime",
|
||||
"manual_playtime_warning": "Your hours will be marked as manually updated, and this cannot be undone.",
|
||||
"manual_playtime_tooltip": "This playtime has been manually updated",
|
||||
"download_error_not_cached_on_torbox": "This download is not available on TorBox and polling download status from TorBox is not yet available.",
|
||||
"download_error_not_cached_on_hydra": "This download is not available on Nimbus.",
|
||||
"game_removed_from_favorites": "Game removed from favorites",
|
||||
"game_added_to_favorites": "Game added to favorites",
|
||||
"game_removed_from_pinned": "Game removed from pinned",
|
||||
"game_added_to_pinned": "Game added to pinned",
|
||||
"automatically_extract_downloaded_files": "Automatically extract downloaded files",
|
||||
"create_start_menu_shortcut": "Create Start Menu shortcut",
|
||||
"invalid_wine_prefix_path": "Invalid Wine prefix path",
|
||||
@@ -334,30 +219,7 @@
|
||||
"backup_frozen": "Backup pinned",
|
||||
"backup_unfrozen": "Backup unpinned",
|
||||
"backup_freeze_failed": "Failed to freeze backup",
|
||||
"backup_freeze_failed_description": "You must leave at least one free slot for automatic backups",
|
||||
"edit_game_modal_button": "Customize game assets",
|
||||
"game_details": "Game Details",
|
||||
"currency_symbol": "$",
|
||||
"currency_country": "us",
|
||||
"prices": "Prices",
|
||||
"no_prices_found": "No prices found",
|
||||
"view_all_prices": "Click to view all prices",
|
||||
"retail_price": "Retail price",
|
||||
"keyshop_price": "Keyshop price",
|
||||
"historical_retail": "Historical retail",
|
||||
"historical_keyshop": "Historical keyshop",
|
||||
"language": "Language",
|
||||
"caption": "Caption",
|
||||
"audio": "Audio",
|
||||
"filter_by_source": "Filter by source",
|
||||
"no_repacks_found": "No sources found for this game",
|
||||
"delete_review": "Delete review",
|
||||
"remove_review": "Remove Review",
|
||||
"delete_review_modal_title": "Are you sure you want to delete your review?",
|
||||
"delete_review_modal_description": "This action cannot be undone.",
|
||||
"delete_review_modal_delete_button": "Delete",
|
||||
"delete_review_modal_cancel_button": "Cancel",
|
||||
"vote_failed": "Failed to register your vote. Please try again."
|
||||
"backup_freeze_failed_description": "You must leave at least one free slot for automatic backups"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Activate Hydra",
|
||||
@@ -446,7 +308,6 @@
|
||||
"found_download_option_one": "Found {{countFormatted}} download option",
|
||||
"found_download_option_other": "Found {{countFormatted}} download options",
|
||||
"import": "Import",
|
||||
"importing": "Importing...",
|
||||
"public": "Public",
|
||||
"private": "Private",
|
||||
"friends_only": "Friends only",
|
||||
@@ -499,8 +360,6 @@
|
||||
"delete_theme_description": "This will delete the theme {{theme}}",
|
||||
"cancel": "Cancel",
|
||||
"appearance": "Appearance",
|
||||
"debrid": "Debrid",
|
||||
"debrid_description": "Debrid services are premium unrestricted downloaders that allow you to quickly download files hosted on various file hosting services, only limited by your internet speed.",
|
||||
"enable_torbox": "Enable TorBox",
|
||||
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
|
||||
"torbox_account_linked": "TorBox account linked",
|
||||
@@ -520,7 +379,6 @@
|
||||
"installing_common_redist": "Installing…",
|
||||
"show_download_speed_in_megabytes": "Show download speed in megabytes per second",
|
||||
"extract_files_by_default": "Extract files by default after download",
|
||||
"enable_steam_achievements": "Enable search for Steam achievements",
|
||||
"achievement_custom_notification_position": "Achievement custom notification position",
|
||||
"top-left": "Top left",
|
||||
"top-center": "Top center",
|
||||
@@ -564,8 +422,7 @@
|
||||
"game_card": {
|
||||
"available_one": "Available",
|
||||
"available_other": "Available",
|
||||
"no_downloads": "No downloads available",
|
||||
"calculating": "Calculating"
|
||||
"no_downloads": "No downloads available"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Programs not installed",
|
||||
@@ -581,18 +438,10 @@
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} hours",
|
||||
"amount_minutes": "{{amount}} minutes",
|
||||
"amount_hours_short": "{{amount}}h",
|
||||
"amount_minutes_short": "{{amount}}m",
|
||||
"last_time_played": "Last played {{period}}",
|
||||
"activity": "Recent Activity",
|
||||
"library": "Library",
|
||||
"pinned": "Pinned",
|
||||
"sort_by": "Sort by:",
|
||||
"achievements_earned": "Achievements earned",
|
||||
"played_recently": "Played recently",
|
||||
"playtime": "Playtime",
|
||||
"total_play_time": "Total playtime",
|
||||
"manual_playtime_tooltip": "This playtime has been manually updated",
|
||||
"no_recent_activity_title": "Hmmm… nothing here",
|
||||
"no_recent_activity_description": "You haven't played any games recently. It's time to change that!",
|
||||
"display_name": "Display name",
|
||||
@@ -666,13 +515,7 @@
|
||||
"earned_points": "Earned points",
|
||||
"show_achievements_on_profile": "Show your achievements on your profile",
|
||||
"show_points_on_profile": "Show your earned points on your profile",
|
||||
"error_adding_friend": "Could not send friend request. Please check friend code",
|
||||
"friend_code_length_error": "Friend code must have 8 characters",
|
||||
"game_removed_from_pinned": "Game removed from pinned",
|
||||
"game_added_to_pinned": "Game added to pinned",
|
||||
"karma": "Karma",
|
||||
"karma_count": "karma",
|
||||
"karma_description": "Earned from positive likes on reviews"
|
||||
"error_adding_friend": "Could not send friend request. Please check friend code"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Achievement unlocked",
|
||||
|
||||
@@ -1,76 +1,34 @@
|
||||
{
|
||||
"language_name": "Español",
|
||||
"app": {
|
||||
"successfully_signed_in": "Iniciaste sesión exitosamente"
|
||||
"successfully_signed_in": "Sesión iniciada exitosamente"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Destacado",
|
||||
"surprise_me": "¡Sorpréndeme!",
|
||||
"no_results": "No se encontraron resultados",
|
||||
"start_typing": "Empezá a escribir para buscar...",
|
||||
"hot": "Tendencias",
|
||||
"no_results": "Sin resultados encontrados",
|
||||
"start_typing": "Empieza a escribir para buscar...",
|
||||
"hot": "Popular Ahora",
|
||||
"weekly": "📅 Mejores juegos de la semana",
|
||||
"achievements": "🏆 Juegos para platinar"
|
||||
"achievements": "🏆 Juegos para completar"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Catálogo",
|
||||
"downloads": "Descargas",
|
||||
"settings": "Ajustes",
|
||||
"my_library": "Mi Librería",
|
||||
"my_library": "Mi biblioteca",
|
||||
"downloading_metadata": "{{title}} (Descargando metadatos…)",
|
||||
"paused": "{{title}} (Pausado)",
|
||||
"downloading": "{{title}} ({{percentage}} - Descargando…)",
|
||||
"filter": "Filtrar Librería",
|
||||
"filter": "Buscar en la biblioteca",
|
||||
"home": "Inicio",
|
||||
"queued": "{{title}} (En cola)",
|
||||
"game_has_no_executable": "El juego no tiene un ejecutable seleccionado",
|
||||
"sign_in": "Iniciar Sesión",
|
||||
"sign_in": "Iniciar sesión",
|
||||
"friends": "Amigos",
|
||||
"need_help": "¿Necesitás ayuda?",
|
||||
"need_help": "¿Necesitas ayuda?",
|
||||
"favorites": "Favoritos",
|
||||
"playable_button_title": "Solo mostrar juegos que podés jugar en este momento",
|
||||
"add_custom_game_tooltip": "Añadir juego personalizado",
|
||||
"show_playable_only_tooltip": "Mostrar Solo Jugable",
|
||||
"custom_game_modal": "Añadir juego personalizado",
|
||||
"custom_game_modal_description": "Añadí un juego personalizado a tu librería seleccionando el ejecutable",
|
||||
"custom_game_modal_executable_path": "Ruta del Ejecutable",
|
||||
"custom_game_modal_select_executable": "Seleccionar archivo ejecutable",
|
||||
"custom_game_modal_title": "Título",
|
||||
"custom_game_modal_enter_title": "Ingresá el título",
|
||||
"custom_game_modal_browse": "Buscar",
|
||||
"custom_game_modal_cancel": "Cancelar",
|
||||
"custom_game_modal_add": "Añadir juego",
|
||||
"custom_game_modal_adding": "Añadiendo juego...",
|
||||
"custom_game_modal_success": "Juego personalizado añadido exitosamente",
|
||||
"custom_game_modal_failed": "Error al añadir juego personalizado",
|
||||
"custom_game_modal_executable": "Ejecutable",
|
||||
"edit_game_modal": "Personalizar recursos",
|
||||
"edit_game_modal_description": "Personaliza los recursos y detalles del juego",
|
||||
"edit_game_modal_title": "Título",
|
||||
"edit_game_modal_enter_title": "Ingresá el título",
|
||||
"edit_game_modal_image": "Imagen",
|
||||
"edit_game_modal_select_image": "Seleccionar imagen",
|
||||
"edit_game_modal_browse": "Navegar",
|
||||
"edit_game_modal_image_preview": "Vista previa de imagen",
|
||||
"edit_game_modal_icon": "Ícono",
|
||||
"edit_game_modal_select_icon": "Seleccionar ícono",
|
||||
"edit_game_modal_icon_preview": "Vista previa de ícono",
|
||||
"edit_game_modal_logo": "Logo",
|
||||
"edit_game_modal_select_logo": "Seleccionar logo",
|
||||
"edit_game_modal_logo_preview": "Vista previa del logo",
|
||||
"edit_game_modal_hero": "Library Hero",
|
||||
"edit_game_modal_select_hero": "Seleccionar una imagen de Library Hero",
|
||||
"edit_game_modal_hero_preview": "Vista previa de library hero",
|
||||
"edit_game_modal_cancel": "Cancelar",
|
||||
"edit_game_modal_update": "Actualizar",
|
||||
"edit_game_modal_updating": "Actualizando...",
|
||||
"edit_game_modal_fill_required": "Por favor rellená todos los espacios requeridos",
|
||||
"edit_game_modal_success": "Recursos actualizados exitosamente",
|
||||
"edit_game_modal_failed": "Error al actualizar los recursos",
|
||||
"edit_game_modal_image_filter": "Imagen",
|
||||
"edit_game_modal_icon_resolution": "Resolución recomendada: 256x256px",
|
||||
"edit_game_modal_logo_resolution": "Resolución recomendada: 640x360px",
|
||||
"edit_game_modal_hero_resolution": "Resolución recomendada: 1920x620px",
|
||||
"edit_game_modal_assets": "Recursos"
|
||||
"playable_button_title": "Mostrar solo juegos que puedes jugar ahora"
|
||||
},
|
||||
"header": {
|
||||
"search": "Buscar juegos",
|
||||
@@ -79,411 +37,348 @@
|
||||
"downloads": "Descargas",
|
||||
"search_results": "Resultados de búsqueda",
|
||||
"settings": "Ajustes",
|
||||
"version_available_install": "Versión {{version}} disponible. Presiona acá para reiniciar e instalar.",
|
||||
"version_available_download": "Versión {{version}} disponible. Presiona acá para descargar."
|
||||
"version_available_install": "Versión {{version}} disponible. Presiona acá para descargar y reinstalar.",
|
||||
"version_available_download": "Versión {{version}} disponible. Presiona aquí para descargar."
|
||||
},
|
||||
"bottom_panel": {
|
||||
"no_downloads_in_progress": "Sin descargas en progreso",
|
||||
"downloading_metadata": "Descargando metadatos de {{title}}…",
|
||||
"downloading": "Descargando {{title}}… ({{percentage}} completado) - Restante {{eta}} - {{speed}}",
|
||||
"calculating_eta": "Descargando {{title}}… ({{percentage}} completado) - Comprobando tiempo restante…",
|
||||
"checking_files": "Revisando archivos de {{title}}… ({{percentage}} completado)",
|
||||
"installing_common_redist": "{{log}}…",
|
||||
"downloading": "Descargando {{title}}… ({{percentage}} completado) - Finalizando {{eta}} - {{speed}}",
|
||||
"calculating_eta": "Descargando {{title}}… ({{percentage}} completado) - Calculando tiempo restante…",
|
||||
"installation_complete": "Instalación completada",
|
||||
"installation_complete_message": "Common redistributables instalados correctamente"
|
||||
"installation_complete_message": "Common redistributables instalados exitosamente",
|
||||
"installing_common_redist": "{{log}}…",
|
||||
"checking_files": "Verificando archivos de {{title}}… ({{percentage}} completado)"
|
||||
},
|
||||
"catalogue": {
|
||||
"search": "Filtrar…",
|
||||
"developers": "Desarrolladores",
|
||||
"genres": "Géneros",
|
||||
"tags": "Etiquetas",
|
||||
"tags": "Marcadores",
|
||||
"publishers": "Editores",
|
||||
"download_sources": "Descargando fuentes",
|
||||
"download_sources": "Fuentes de descarga",
|
||||
"result_count": "{{resultCount}} resultados",
|
||||
"filter_count": "{{filterCount}} disponible",
|
||||
"filter_count": "{{filterCount}} disponibles",
|
||||
"clear_filters": "Limpiar {{filterCount}} seleccionados"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Abrir opciones de descargas",
|
||||
"download_options_zero": "Sin opciones de descargas",
|
||||
"open_download_options": "Ver opciones de descargas",
|
||||
"automatically_extract_downloaded_files": "Extraer automáticamente archivos descargados",
|
||||
"download_error_not_cached_on_hydra": "Esta descarga no está disponible en Nimbus.",
|
||||
"download_options_zero": "No hay opciones de descargas disponibles",
|
||||
"download_options_one": "{{count}} opción de descarga",
|
||||
"download_options_other": "{{count}} opciones de descargas",
|
||||
"updated_at": "Actualizado el {{updated_at}}",
|
||||
"updated_at": "Actualizado el: {{updated_at}}",
|
||||
"install": "Instalar",
|
||||
"resume": "Resumir",
|
||||
"pause": "Pausar",
|
||||
"resume": "Continuar",
|
||||
"pause": "Pausa",
|
||||
"cancel": "Cancelar",
|
||||
"remove": "Remover",
|
||||
"space_left_on_disk": "{{space}} restante en el disco",
|
||||
"eta": "Conclusión {{eta}}",
|
||||
"remove": "Eliminar",
|
||||
"space_left_on_disk": "{{space}} restantes en el disco",
|
||||
"eta": "Tiempo restante: {{eta}}",
|
||||
"calculating_eta": "Calculando tiempo restante…",
|
||||
"downloading_metadata": "Descargando metadatos…",
|
||||
"filter": "Filtrar repacks",
|
||||
"filter": "Buscar repacks",
|
||||
"requirements": "Requisitos del Sistema",
|
||||
"minimum": "Mínimos",
|
||||
"recommended": "Recomendados",
|
||||
"paused": "En Pausa",
|
||||
"release_date": "Lanzado el {{date}}",
|
||||
"publisher": "Públicado por {{publisher}}",
|
||||
"paused": "Pausado",
|
||||
"release_date": "Fecha de lanzamiento: {{date}}",
|
||||
"publisher": "Publicado por: {{publisher}}",
|
||||
"hours": "horas",
|
||||
"minutes": "minutos",
|
||||
"amount_hours": "{{amount}} horas",
|
||||
"amount_minutes": "{{amount}} minutos",
|
||||
"accuracy": "{{accuracy}}% completista",
|
||||
"add_to_library": "Añadir a la librería",
|
||||
"already_in_library": "Ya está en la librería",
|
||||
"remove_from_library": "Eliminar de la librería",
|
||||
"no_downloads": "Sin descargas disponibles",
|
||||
"play_time": "Jugado por {{amount}}",
|
||||
"last_time_played": "Última vez jugado {{period}}",
|
||||
"not_played_yet": "No has jugado a {{title}} todavía",
|
||||
"accuracy": "{{accuracy}}% precisión",
|
||||
"add_to_library": "Agregar a la biblioteca",
|
||||
"remove_from_library": "Eliminar de la biblioteca",
|
||||
"no_downloads": "No hay descargas disponibles",
|
||||
"play_time": "Has jugado {{amount}}",
|
||||
"last_time_played": "Jugado por última vez: {{period}}",
|
||||
"not_played_yet": "Aún no has jugado a {{title}}",
|
||||
"next_suggestion": "Siguiente sugerencia",
|
||||
"play": "Jugar",
|
||||
"deleting": "Eliminando instalador…",
|
||||
"close": "Cerrar",
|
||||
"playing_now": "Jugando ahora",
|
||||
"change": "Cambiar",
|
||||
"repacks_modal_description": "Elegí el repack que querés descargar",
|
||||
"select_folder_hint": "Si querés cambiar la carpeta por defecto, andá a <0>Ajustes</0>",
|
||||
"repacks_modal_description": "Selecciona el repack que quieres descargar",
|
||||
"select_folder_hint": "Para cambiar la carpeta predeterminada, ve a <0>Ajustes</0>",
|
||||
"download_now": "Descargar ahora",
|
||||
"no_shop_details": "No se pudieron obtener detalles de la tienda.",
|
||||
"download_options": "Opciones de descarga",
|
||||
"download_path": "Ruta de descarga",
|
||||
"previous_screenshot": "Anterior captura",
|
||||
"next_screenshot": "Siguiente captura",
|
||||
"screenshot": "Captura número {{number}}",
|
||||
"open_screenshot": "Abrir captura número {{number}}",
|
||||
"download_settings": "Descargar ajustes",
|
||||
"downloader": "Descargador",
|
||||
"screenshot": "Captura {{number}}",
|
||||
"open_screenshot": "Abrir captura {{number}}",
|
||||
"download_settings": "Ajustes de descarga",
|
||||
"downloader": "Método de descarga",
|
||||
"select_executable": "Seleccionar",
|
||||
"no_executable_selected": "Sin ejecutable seleccionado",
|
||||
"no_executable_selected": "No se seleccionó un ejecutable",
|
||||
"open_folder": "Abrir carpeta",
|
||||
"open_download_location": "Ver archivos descargados",
|
||||
"create_shortcut": "Crear atajo en el escritorio",
|
||||
"clear": "Limpiar",
|
||||
"remove_files": "Remover archivos",
|
||||
"create_shortcut": "Crear acceso directo en el escritorio",
|
||||
"remove_files": "Eliminar archivos",
|
||||
"remove_from_library_title": "¿Estás seguro?",
|
||||
"remove_from_library_description": "Esto va eliminará {{game}} de tu librería",
|
||||
"remove_from_library_description": "Esto eliminará {{game}} de tu biblioteca",
|
||||
"options": "Opciones",
|
||||
"executable_section_title": "Ejecutable",
|
||||
"executable_section_description": "Ruta del archivo que se ejecutará cuando presiones \"Jugar\"",
|
||||
"executable_section_description": "Ruta del archivo que se ejecutará cuando se presione \"Jugar\"",
|
||||
"downloads_section_title": "Descargas",
|
||||
"downloads_section_description": "Revisar actualizaciones u otras versiones del juego",
|
||||
"danger_zone_section_title": "Zona de Peligro",
|
||||
"danger_zone_section_description": "Remover este juego de tu librería o los archivos descargados por Hydra",
|
||||
"downloads_section_description": "Buscar actualizaciones u otras versiones de este juego",
|
||||
"danger_zone_section_title": "Opciones Avanzadas",
|
||||
"danger_zone_section_description": "Eliminar este juego de tu librería o los archivos descargados por Hydra (Esto solo eliminará los archivos de instalación y no el juego instalado)",
|
||||
"download_in_progress": "Descarga en progreso",
|
||||
"download_paused": "Descarga pausada",
|
||||
"last_downloaded_option": "Última opción de descarga",
|
||||
"create_steam_shortcut": "Crear atajo de Steam",
|
||||
"last_downloaded_option": "Última opción descargada",
|
||||
"create_shortcut_success": "Atajo creado con éxito",
|
||||
"you_might_need_to_restart_steam": "Probablemente necesités reiniciar Steam para ver cambios",
|
||||
"create_shortcut_error": "Error al crear atajo",
|
||||
"nsfw_content_title": "Este juego tiene contenido inapropiado",
|
||||
"nsfw_content_description": "{{title}} tiene contenido no apto para todas las edades. ¿Querés continuar igualmente?",
|
||||
"you_might_need_to_restart_steam": "Es posible que necesites reiniciar Steam para ver los cambios",
|
||||
"create_shortcut_error": "Error al crear un atajo",
|
||||
"nsfw_content_title": "Este juego contiene contenido inapropiado.",
|
||||
"nsfw_content_description": "{{title}} puede ser no adecuado para todas las edades por su contenido. \n¿Deseas continuar de igual forma?",
|
||||
"allow_nsfw_content": "Continuar",
|
||||
"refuse_nsfw_content": "Regresar",
|
||||
"refuse_nsfw_content": "No, gracias",
|
||||
"stats": "Estadísticas",
|
||||
"download_count": "Descargas",
|
||||
"player_count": "Jugadores activos",
|
||||
"download_error": "Esta opción de descarga no está disponible",
|
||||
"download_count": "Downloads",
|
||||
"player_count": "Jugadores activos",
|
||||
"download_error": "Esta opción de descarga no está disponible.",
|
||||
"download": "Descargar",
|
||||
"executable_path_in_use": "El ejecutable ya se está usando por \"{{game}}\"",
|
||||
"executable_path_in_use": "El ejecutable se encuentra en uso por \"{{game}}\"",
|
||||
"warning": "Advertencia:",
|
||||
"hydra_needs_to_remain_open": "para esta descarga, Hydra necesita estar abierta hasta que termine. Si se cierra antes de completar, perderás todo el progreso.",
|
||||
"hydra_needs_to_remain_open": "Para esta descarga, Hydra necesita mantenerse abierta hasta que concluya. En caso de que Hydra se cierre antes de que concluya, podrías perder todo el progreso.",
|
||||
"achievements": "Logros",
|
||||
"achievements_count": "Logros {{unlockedCount}}/{{achievementsCount}}",
|
||||
"cloud_save": "Guardado en la nube",
|
||||
"cloud_save_description": "Guardá tu progreso en la nube y jugá en cualquier dispositivo",
|
||||
"backups": "Copia de seguridad",
|
||||
"cloud_save_description": "Guarda tu progreso en la nube y continúa jugando en cualquier dispositivo",
|
||||
"backups": "Copias de Seguridad",
|
||||
"install_backup": "Instalar",
|
||||
"delete_backup": "Eliminar",
|
||||
"create_backup": "Nueva copia de seguridad",
|
||||
"last_backup_date": "Última copia de seguridad {{date}}",
|
||||
"no_backup_preview": "No se han encotrado puntos de guardado para este juego",
|
||||
"create_backup": "Nueva Copia de Seguridad",
|
||||
"last_backup_date": "Última copia de seguridad el {{date}}",
|
||||
"no_backup_preview": "No se encontraron datos de guardados para este juego",
|
||||
"restoring_backup": "Restaurando copia de seguridad ({{progress}} completado)…",
|
||||
"uploading_backup": "Subiendo copia de seguridad…",
|
||||
"no_backups": "No has creado ninguna copia de seguridad para este juego todavía",
|
||||
"no_backups": "No has creado ninguna copia de seguridad para este juego aún",
|
||||
"backup_uploaded": "Copia de seguridad subida",
|
||||
"backup_failed": "Copia de seguridad fallida",
|
||||
"backup_deleted": "Copia de seguridad eliminada",
|
||||
"backup_restored": "Copia de seguridad restaurada",
|
||||
"see_all_achievements": "Ver todos los logros",
|
||||
"sign_in_to_see_achievements": "Iniciá sesión para ver los logros",
|
||||
"sign_in_to_see_achievements": "Inicia sesión para ver los logros",
|
||||
"mapping_method_automatic": "Automático",
|
||||
"mapping_method_manual": "Manual",
|
||||
"mapping_method_label": "Método de mapeado",
|
||||
"files_automatically_mapped": "Archivos automáticamente mapeados",
|
||||
"mapping_method_label": "Método de mapeo",
|
||||
"files_automatically_mapped": "Archivos mapeados automáticamente",
|
||||
"no_backups_created": "Sin copias de seguridad creadas para este juego",
|
||||
"manage_files": "Administrar archivos",
|
||||
"loading_save_preview": "Buscando por guardado de juegos…",
|
||||
"manage_files": "Gestionar archivos",
|
||||
"loading_save_preview": "Buscando datos de guardados de juegos…",
|
||||
"wine_prefix": "Prefijo de Wine",
|
||||
"wine_prefix_description": "El prefijo de Wine usado para este juego",
|
||||
"launch_options": "Opciones para iniciar",
|
||||
"launch_options_description": "Los usuarios avanzados pueden ingresar sus modificaciones para el inicio de sus juegos (característica experimental)",
|
||||
"launch_options_placeholder": "Sin parámetro específicado",
|
||||
"wine_prefix_description": "El prefijo de Wine usado para ejecutar este juego",
|
||||
"no_download_option_info": "Sin información disponible",
|
||||
"backup_deletion_failed": "Error al eliminar copia de seguridad",
|
||||
"max_number_of_artifacts_reached": "Máximo de copias de seguridad alcanzadas para este juego",
|
||||
"achievements_not_sync": "Revisá como sincronizar tus logros'",
|
||||
"manage_files_description": "Elegí que archivos se guardarán y restaurarán de la copia de seguridad",
|
||||
"backup_deletion_failed": "La eliminación de la copia de seguridad falló",
|
||||
"max_number_of_artifacts_reached": "Número máximo de copias de seguridad de este juego alcanzadas",
|
||||
"achievements_not_sync": "Tus logros no están sincronizados",
|
||||
"manage_files_description": "Gestiona los archivos que serán respaldados y restaurados",
|
||||
"select_folder": "Seleccionar carpeta",
|
||||
"backup_from": "Copia de seguridad de {{date}}",
|
||||
"automatic_backup_from": "Copia de seguridad automática de {{date}}",
|
||||
"enable_automatic_cloud_sync": "Habilitar sincronización con la nube",
|
||||
"custom_backup_location_set": "Ubicación de copia de seguridad personalizada",
|
||||
"no_directory_selected": "Sin directorio seleccionado",
|
||||
"no_write_permission": "No se puede descargar en este directorio. Presioná acá para más información.",
|
||||
"enable_automatic_cloud_sync": "Habilitar sincronización automática en la nube",
|
||||
"custom_backup_location_set": "Se configuró la carpeta de copia de seguridad",
|
||||
"clear": "Limpiar",
|
||||
"no_directory_selected": "No se seleccionó un directorio",
|
||||
"launch_options": "Opciones de Inicio",
|
||||
"launch_options_description": "Los usuarios avanzados pueden introducir sus propias modificaciones de opciones de inicio (característica experimental)",
|
||||
"launch_options_placeholder": "Sin parámetro específicado",
|
||||
"no_write_permission": "No se puede descargar en este directorio. Presiona aquí para aprender más.",
|
||||
"reset_achievements": "Reiniciar logros",
|
||||
"reset_achievements_description": "Esto va a reiniciar todos los logros para {{game}}",
|
||||
"reset_achievements_title": "¿Querés continuar?",
|
||||
"reset_achievements_success": "Logros reiniciados éxitosamente",
|
||||
"reset_achievements_error": "Error al reiniciar logros",
|
||||
"download_error_gofile_quota_exceeded": "Has excedido la cuota mensual de GoFile. Esperá a que se reinice.",
|
||||
"download_error_real_debrid_account_not_authorized": "Tu cuenta de Real-Debrid no está autorizada para nuevas descargas. Revisá los ajustes de tu cuenta y probá de nuevo.",
|
||||
"download_error_not_cached_on_real_debrid": "Esta descarga no está disponible en Real-Debrid y no está disponible el estado de descarga de sondeo todavía.",
|
||||
"update_playtime_title": "Actualizar tiempo de juego",
|
||||
"update_playtime_description": "Actualizar manualmente el tiempo de juego para {{game}}",
|
||||
"update_playtime": "Actualizar tiempo de juego",
|
||||
"update_playtime_success": "Tiempo de juego actualizado éxitosamente",
|
||||
"update_playtime_error": "Error al actualizar el tiempo de juego",
|
||||
"update_game_playtime": "Actualizar tu tiempo de juego",
|
||||
"manual_playtime_warning": "Tus horas de juego se marcarán como actualizadas manualmente, y esto no se puede deshacer.",
|
||||
"manual_playtime_tooltip": "Este tiempo de juego se ha actualizad manualmente",
|
||||
"download_error_not_cached_on_torbox": "Esta descarga no está disponible en TorBox y no está disponible el estado de descarga de sondeo todavía.",
|
||||
"download_error_not_cached_on_hydra": "Esta descarga no está disponible en Nimbus.",
|
||||
"game_removed_from_favorites": "Juego eliminado de favoritos",
|
||||
"reset_achievements_description": "Esto reiniciará todos los logros de {{game}}",
|
||||
"reset_achievements_title": "¿Estás seguro?",
|
||||
"reset_achievements_success": "Logros reiniciados exitosamente",
|
||||
"reset_achievements_error": "Se produjo un error al reiniciar los logros",
|
||||
"download_error_gofile_quota_exceeded": "Has excedido la cuota mensual de Gofile. Por favor espera a que se reinicie la cuota.",
|
||||
"download_error_real_debrid_account_not_authorized": "Tu cuenta de Real-Debrid no está autorizada para nueva descargas. Por favor, revisa los ajustes de tu cuenta e intenta de nuevo.",
|
||||
"download_error_not_cached_on_real_debrid": "Esta descarga no está disponible en Real-Debrid y el estado de descarga del sondeo de Real-Debrid aún no está disponible.",
|
||||
"download_error_not_cached_on_torbox": "Esta descarga no está disponible en TorBox y el estado de descarga del sondeo aún no está disponible.",
|
||||
"game_added_to_favorites": "Juego añadido a favoritos",
|
||||
"game_removed_from_pinned": "Juego removido de fijados",
|
||||
"game_added_to_pinned": "Juego añadido a fijados",
|
||||
"automatically_extract_downloaded_files": "Extraer automáticamente archivos descargados",
|
||||
"create_start_menu_shortcut": "Crear un atajo en el Menú de Inicio",
|
||||
"invalid_wine_prefix_path": "Ruta inválida del prefijo de Wine",
|
||||
"invalid_wine_prefix_path_description": "La ruta al prefijo de Wine es inválida. Por favor revisá la ruta y probá de nuevo.",
|
||||
"missing_wine_prefix": "EL prefijo de Wine es requerido para hacer una copia en Linux",
|
||||
"artifact_renamed": "Copia de seguridad renombrada éxitosamente",
|
||||
"rename_artifact": "Renombrar copia de seguridad",
|
||||
"rename_artifact_description": "Renombrar copia de seguridad con un nombre más descriptivo",
|
||||
"artifact_name_label": "Nombre de la copia de seguridad",
|
||||
"artifact_name_placeholder": "Introducí un nombre para la copia de seguridad",
|
||||
"save_changes": "Guardar cambios",
|
||||
"required_field": "Este campo es requerido",
|
||||
"max_length_field": "Este campo debe tener menos de {{length}} carácteres",
|
||||
"freeze_backup": "Fíjalo así no se re-escríbira por copias de seguridad automáticas",
|
||||
"unfreeze_backup": "Dejar de fijar",
|
||||
"backup_frozen": "Copia de seguridad fijada",
|
||||
"backup_unfrozen": "Copia de seguridad desfijada",
|
||||
"backup_freeze_failed": "Error al congelar tu copia de seguridad",
|
||||
"backup_freeze_failed_description": "Tenés que tener mínimo un espacio para copias de seguridad automáticas",
|
||||
"edit_game_modal_button": "Personalizar recursos de juego",
|
||||
"game_details": "Detalles del juego",
|
||||
"currency_symbol": "$",
|
||||
"currency_country": "us",
|
||||
"prices": "Precios",
|
||||
"no_prices_found": "No se encontraron precios",
|
||||
"view_all_prices": "Presioná acá para ver todos los precios",
|
||||
"retail_price": "Precio recomendado",
|
||||
"keyshop_price": "Precio de tiendas de terceros",
|
||||
"historical_retail": "Precio de tiendas",
|
||||
"historical_keyshop": "Precio de tiendas de terceros",
|
||||
"language": "Idioma",
|
||||
"caption": "Subtítulo",
|
||||
"audio": "Audio"
|
||||
"game_removed_from_favorites": "Juego removido de favoritos",
|
||||
"invalid_wine_prefix_path": "Ruta de prefixo Wine inválida",
|
||||
"invalid_wine_prefix_path_description": "La ruta al prefixo Wine es inválida. Por favor, verifica la ruta y vuelve a intentarlo.",
|
||||
"missing_wine_prefix": ""
|
||||
},
|
||||
"activation": {
|
||||
"title": "Activar Hydra",
|
||||
"installation_id": "ID de Instalación:",
|
||||
"enter_activation_code": "Introducí tu código de activación",
|
||||
"message": "Si no sabes donde preguntar por esto, entonces no tenés que tener esto.",
|
||||
"installation_id": "ID de la Instalación:",
|
||||
"enter_activation_code": "Introduce tu código de activación",
|
||||
"message": "Si no sabes donde obtener el código, no deberías de tener esto.",
|
||||
"activate": "Activar",
|
||||
"loading": "Cargando…"
|
||||
},
|
||||
"downloads": {
|
||||
"resume": "Resumir",
|
||||
"pause": "Pausar",
|
||||
"eta": "Tiempo de finalizción {{eta}}",
|
||||
"paused": "Pausado",
|
||||
"pause": "Pausa",
|
||||
"eta": "Finalizando en {{eta}}",
|
||||
"paused": "En Pausa",
|
||||
"verifying": "Verificando…",
|
||||
"completed": "Completado",
|
||||
"removed": "No descargado",
|
||||
"cancel": "Cancelar",
|
||||
"filter": "Filtrar juegos descargados",
|
||||
"remove": "Remover",
|
||||
"filter": "Buscar juegos descargados",
|
||||
"remove": "Eliminar",
|
||||
"downloading_metadata": "Descargando metadatos…",
|
||||
"deleting": "Eliminado instalador…",
|
||||
"delete": "Remover instalador",
|
||||
"delete_modal_title": "¿Querés continuar?",
|
||||
"delete_modal_description": "Esto eliminará todos los archivos del instalador de tu computadora",
|
||||
"deleting": "Eliminando instalador…",
|
||||
"delete": "Eliminar instalador",
|
||||
"delete_modal_title": "¿Estás seguro?",
|
||||
"delete_modal_description": "Esto eliminará todos los archivos de la instalación del repack del juego de tu computadora. (Si ya instalaste el juego, puedes eliminar esto, no afectará al juego)",
|
||||
"install": "Instalar",
|
||||
"download_in_progress": "En progreso",
|
||||
"queued_downloads": "Descargas en cola",
|
||||
"downloads_completed": "Completado",
|
||||
"queued": "En cola",
|
||||
"no_downloads_title": "Esto está... tan, ¿vacío?",
|
||||
"no_downloads_description": "No has descargado nada con Hydra, pero nunca es tarde para comenzar.",
|
||||
"checking_files": "Revisando archivos…",
|
||||
"seeding": "Sembrando",
|
||||
"stop_seeding": "Dejar de sembrar",
|
||||
"resume_seeding": "Continuar sembrando",
|
||||
"options": "Administrar",
|
||||
"no_downloads_title": "Esto está tan... vacío",
|
||||
"no_downloads_description": "No has descargado nada con Hydra... aún, ¡pero nunca es tarde para comenzar!.",
|
||||
"checking_files": "Verificando archivos…",
|
||||
"seeding": "Seeding",
|
||||
"stop_seeding": "Detener seeding",
|
||||
"resume_seeding": "Continuar seeding",
|
||||
"extract": "Extraer archivos",
|
||||
"extracting": "Extrayendo archivos…"
|
||||
"extracting": "Extrayendo archivos…",
|
||||
"options": "Gestionar"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Ruta de descarga",
|
||||
"change": "Actualizar",
|
||||
"common_redist": "Common redistributables",
|
||||
"common_redist_description": "Las Common redistributables son requeridos para ejecutar algunos juegos. Es recomendado instalarlos para evitar problemas.",
|
||||
"create_real_debrid_account": "Presiona acá si no tienes una cuenta de Real-Debrid aún",
|
||||
"create_torbox_account": "Presiona acá si no tienes una cuenta de TorBox aún",
|
||||
"install_common_redist": "Instalar",
|
||||
"installing_common_redist": "Instalando…",
|
||||
"show_download_speed_in_megabytes": "Mostrar velocidad de descargar en megabytes por segundo",
|
||||
"change": "Cambiar",
|
||||
"notifications": "Notificaciones",
|
||||
"enable_download_notifications": "Cuando una descarga se completa",
|
||||
"enable_repack_list_notifications": "Cuando un nuevo repack se añade",
|
||||
"real_debrid_api_token_label": "Real-Debrid API token",
|
||||
"quit_app_instead_hiding": "No ocultar Hydra cuando se cierra",
|
||||
"launch_with_system": "Iniciar Hydra con el sistema",
|
||||
"enable_download_notifications": "Cuando se completa una descarga",
|
||||
"enable_repack_list_notifications": "Cuando se añade un repack nuevo",
|
||||
"real_debrid_api_token_label": "Token API de Real-Debrid",
|
||||
"quit_app_instead_hiding": "Salir de Hydra en vez de minimizar en la bandeja del sistema",
|
||||
"launch_with_system": "Iniciar Hydra al inicio del sistema",
|
||||
"general": "General",
|
||||
"behavior": "Comportamiento",
|
||||
"download_sources": "Fuentes de descargas",
|
||||
"behavior": "Otros",
|
||||
"download_sources": "Fuentes de descarga",
|
||||
"language": "Idioma",
|
||||
"api_token": "API Token",
|
||||
"enable_real_debrid": "Habilitar Real-Debrid",
|
||||
"real_debrid_description": "Real-Debrid es un descargador que te permite descargar archivos más rápidos, solo límitado por la velocidad de tu internet.",
|
||||
"debrid_invalid_token": "Token API inválido",
|
||||
"debrid_api_token_hint": "Podés obtener la el token de tu API <0>acá</0>",
|
||||
"real_debrid_free_account_error": "La cuenta \"{{username}}\" es una cuenta gratis. Por favor suscribíte a Real-Debrid",
|
||||
"api_token": "Token API",
|
||||
"enable_real_debrid": "Activar Real-Debrid",
|
||||
"real_debrid_description": "Real-Debrid es una forma de descargar sin restricciones archivos instantáneamente con la máxima velocidad de tu internet.",
|
||||
"debrid_invalid_token": "Token de API inválido",
|
||||
"debrid_api_token_hint": "Puedes obtener tu clave de API <0>aquí</0>",
|
||||
"real_debrid_free_account_error": "La cuenta \"{{username}}\" es una cuenta gratuita. Por favor, suscríbete a Real-Debrid",
|
||||
"debrid_linked_message": "Cuenta \"{{username}}\" vinculada",
|
||||
"save_changes": "Guardar cambios",
|
||||
"changes_saved": "Cambios guardados éxitosamente",
|
||||
"download_sources_description": "Hydra va a recoger los links de descarga de cada fuente. La URL de origen debe ser un enlace .json que contenga los enlaces de descarga.",
|
||||
"changes_saved": "Ajustes guardados exitosamente",
|
||||
"download_sources_description": "Hydra buscará los enlaces de descarga de estas fuentes. La URL de origen debe ser un enlace directo a un archivo .json que contenga los enlaces de descarga",
|
||||
"validate_download_source": "Validar",
|
||||
"remove_download_source": "Remover",
|
||||
"add_download_source": "Añadir fuente",
|
||||
"download_count_zero": "Sin opciones de descarga",
|
||||
"download_count_one": "{{countFormatted}} opción de descarga",
|
||||
"download_count_other": "{{countFormatted}} opciones de descarga",
|
||||
"download_source_url": "Descargar fuente URL",
|
||||
"add_download_source_description": "Introducí la URL del archivo .json",
|
||||
"download_source_up_to_date": "Actualizado",
|
||||
"remove_download_source": "Eliminar",
|
||||
"add_download_source": "Añadir fuente de descarga",
|
||||
"download_count_zero": "No hay descargas en la lista",
|
||||
"download_count_one": "{{countFormatted}} descarga en la lista",
|
||||
"download_count_other": "{{countFormatted}} descargas en la lista",
|
||||
"download_source_url": "Descargar URL de origen",
|
||||
"add_download_source_description": "Introduce la URL con el archivo .json",
|
||||
"download_source_up_to_date": "Al día",
|
||||
"download_source_errored": "Error",
|
||||
"sync_download_sources": "Sincronizar fuentes",
|
||||
"removed_download_source": "Fuente de descarga eliminada",
|
||||
"removed_download_sources": "Fuente de descarga eliminadas",
|
||||
"cancel_button_confirmation_delete_all_sources": "No",
|
||||
"confirm_button_confirmation_delete_all_sources": "Si, eliminar todo",
|
||||
"confirm_button_confirmation_delete_all_sources": "Sí, eliminar todo",
|
||||
"description_confirmation_delete_all_sources": "Eliminarás todas las fuentes de descarga",
|
||||
"title_confirmation_delete_all_sources": "Eliminar todas las fuentes de descarga",
|
||||
"description_confirmation_delete_all_sources": "Vas a eliminar todas las fuentes de descargas",
|
||||
"button_delete_all_sources": "Eliminar todo",
|
||||
"added_download_source": "Añadir fuente de descarga",
|
||||
"download_sources_synced": "Todas las fuentes de descarga están sincronizadas",
|
||||
"insert_valid_json_url": "Introducí una URL de json válida",
|
||||
"found_download_option_zero": "Sin opciones de descargas encontrada",
|
||||
"found_download_option_one": "Encontrada {{countFormatted}} fuente de descarga",
|
||||
"found_download_option_other": "Encontradas {{countFormatted}} opciones de descargas",
|
||||
"removed_download_sources": "Fuentes de descarga eliminadas",
|
||||
"button_delete_all_sources": "Eliminar todas las fuentes de descarga",
|
||||
"added_download_source": "Fuente de descarga añadida",
|
||||
"download_sources_synced": "Todas las fuentes de descargas están actualizadas.",
|
||||
"insert_valid_json_url": "Introduce una URL JSON válida",
|
||||
"found_download_option_zero": "No se encontró una opción de descarga",
|
||||
"found_download_option_one": "Se encontró {{countFormatted}} opción de descarga",
|
||||
"found_download_option_other": "Se encontraron {{countFormatted}} opciones de descarga",
|
||||
"import": "Importar",
|
||||
"importing": "Importando...",
|
||||
"public": "Público",
|
||||
"private": "Privado",
|
||||
"friends_only": "Sólo amigos",
|
||||
"friends_only": "Solo amigos",
|
||||
"privacy": "Privacidad",
|
||||
"profile_visibility": "Visibilidad del perfil",
|
||||
"profile_visibility_description": "Elegí quién puede ver tú perfil y biblioteca",
|
||||
"required_field": "Este campo es requerido",
|
||||
"source_already_exists": "Esta fuente ya está añadida",
|
||||
"must_be_valid_url": "La fuente debe ser una URL válida",
|
||||
"profile_visibility_description": "Elige quién puede ver tu perfil y biblioteca",
|
||||
"required_field": "Este campo es obligatorio",
|
||||
"source_already_exists": "Esta fuente ya ha sido agregada.",
|
||||
"must_be_valid_url": "La fuente debe ser una URL válida.",
|
||||
"blocked_users": "Usuarios bloqueados",
|
||||
"user_unblocked": "Has desbloqueado a este usuario",
|
||||
"enable_achievement_notifications": "Cuando desbloqueás un logro",
|
||||
"user_unblocked": "El usuario ha sido desbloqueado",
|
||||
"enable_achievement_notifications": "Cuando un logro se desbloquea",
|
||||
"launch_minimized": "Iniciar Hydra minimizado",
|
||||
"disable_nsfw_alert": "Deshabilitar alerta de NSFW",
|
||||
"seed_after_download_complete": "Sembrar después de completar una descarga",
|
||||
"show_hidden_achievement_description": "Mostrar logros ocultos antes de desbloquearlos",
|
||||
"disable_nsfw_alert": "Desactivar alerta NSFW",
|
||||
"seed_after_download_complete": "Realizar seeding después de que se completa la descarga",
|
||||
"show_hidden_achievement_description": "Ocultar descripción de logros ocultos antes de desbloquearlos",
|
||||
"account": "Cuenta",
|
||||
"no_users_blocked": "No has bloqueado a ningún usuario",
|
||||
"subscription_active_until": "Tu Hydra Cloud está activo hasta {{date}}",
|
||||
"manage_subscription": "Administrar suscripción",
|
||||
"update_email": "Actualizar correo",
|
||||
"update_password": "Cambiar contraseña",
|
||||
"account_data_updated_successfully": "Datos de la cuenta actualizados",
|
||||
"bill_sent_until": "Tú próxima factura se enviará el {{date}}",
|
||||
"current_email": "Correo actual:",
|
||||
"no_email_account": "No tenés ningún correo vinculado aún",
|
||||
"account_data_updated_successfully": "Datos de la cuenta actualizados correctamente",
|
||||
"manage_subscription": "Gestionar suscripción",
|
||||
"no_email_account": "No has configurado un correo aún",
|
||||
"no_subscription": "Disfruta Hydra de la mejor manera",
|
||||
"no_users_blocked": "No tienes usuarios bloqueados",
|
||||
"renew_subscription": "Renovar Hydra Cloud",
|
||||
"subscription_expired_at": "Tu suscripción expiró el {{date}}",
|
||||
"no_subscription": "Disfrutá Hydra de la mejor forma",
|
||||
"become_subscriber": "Sé parte de Hydra Cloud",
|
||||
"subscription_renew_cancelled": "Renovación automática desactivada",
|
||||
"subscription_renews_on": "Tu suscripción se renueva el {{date}}",
|
||||
"bill_sent_until": "Tu próxima factura se enviará este día",
|
||||
"no_themes": "Parece que no tenés ningún tema aún, pero no te preocupés, presiona acá para hacer tu primera obra maestra.",
|
||||
"subscription_active_until": "Tu Hydra Cloud está activa hasta {{date}}",
|
||||
"subscription_expired_at": "Tú suscripción expiró el {{date}}",
|
||||
"subscription_renew_cancelled": "Está desactivada la renovación automática",
|
||||
"subscription_renews_on": "Tú suscripción se renueva el {{date}}",
|
||||
"update_email": "Actualizar correo",
|
||||
"update_password": "Actualizar contraseña",
|
||||
"appearance": "Apariencia",
|
||||
"become_subscriber": "Sé Hydra Cloud",
|
||||
"cancel": "Cancelar",
|
||||
"clear_themes": "Limpiar",
|
||||
"create_theme": "Crear",
|
||||
"create_theme_modal_description": "Crea un nuevo tema para personalizar la apariencia de Hydra",
|
||||
"create_theme_modal_title": "Crear tema personalizado",
|
||||
"delete_all_themes": "Eliminar todos los temas",
|
||||
"delete_all_themes_description": "Esto eliminará todos tus temas personalizados",
|
||||
"delete_theme": "Eliminar tema",
|
||||
"delete_theme_description": "Esto eliminará el tema {{theme}}",
|
||||
"edit_theme": "Editar tema",
|
||||
"editor_tab_code": "Código",
|
||||
"editor_tab_info": "Info",
|
||||
"editor_tab_save": "Guardar",
|
||||
"web_store": "Tienda Web",
|
||||
"clear_themes": "Limpiar",
|
||||
"create_theme": "Crear",
|
||||
"create_theme_modal_title": "Crear tema personalizado",
|
||||
"create_theme_modal_description": "Crear un nuevo tema para personalizar el estilo de Hydra",
|
||||
"theme_name": "Nombre",
|
||||
"insert_theme_name": "Introducí un nombre del tema",
|
||||
"set_theme": "Usar tema",
|
||||
"unset_theme": "Dejar de usar tema",
|
||||
"delete_theme": "Eliminar tema",
|
||||
"edit_theme": "Editar tema",
|
||||
"delete_all_themes": "Eliminar todos los temas",
|
||||
"delete_all_themes_description": "Esto va a eliminar todos los temas personalizados",
|
||||
"delete_theme_description": "Esto va a eliminar el tema {{theme}}",
|
||||
"cancel": "Cancelar",
|
||||
"appearance": "Apariencia",
|
||||
"enable_torbox": "Activar TorBox",
|
||||
"torbox_description": "TorBox es un servicio premium de seedbox que incluso rivaliza los mejores servidores.",
|
||||
"torbox_account_linked": "Cuenta de TorBox vinculada",
|
||||
"create_real_debrid_account": "Presioná acá si todavía no tenés una cuenta de Real-Debrid",
|
||||
"create_torbox_account": "Presioná acá si todavía no tenés una cuenta de TorBox",
|
||||
"real_debrid_account_linked": "Cuenta de Real-Debrid vinculada",
|
||||
"name_min_length": "El nombre del tema debe tener mínimo 3 carácteres",
|
||||
"enable_torbox": "Habilitar TorBox",
|
||||
"error_importing_theme": "Error al importar el tema",
|
||||
"import_theme": "Importar tema",
|
||||
"import_theme_description": "Vas a importar el tema {{theme}} desde la tienda de temas",
|
||||
"error_importing_theme": "Error al importar el tema",
|
||||
"theme_imported": "Tema importado correctamente",
|
||||
"enable_friend_request_notifications": "Cuando recibís una solicitud de amistad",
|
||||
"enable_auto_install": "Descargar actualizaciones automáticamente",
|
||||
"common_redist": "Common redistributables",
|
||||
"common_redist_description": "Common redistributables son requeridos para algunos juegos. Es recomendable instalarlos para evitar algunos problemas.",
|
||||
"install_common_redist": "Instalar",
|
||||
"installing_common_redist": "Instalando…",
|
||||
"show_download_speed_in_megabytes": "Mostrar velocidad de descarga en megabytes por segundo",
|
||||
"extract_files_by_default": "Extraer archivos por defecto después de descargar",
|
||||
"enable_steam_achievements": "Habilitar búsqueda de logros de Steam",
|
||||
"achievement_custom_notification_position": "Posición de notificación de logros",
|
||||
"top-left": "Superior Izquierda",
|
||||
"top-center": "Superior Centro",
|
||||
"top-right": "Superior Derecha",
|
||||
"bottom-left": "Inferior Izquierda",
|
||||
"bottom-center": "Inferior Centro",
|
||||
"bottom-right": "Inferior Derecha",
|
||||
"enable_achievement_custom_notifications": "Habilitar notificación personalizada de logros",
|
||||
"alignment": "Centrado",
|
||||
"variation": "Variación",
|
||||
"default": "Defecto",
|
||||
"rare": "Raro",
|
||||
"platinum": "Platino",
|
||||
"hidden": "Oculto",
|
||||
"test_notification": "Probar notificación",
|
||||
"notification_preview": "Probar notificación de logro",
|
||||
"enable_friend_start_game_notifications": "Cuando un amigo está jugando un juego"
|
||||
"insert_theme_name": "Introducí el nombre del tema",
|
||||
"name_min_length": "El tema tiene que tener 3 carácteres de largo mínimo",
|
||||
"no_themes": "Parece que no tenés ningún tema aún, pero no te preocupes, presiona acá para crear tu primer tema.",
|
||||
"real_debrid_account_linked": "Cuenta de Real-Debrid vinculada",
|
||||
"set_theme": "Establecer tema",
|
||||
"theme_imported": "Tema importado exitosamente",
|
||||
"theme_name": "Nombre",
|
||||
"torbox_account_linked": "Cuenta de TorBox vinculada",
|
||||
"torbox_description": "TorBox es tu servicio premium de seedbox que rivaliza incluso a los mejores servidores del mercado.",
|
||||
"unset_theme": "Desactivar tema",
|
||||
"web_store": "Tienda Web",
|
||||
"enable_friend_request_notifications": "Cuando se recibe una solicitud de amistad",
|
||||
"enable_auto_install": "Descargar actualizaciones automáticamente"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Descarga completada",
|
||||
"game_ready_to_install": "{{title}} está listo para instalar",
|
||||
"repack_list_updated": "Lista de repacks actualizadas",
|
||||
"repack_count_one": "{{count}} repack añadido",
|
||||
"repack_count_other": "{{count}} repacks añadidos",
|
||||
"new_update_available": "Versión {{version}} disponible",
|
||||
"restart_to_install_update": "Reiniciá Hydra para instalar la actualización",
|
||||
"notification_achievement_unlocked_title": "Logro desbloqueado para {{game}}",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados",
|
||||
"new_friend_request_description": "{{displayName}} te envió una solicitud de amistad",
|
||||
"new_friend_request_title": "Nueva solicitud de amistad",
|
||||
"extraction_complete": "Extracción completada",
|
||||
"game_extracted": "{{title}} extraído exitosamente",
|
||||
"friend_started_playing_game": "{{displayName}} empezó a jugar un juego",
|
||||
"test_achievement_notification_title": "Esto es una notificación de prueba",
|
||||
"test_achievement_notification_description": "Piola, ¿verdad?"
|
||||
"game_ready_to_install": "{{title}} está listo para instalarse",
|
||||
"repack_list_updated": "Lista de repacks actualizadas",
|
||||
"repack_count_one": "{{count}} repack ha sido añadido",
|
||||
"repack_count_other": "{{count}} repacks añadidos",
|
||||
"new_update_available": "Version {{version}} disponible",
|
||||
"restart_to_install_update": "Reinicia Hydra para instalar la actualización",
|
||||
"notification_achievement_unlocked_title": "Logro desbloqueado de {{game}}",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados",
|
||||
"new_friend_request_title": "Nueva solicitud de amistad",
|
||||
"new_friend_request_description": "{{displayName}} te envió una solicitud de amistad",
|
||||
"friend_started_playing_game": "{{displayName}} está jugando"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Abrir Hydra",
|
||||
@@ -492,15 +387,15 @@
|
||||
"game_card": {
|
||||
"available_one": "Disponible",
|
||||
"available_other": "Disponibles",
|
||||
"no_downloads": "Sin descargas disponibles"
|
||||
"no_downloads": "No hay descargas disponibles"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Programas no instalados",
|
||||
"description": "Ejecutables de Wine o Lutris executables no encontrados en tu sistema",
|
||||
"instructions": "Comprobá la forma correcta de instalar cualquiera de ellos en tu distribución de Linux para que el juego pueda ejecutarse con normalidad"
|
||||
"description": "Los ejecutables de Wine o Lutris no se encontraron en tu sistema",
|
||||
"instructions": "Comprueba como instalar de forma correcta uno de los dos en tu distro de Linux para ejecutar el juego con normalidad"
|
||||
},
|
||||
"modal": {
|
||||
"close": "Botón de cerrar"
|
||||
"close": "Botón de cierre"
|
||||
},
|
||||
"forms": {
|
||||
"toggle_password_visibility": "Cambiar visibilidad de contraseña"
|
||||
@@ -508,120 +403,111 @@
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} horas",
|
||||
"amount_minutes": "{{amount}} minutos",
|
||||
"last_time_played": "Jugado por última vez el {{period}}",
|
||||
"last_time_played": "Última vez jugado: {{period}}",
|
||||
"activity": "Actividad reciente",
|
||||
"library": "Librería",
|
||||
"pinned": "Fijado",
|
||||
"total_play_time": "Total de tiempo de juego",
|
||||
"achievements_earned": "Logros conseguidos",
|
||||
"played_recently": "Jugado recientemente",
|
||||
"playtime": "Tiempo de juego",
|
||||
"manual_playtime_tooltip": "Este tiempo de juego ha sido modificado manualmente",
|
||||
"no_recent_activity_title": "Hmmm… nada por acá",
|
||||
"no_recent_activity_description": "No has jugado nada recientemente. ¡Te toca cambiar eso!",
|
||||
"display_name": "Nombre a mostar",
|
||||
"library": "Biblioteca",
|
||||
"total_play_time": "Has jugado",
|
||||
"no_recent_activity_title": "Que raro, no hay nada por acá...",
|
||||
"no_recent_activity_description": "No has jugado ningún juego recientemente, ¡vamos a cambiar eso ahora!",
|
||||
"display_name": "Nombre en pantalla",
|
||||
"saving": "Guardando",
|
||||
"save": "Guardado",
|
||||
"save": "Guardar",
|
||||
"edit_profile": "Editar perfil",
|
||||
"saved_successfully": "Guardado exitosamente",
|
||||
"try_again": "Por favor, intentá de nuevo",
|
||||
"sign_out_modal_title": "¿Querés continuar?",
|
||||
"try_again": "Por favor, intenta de nuevo",
|
||||
"sign_out_modal_title": "¿Estás seguro?",
|
||||
"cancel": "Cancelar",
|
||||
"successfully_signed_out": "Cerraste sesión exitosamente",
|
||||
"successfully_signed_out": "Sesión cerrada exitosamente",
|
||||
"sign_out": "Cerrar sesión",
|
||||
"playing_for": "Jugando por {{amount}}",
|
||||
"sign_out_modal_text": "Tu librería está vinculada con esta cuenta. Cuando cerrés sesión, tu librería ya no será visible, y cualquier progreso no se guardará. ¿Querés continuar con el cierre de sesión?",
|
||||
"add_friends": "Añadir amistades",
|
||||
"playing_for": "Llevas jugando {{amount}}",
|
||||
"sign_out_modal_text": "Tu biblioteca se ha vinculado con tu cuenta. Cuando cierres sesión, tú biblioteca ya no será visible y cualquier progreso no se guardará. ¿Continuar con el cierre de sesión?",
|
||||
"add_friends": "Añadir amigos",
|
||||
"add": "Añadir",
|
||||
"friend_code": "Código de amistad",
|
||||
"friend_code": "Código de amigo",
|
||||
"see_profile": "Ver perfil",
|
||||
"sending": "Enviando",
|
||||
"friend_request_sent": "Solicitud de amistad enviada",
|
||||
"friends": "Amistades",
|
||||
"friends_list": "Lista de amistades",
|
||||
"friends": "Amigos",
|
||||
"friends_list": "Lista de amigos",
|
||||
"user_not_found": "Usuario no encontrado",
|
||||
"block_user": "Bloquear usuario",
|
||||
"add_friend": "Añadir amistad",
|
||||
"add_friend": "Añadir amigo",
|
||||
"request_sent": "Solicitud enviada",
|
||||
"request_received": "Solicitud recibida",
|
||||
"accept_request": "Aceptar solicitud",
|
||||
"ignore_request": "Ignorar solicitud",
|
||||
"cancel_request": "Cancelar solicitud",
|
||||
"undo_friendship": "Deshacer amistad",
|
||||
"undo_friendship": "Eliminar amistad",
|
||||
"request_accepted": "Solicitud aceptada",
|
||||
"user_blocked_successfully": "Usuario bloqueado exitosamente",
|
||||
"user_block_modal_text": "Esto va a bloquear a {{displayName}}",
|
||||
"blocked_users": "Usuarios bloqueados",
|
||||
"unblock": "Desbloquear",
|
||||
"no_friends_added": "No tenés amistades añadidas",
|
||||
"no_friends_added": "Todavía no tienes amigos añadidos",
|
||||
"pending": "Pendiente",
|
||||
"no_pending_invites": "No tenés invitaciones pendientes",
|
||||
"no_blocked_users": "No has bloqueado a nadie",
|
||||
"friend_code_copied": "Código de amistad copiado",
|
||||
"undo_friendship_modal_text": "Esto va a deshacer tu amistad con {{displayName}}",
|
||||
"privacy_hint": "Para cambiar quién puede ver esto, andá a <0>Ajustes</0>",
|
||||
"no_pending_invites": "No tienes invitaciones pendientes",
|
||||
"no_blocked_users": "No has bloqueado a ningún usuario",
|
||||
"friend_code_copied": "Código de amigo copiado",
|
||||
"undo_friendship_modal_text": "Esto deshará tu amistad con {{displayName}}",
|
||||
"privacy_hint": "Para ajustar quién puede ver esto, ve a <0>Configuración</0>.",
|
||||
"locked_profile": "Este perfil es privado",
|
||||
"image_process_failure": "Errpr al procesar la imagen",
|
||||
"required_field": "Este campo es requerido",
|
||||
"displayname_min_length": "El nombre en pantalla debe tener mínimo 3 caracteres",
|
||||
"displayname_max_length": "El nombre en pantalla debe tener máximo 50 caracteres",
|
||||
"image_process_failure": "Error al procesar la imagen",
|
||||
"required_field": "Este campo es obligatorio",
|
||||
"displayname_min_length": "El nombre a mostrar debe tener al menos 3 caracteres",
|
||||
"displayname_max_length": "El nombre a mostrar debe tener como máximo 50 caracteres",
|
||||
"report_profile": "Reportar este perfil",
|
||||
"report_reason": "¿Porque estás reportando este perfil?",
|
||||
"report_reason": "¿Cual es el motivo del reporte?",
|
||||
"report_description": "Información adicional",
|
||||
"report_description_placeholder": "Información adicional",
|
||||
"report": "Reportar",
|
||||
"report_reason_hate": "Discursos de odio",
|
||||
"report_reason_sexual_content": "Contenido sexual",
|
||||
"report_reason_violence": "Violencia",
|
||||
"report_reason_spam": "Spam",
|
||||
"report_reason_other": "Otros",
|
||||
"report_reason_spam": "Spam / Contenido no deseado",
|
||||
"report_reason_other": "Otro",
|
||||
"profile_reported": "Perfil reportado",
|
||||
"your_friend_code": "Tu código de amistad:",
|
||||
"upload_banner": "Subir banner",
|
||||
"your_friend_code": "Tu código de amigo:",
|
||||
"upload_banner": "Subir un banner",
|
||||
"uploading_banner": "Subiendo banner…",
|
||||
"background_image_updated": "Imagen de fondo actualizada",
|
||||
"stats": "Estadísticas",
|
||||
"achievements": "logros",
|
||||
"games": "Juegos",
|
||||
"top_percentile": "Top {{percentile}}%",
|
||||
"ranking_updated_weekly": "El ranking se actualiza semanalmente",
|
||||
"playing": "Jugando {{game}}",
|
||||
"achievements": "logros",
|
||||
"achievements_unlocked": "Logros desbloqueados",
|
||||
"earned_points": "Puntos obtenidos",
|
||||
"show_achievements_on_profile": "Mostrá tus logros en tu perfil",
|
||||
"show_points_on_profile": "Mostrá los puntos obtenidos en tu perfil",
|
||||
"error_adding_friend": "No se pudo enviar la solicitud de amistad. Por favor revisá el código",
|
||||
"friend_code_length_error": "El código de amistad debe tener mínimo 8 caracteres",
|
||||
"game_removed_from_pinned": "Juego removido de fijados",
|
||||
"game_added_to_pinned": "Juego añadido a fijados"
|
||||
"earned_points": "Puntos Obtenidos",
|
||||
"show_achievements_on_profile": "Mostrar tus logros en tu perfil",
|
||||
"show_points_on_profile": "Mostrar tus puntos obtenidos en tu perfil",
|
||||
"games": "Juegos",
|
||||
"ranking_updated_weekly": "El Ranking se actualiza semanalmente",
|
||||
"stats": "Estadísticas",
|
||||
"top_percentile": "Top {{percentile}}%"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Logro desbloqueado",
|
||||
"user_achievements": "Logros de {{displayName}}",
|
||||
"your_achievements": "Tus logros",
|
||||
"your_achievements": "Tus Logros",
|
||||
"unlocked_at": "Desbloqueado el: {{date}}",
|
||||
"subscription_needed": "Se requiere una suscripción a Hydra Cloud para ver esto",
|
||||
"new_achievements_unlocked": "Desbloqueaste {{achievementCount}} nuevos logros de {{gameCount}} juegos",
|
||||
"subscription_needed": "Se necesita una suscripción a Hydra Cloud necesita para ver este contenido",
|
||||
"new_achievements_unlocked": "Desbloqueados {{achievementCount}} nuevos logros de {{gameCount}} juegos",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} logros",
|
||||
"achievements_unlocked_for_game": "Desbloqueaste {{achievementCount}} nuevos logros para {{gameTitle}}",
|
||||
"achievements_unlocked_for_game": "Se han desbloqueado {{achievementCount}} nuevos logros de {{gameTitle}}",
|
||||
"hidden_achievement_tooltip": "Este es un logro oculto",
|
||||
"achievement_earn_points": "Conseguí {{points}} puntos por este logro",
|
||||
"achievement_earn_points": "Obtén {{points}} puntos con este logro",
|
||||
"earned_points": "Puntos obtenidos:",
|
||||
"available_points": "Puntos disponibles:",
|
||||
"how_to_earn_achievements_points": "¿Como conseguir puntos por logros?"
|
||||
"how_to_earn_achievements_points": "¿Cómo obtener puntos de logros?"
|
||||
},
|
||||
"hydra_cloud": {
|
||||
"subscription_tour_title": "Suscripción Hydra Cloud",
|
||||
"subscribe_now": "suscribíte ahora",
|
||||
"debrid_description": "Descargas hasta x4 más rápidas con Nimbus",
|
||||
"subscribe_now": "Suscribirse ahora",
|
||||
"cloud_saving": "Guardado en la nube",
|
||||
"cloud_achievements": "Guardá tus logros en la nube",
|
||||
"animated_profile_picture": "Foto de perfil animada",
|
||||
"cloud_achievements": "Guarda tus logros en la nube",
|
||||
"animated_profile_picture": "Fotos de perfil animadas",
|
||||
"premium_support": "Soporte Premium",
|
||||
"show_and_compare_achievements": "Mostrá y compará tus logros con otros usuarios",
|
||||
"animated_profile_banner": "Banner de perfil animado",
|
||||
"show_and_compare_achievements": "Muestra y compara tus logros con otros usuarios",
|
||||
"animated_profile_banner": "Fondo de perfil animado",
|
||||
"hydra_cloud": "Hydra Cloud",
|
||||
"hydra_cloud_feature_found": "¡Acabas de descubrir una característica de Hydra Cloud!",
|
||||
"learn_more": "Descubrir más",
|
||||
"debrid_description": "Descargas hasta x4 veces más rápidas con Nimbus"
|
||||
"hydra_cloud_feature_found": "¡Has descubierto una característica de Hydra Cloud!",
|
||||
"learn_more": "Aprender más"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Edukalt sisse logitud"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Esile toodud",
|
||||
"surprise_me": "Üllata mind",
|
||||
"no_results": "Tulemusi ei leitud",
|
||||
"start_typing": "Alusta otsimiseks kirjutamist...",
|
||||
@@ -44,7 +45,10 @@
|
||||
"calculating_eta": "{{title}} allalaadimine… ({{percentage}} valmis) - Järelejäänud aja arvutamine…",
|
||||
"checking_files": "{{title}} failide kontrollimine… ({{percentage}} valmis)"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Järgmine leht",
|
||||
"previous_page": "Eelmine leht"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Ava allalaadimise valikud",
|
||||
"download_options_zero": "Allalaadimise valikuid pole",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"language_name": "فارسی",
|
||||
"home": {
|
||||
"featured": "پیشنهادی",
|
||||
"surprise_me": "سوپرایزم کن",
|
||||
"no_results": "اتمامای پیدا نشد"
|
||||
},
|
||||
@@ -16,6 +17,7 @@
|
||||
"home": "خانه",
|
||||
"favorites": "علاقهمندیها"
|
||||
},
|
||||
|
||||
"header": {
|
||||
"search": "جستجوی بازیها",
|
||||
"home": "خانه",
|
||||
@@ -29,7 +31,10 @@
|
||||
"downloading_metadata": "درحال دانلود متادیتاهای {{title}}…",
|
||||
"downloading": "در حال دانلود {{title}}… ({{percentage}} تکمیل شده) - اتمام {{eta}} - {{speed}}"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "صفحهی بعدی",
|
||||
"previous_page": "صفحهی قبلی"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "بازکردن آپشنهای دانلود",
|
||||
"download_options_zero": "هیچ آپشن دانلودی وجود ندارد",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Connecté avec succès"
|
||||
},
|
||||
"home": {
|
||||
"featured": "En vedette",
|
||||
"surprise_me": "Surprenez-moi",
|
||||
"no_results": "Aucun résultat trouvé",
|
||||
"start_typing": "Commencez à taper pour rechercher...",
|
||||
|
||||
@@ -1,131 +1,45 @@
|
||||
{
|
||||
"language_name": "Magyar",
|
||||
"app": {
|
||||
"successfully_signed_in": "Sikeresen bejelentkeztél"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Featured",
|
||||
"surprise_me": "Lepj meg",
|
||||
"no_results": "Nincs találat",
|
||||
"start_typing": "Kereséshez gépelj...",
|
||||
"hot": "Most felkapott",
|
||||
"weekly": "📅 A hét felkapott játékai",
|
||||
"achievements": "🏆 Achievement támogatott"
|
||||
"no_results": "Nem található"
|
||||
},
|
||||
"sidebar": {
|
||||
"catalogue": "Katalógus",
|
||||
"downloads": "Letöltések",
|
||||
"settings": "Beállítások",
|
||||
"my_library": "Könyvtáram",
|
||||
"downloading_metadata": "{{title}} (metaadatai letöltése…)",
|
||||
"paused": "{{title}} (Szüneteltetve)",
|
||||
"downloading_metadata": "{{title}} (Metadata letöltése…)",
|
||||
"paused": "{{title}} (Szünet)",
|
||||
"downloading": "{{title}} ({{percentage}} - Letöltés…)",
|
||||
"filter": "Könyvtár szűrése",
|
||||
"home": "Főoldal",
|
||||
"queued": "A(z) {{title}} (Várakozósorban van)",
|
||||
"game_has_no_executable": "A játékhoz nincs tallózva futtatható fájl",
|
||||
"sign_in": "Bejelentkezés",
|
||||
"friends": "Barátok",
|
||||
"need_help": "Elakadtál?",
|
||||
"favorites": "Kedvenc játékok",
|
||||
"playable_button_title": "Csak az azonnal játszható játékokat mutasd",
|
||||
"add_custom_game_tooltip": "Saját játék hozzáadása",
|
||||
"show_playable_only_tooltip": "Csak játszható játék mutatása",
|
||||
"custom_game_modal": "Saját játék hozzáadása:",
|
||||
"custom_game_modal_description": "Adj meg egy futtatható fájlt",
|
||||
"custom_game_modal_executable_path": "Futtatható fájl",
|
||||
"custom_game_modal_select_executable": "Fájl útvonala",
|
||||
"custom_game_modal_title": "Játékcím",
|
||||
"custom_game_modal_enter_title": "Játék elnevezése",
|
||||
"custom_game_modal_browse": "Tallózás",
|
||||
"custom_game_modal_cancel": "Mégse",
|
||||
"custom_game_modal_add": "Játék hozzáadása",
|
||||
"custom_game_modal_adding": "Játék hozzáadása...",
|
||||
"custom_game_modal_success": "Saját játék sikeresen hozzáadva",
|
||||
"custom_game_modal_failed": "Saját játék hozzáadása sikertelen",
|
||||
"custom_game_modal_executable": "Futtatható fájl",
|
||||
"edit_game_modal": "Játékmegjelenítése:",
|
||||
"edit_game_modal_description": "Játékcím és vizuális elemek módosítása",
|
||||
"edit_game_modal_title": "Játékcím",
|
||||
"edit_game_modal_enter_title": "Játék elnevezése",
|
||||
"edit_game_modal_image": "Kép",
|
||||
"edit_game_modal_select_image": "Kép útvonala",
|
||||
"edit_game_modal_browse": "Tallózás",
|
||||
"edit_game_modal_image_preview": "Kép előnézete",
|
||||
"edit_game_modal_icon": "Ikon",
|
||||
"edit_game_modal_select_icon": "Ikon útvonala",
|
||||
"edit_game_modal_icon_preview": "Ikon előnézete",
|
||||
"edit_game_modal_logo": "Logó",
|
||||
"edit_game_modal_select_logo": "Logó útvonala",
|
||||
"edit_game_modal_logo_preview": "Logó előnézete",
|
||||
"edit_game_modal_hero": "Borítókép",
|
||||
"edit_game_modal_select_hero": "Borítókép útvonala",
|
||||
"edit_game_modal_hero_preview": "Borítókép előnézete",
|
||||
"edit_game_modal_cancel": "Mégse",
|
||||
"edit_game_modal_update": "Frissít",
|
||||
"edit_game_modal_updating": "Frissítés...",
|
||||
"edit_game_modal_fill_required": "Kérlek töltsd ki az összes kötelező mezőt",
|
||||
"edit_game_modal_success": "Játék megjelenés frissítése sikeres",
|
||||
"edit_game_modal_failed": "Játék megjelenés frissítése sikertelen",
|
||||
"edit_game_modal_image_filter": "Kép",
|
||||
"edit_game_modal_icon_resolution": "Ajánlott felbontás: 256x256px",
|
||||
"edit_game_modal_logo_resolution": "Ajánlott felbontás: 640x360px",
|
||||
"edit_game_modal_hero_resolution": "Ajánlott felbontás: 1920x620px",
|
||||
"edit_game_modal_assets": "Vizuális elemek:",
|
||||
"edit_game_modal_drop_icon_image_here": "Húzd ide az ikon képét",
|
||||
"edit_game_modal_drop_logo_image_here": "Húzd ide a logó képét",
|
||||
"edit_game_modal_drop_hero_image_here": "Húzd ide a borítókép képét",
|
||||
"edit_game_modal_drop_to_replace_icon": "Ikon kicserélése ráhúzással",
|
||||
"edit_game_modal_drop_to_replace_logo": "Logó kicserélése ráhúzással",
|
||||
"edit_game_modal_drop_to_replace_hero": "Borítókép kicserélése ráhúzással",
|
||||
"install_decky_plugin": "Decky Plugin Telepítése",
|
||||
"update_decky_plugin": "Decky Plugin Frissítése",
|
||||
"decky_plugin_installed_version": "Decky Plugin (v{{version}})",
|
||||
"install_decky_plugin_title": "Telepítsd a Hydra Decky Plugint",
|
||||
"install_decky_plugin_message": "Ez letölti és telepíteni fogja a Hydra plugint a Decky Loaderhez. Előfordulhat, hogy rendszergazdai jogosultságra lesz szükség. Folytatod?",
|
||||
"update_decky_plugin_title": "Hydra Decky Plugin Frissítése",
|
||||
"update_decky_plugin_message": "Egy új verzió elérhető a Hydra Decky Pluginhoz. Szeretnéd frissíteni?",
|
||||
"decky_plugin_installed": "Decky plugin v{{version}} sikeresen telepítve",
|
||||
"decky_plugin_installation_failed": "Decky plugin telepítése sikertelen: {{error}}",
|
||||
"decky_plugin_installation_error": "Decky plugin telepítése hibával járt el: {{error}}",
|
||||
"confirm": "Megerősít",
|
||||
"cancel": "Mégse"
|
||||
"favorites": "Kedvenc játékok"
|
||||
},
|
||||
"header": {
|
||||
"search": "Keresés",
|
||||
|
||||
"home": "Főoldal",
|
||||
"catalogue": "Katalógus",
|
||||
"downloads": "Letöltések",
|
||||
"search_results": "Keresési találatok",
|
||||
"settings": "Beállítások",
|
||||
"version_available_install": "A(z) {{version}} verzió elérhető. Kattints ide az újraindításhoz és telepítéshez.",
|
||||
"version_available_download": "A(z) {{version}} verzió elérhető. A letöltéshez kattints ide."
|
||||
"search_results": "Keresési eredmények",
|
||||
"settings": "Beállítások"
|
||||
},
|
||||
"bottom_panel": {
|
||||
"no_downloads_in_progress": "Nincs folyamatban lévő letöltés",
|
||||
"no_downloads_in_progress": "Nincsenek folyamatban lévő letöltések",
|
||||
"downloading_metadata": "{{title}} metaadatainak letöltése…",
|
||||
"downloading": "{{title}} letöltése… ({{percentage}} kész) - Befejezés {{eta}} - {{speed}}",
|
||||
"calculating_eta": "{{title}} letöltése… ({{percentage}} kész) - Hátralévő idő…",
|
||||
"checking_files": "A(z) {{title}} fájljaiból… ({{percentage}} kész)",
|
||||
"installing_common_redist": "{{log}}…",
|
||||
"installation_complete": "Telepítés befejezve",
|
||||
"installation_complete_message": "A(z) Alapvető segédprogramok sikeresen telepítve"
|
||||
"downloading": "{{title}} letöltése… ({{percentage}} kész) - Befejezés {{eta}} - {{speed}}"
|
||||
},
|
||||
"catalogue": {
|
||||
"search": "Szűrés…",
|
||||
"developers": "Fejlesztők",
|
||||
"genres": "Műfajok",
|
||||
"tags": "Címkék",
|
||||
"publishers": "Kiadók",
|
||||
"download_sources": "Letöltési források",
|
||||
"result_count": "{{resultCount}} találatok",
|
||||
"filter_count": "{{filterCount}} elérhető",
|
||||
"clear_filters": "{{filterCount}} kiválaszott szűrő törlése"
|
||||
"next_page": "Következő olda",
|
||||
"previous_page": "Előző olda"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Letöltési opciók megnyitása",
|
||||
"download_options_zero": "Nincs letöltési opció",
|
||||
"download_options_one": "{{count}} letöltési opció",
|
||||
"download_options_other": "{{count}} letöltési opció",
|
||||
"open_download_options": "Letöltési lehetőségek",
|
||||
"download_options_zero": "Nincs letöltési lehetőség",
|
||||
"download_options_one": "{{count}} letöltési lehetőség",
|
||||
"download_options_other": "{{count}} letöltési lehetőség",
|
||||
"updated_at": "Frissítve: {{updated_at}}",
|
||||
"install": "Letöltés",
|
||||
"resume": "Folytatás",
|
||||
@@ -134,13 +48,11 @@
|
||||
"remove": "Eltávolítás",
|
||||
"space_left_on_disk": "{{space}} szabad hely a lemezen",
|
||||
"eta": "Befejezés {{eta}}",
|
||||
"calculating_eta": "Hátralevő idő kiszámítása…",
|
||||
"downloading_metadata": "Metaadat letöltése",
|
||||
"downloading_metadata": "Metaadatok letöltése…",
|
||||
"filter": "Repackek szűrése",
|
||||
"requirements": "Rendszerkövetelmények",
|
||||
"minimum": "Minimális",
|
||||
"recommended": "Ajánlott",
|
||||
"paused": "Szüneteltetve",
|
||||
"release_date": "Megjelenés: {{date}}",
|
||||
"publisher": "Kiadta: {{publisher}}",
|
||||
"hours": "óra",
|
||||
@@ -148,223 +60,29 @@
|
||||
"amount_hours": "{{amount}} óra",
|
||||
"amount_minutes": "{{amount}} perc",
|
||||
"accuracy": "{{accuracy}}% pontosság",
|
||||
"add_to_library": "Könyvtárba helyezés",
|
||||
"already_in_library": "Már könyvtárban",
|
||||
"add_to_library": "Hozzáadás a könyvtárhoz",
|
||||
"remove_from_library": "Eltávolítás a könyvtárból",
|
||||
"no_downloads": "Nincs elérhető letöltés",
|
||||
"play_time": "Játszva: {{amount}}",
|
||||
"last_time_played": "Utoljára játszva: {{period}}",
|
||||
"not_played_yet": "Ezzel a játékkal még nem játszottál: {{title}}",
|
||||
"last_time_played": "Utoljára játszva {{period}}",
|
||||
"not_played_yet": "{{title}} még nem játszottál",
|
||||
"next_suggestion": "Következő javaslat",
|
||||
"play": "Játék",
|
||||
"deleting": "Telepítő törlése…",
|
||||
"close": "Bezárás",
|
||||
"playing_now": "Játékban: ",
|
||||
"playing_now": "Jelenleg játszva",
|
||||
"change": "Változtatás",
|
||||
"repacks_modal_description": "Válaszd ki a repacket amit leszeretnél tölteni",
|
||||
"select_folder_hint": "A letöltési mappát a <0>Beállítások</0> menüjében változtathatod meg",
|
||||
"download_now": "Letöltés",
|
||||
"no_shop_details": "A bolt adatai nem érhetőek el.",
|
||||
"download_options": "Letöltési opciók",
|
||||
"download_path": "Letöltis hely",
|
||||
"previous_screenshot": "Előző screenshot",
|
||||
"next_screenshot": "Következő screenshot",
|
||||
"screenshot": "Screenshot {{number}}",
|
||||
"open_screenshot": "Screenshot megnyitása {{number}}",
|
||||
"download_settings": "Letöltési beállítások",
|
||||
"downloader": "Letöltési mód",
|
||||
"select_executable": "Tallózás",
|
||||
"no_executable_selected": "Nincs futtatható fájl tallózva",
|
||||
"open_folder": "Mappa megnyitása",
|
||||
"open_download_location": "Letöltött fájlok megtekintése",
|
||||
"create_shortcut": "Asztali parancsikon létrehozása",
|
||||
"create_shortcut_simple": "Parancsikon létrehozása",
|
||||
"clear": "Visszavon",
|
||||
"remove_files": "Fájlok eltávolítása",
|
||||
"remove_from_library_title": "Biztos vagy ebben?",
|
||||
"remove_from_library_description": "Ezzel eltávolítod a játékot {{game}} a könyvtáradból",
|
||||
"options": "Beállítások",
|
||||
"properties": "További beállítások",
|
||||
"executable_section_title": "Futtatható fájl",
|
||||
"executable_section_description": "A fájl amely futtatásra fog kerülni amikor a \"Játék\" lenyomásra kerül",
|
||||
"downloads_section_title": "Letöltések",
|
||||
"downloads_section_description": "Csekkold le a játék frissítéseit vagy más verzióit",
|
||||
"danger_zone_section_title": "Veszélyzóna",
|
||||
"danger_zone_section_description": "Itt eltávolítható a játék a könyvtáradból, vagy a fájlok amelyek a Hydra által lettek letöltve",
|
||||
"download_in_progress": "Letöltés folyamatban",
|
||||
"download_paused": "Letöltés szüneteltetve",
|
||||
"last_downloaded_option": "Utoljára letöltött",
|
||||
"create_steam_shortcut": "Steam parancsikon létrehozása",
|
||||
"create_shortcut_success": "A parancsikon létrehozása sikeres",
|
||||
"you_might_need_to_restart_steam": "Lehetséges hogy újrakell indítsd a Steamet hogy lásd a változást.",
|
||||
"create_shortcut_error": "Hiba lépett fel létrehozás közben",
|
||||
"add_to_favorites": "Kedvencekhez adás",
|
||||
"remove_from_favorites": "Eltávolítás a kedvencek közül",
|
||||
"failed_update_favorites": "Kedvencek frissítése sikertelen",
|
||||
"game_removed_from_library": "Játék eltávolítva a könyvtárból",
|
||||
"failed_remove_from_library": "Játék eltávolítása a könyvtárból sikertelen",
|
||||
"files_removed_success": "Fájlok eltávolítása sikeres",
|
||||
"failed_remove_files": "Fájlok eltávolítása sikertelen",
|
||||
"nsfw_content_title": "Ez a játék tartalmaz nem megfelelő tartalmat",
|
||||
"nsfw_content_description": "A(z) {{title}} tartalma lehetséges hogy nem megfelelő minden korosztály számára. Biztosan folytatni szeretnéd?",
|
||||
"allow_nsfw_content": "Folytatás",
|
||||
"refuse_nsfw_content": "Vissza",
|
||||
"stats": "Statisztikák",
|
||||
"download_count": "Letöltések",
|
||||
"player_count": "Aktív játékosok",
|
||||
"rating_count": "Értékelés",
|
||||
"download_error": "Ez a letöltési opció nem elérhető",
|
||||
"download": "Letöltés",
|
||||
"executable_path_in_use": "Ez a futtatható fájl már használatban van a(z) \"{{game}}\" által",
|
||||
"warning": "Figyelmeztetés:",
|
||||
"hydra_needs_to_remain_open": "ehhez a letöltéshez, a Hydrának muszáj nyitva maradnia hogy letöltődjön. Ha a Hydra bezáródik letöltés előtt, a letöltés elveszik.",
|
||||
"achievements": "Achievementek",
|
||||
"achievements_count": "Achievementek {{unlockedCount}}/{{achievementsCount}}",
|
||||
"show_more": "Mutass többet",
|
||||
"show_less": "Mutass kevesebbet",
|
||||
"reviews": "Vélemények",
|
||||
"leave_a_review": "Hagyd itt a véleményed",
|
||||
"write_review_placeholder": "Oszd meg a gondolataid a játékról...",
|
||||
"sort_newest": "Legújabb",
|
||||
"no_reviews_yet": "Még nem lett vélemény megosztva",
|
||||
"be_first_to_review": "Légy az első, aki megossza a véleményét a játékról!",
|
||||
"sort_oldest": "Legrégibb",
|
||||
"sort_highest_score": "Legmagasabb Pontszám",
|
||||
"sort_lowest_score": "Legalacsonyabb Pontszám",
|
||||
"sort_most_voted": "Legszavazottabb",
|
||||
"rating": "Értékelés",
|
||||
"rating_stats": "Értékelés",
|
||||
"rating_very_negative": "Nagyon Negatív",
|
||||
"rating_negative": "Negatív",
|
||||
"rating_neutral": "Átlagos",
|
||||
"rating_positive": "Pozitív",
|
||||
"rating_very_positive": "Nagyon Pozitív",
|
||||
"submit_review": "Küldés",
|
||||
"submitting": "Küldés alatt...",
|
||||
"review_submitted_successfully": "Vélemény beküldve sikeresen!",
|
||||
"review_submission_failed": "Vélemény beküldése sikertelen. Kérlek próbáld újra.",
|
||||
"review_cannot_be_empty": "A vélemény mező nem lehet üres.",
|
||||
"review_deleted_successfully": "Vélemény sikeresen törölve.",
|
||||
"review_deletion_failed": "Vélemény törlése sikertelen. Kérlek próbáld újra.",
|
||||
"loading_reviews": "Vélemények betöltése...",
|
||||
"loading_more_reviews": "Több vélemény betöltése...",
|
||||
"load_more_reviews": "Több vélemény betöltése",
|
||||
"you_seemed_to_enjoy_this_game": "Úgy látszik élvezted ezt a játékot",
|
||||
"would_you_recommend_this_game": "Szeretnél véleményt írni erről a játékról?",
|
||||
"yes": "Igen",
|
||||
"maybe_later": "Talán Később",
|
||||
"cloud_save": "Mentés felhőben",
|
||||
"cloud_save_description": "Mentsd el az előrehaladásod a felhőben, majd folytasd egy másik eszközön",
|
||||
"backups": "Biztonsági másolatok",
|
||||
"install_backup": "Telepít",
|
||||
"delete_backup": "Töröl",
|
||||
"create_backup": "Biztonsági másolat létrehozása",
|
||||
"last_backup_date": "Utolsó biztonsági mentés {{date}}",
|
||||
"no_backup_preview": "Ehhez a címhez nem található mentett játék",
|
||||
"restoring_backup": "Biztonsági mentés helyreállítás: ({{progress}} kész)…",
|
||||
"uploading_backup": "Biztonsági mentés feltöltése…",
|
||||
"no_backups": "Még nem hoztál létre biztonsági másolatot ehhez a játékhoz",
|
||||
"backup_uploaded": "Biztonsági mentés feltöltve",
|
||||
"backup_failed": "Biztonsági mentés sikertelen",
|
||||
"backup_deleted": "Biztonsági mentés törölve",
|
||||
"backup_restored": "Biztonsági mentés helyreállítva",
|
||||
"see_all_achievements": "Achievementlista megtekintése",
|
||||
"sign_in_to_see_achievements": "Jelentkezz be hogy lásd az achievementjeid",
|
||||
"mapping_method_automatic": "Automatikus",
|
||||
"mapping_method_manual": "Kézi",
|
||||
"mapping_method_label": "Térképezési módszer",
|
||||
"files_automatically_mapped": "Fájlok automatikusan térképezve",
|
||||
"no_backups_created": "Ehhez a játékhoz nincs biztonsági másolat létrehozva",
|
||||
"manage_files": "Fájlok kezelése",
|
||||
"loading_save_preview": "Mentett játék keresése…",
|
||||
"wine_prefix": "Wine Prefix",
|
||||
"wine_prefix_description": "A Wine környezet, amiben a játék fut",
|
||||
"launch_options": "Indítási opciók",
|
||||
"launch_options_description": "Indítási opciók testreszabása haladó felhasználóknak (kísérleti funkció)",
|
||||
"launch_options_placeholder": "Nincs paraméter megadva",
|
||||
"no_download_option_info": "Nincs elérhető információ",
|
||||
"backup_deletion_failed": "Biztonsági mentés törlése sikertelen",
|
||||
"max_number_of_artifacts_reached": "A játék biztonsági mentéseinek száma elérte a határt",
|
||||
"achievements_not_sync": "Tekintsd meg hogyan kell szinkronizálni az achievementjeid",
|
||||
"manage_files_description": "Kezeld mely fájlokról készül biztonsági másolat, és melyek állíthatók vissza",
|
||||
"select_folder": "Mappa tallózása",
|
||||
"backup_from": "Biztonsági másolat: {{date}}",
|
||||
"automatic_backup_from": "Automatikus másolat: {{date}}",
|
||||
"enable_automatic_cloud_sync": "Automatikus felhőalapú szinkronizálás engedélyezése",
|
||||
"custom_backup_location_set": "Egyéni biztonsági mentési hely",
|
||||
"no_directory_selected": "Nincs mappa tallózva",
|
||||
"no_write_permission": "Nem lehet a mappába letölteni. Kattints ide további információért.",
|
||||
"reset_achievements": "Achievementek nullázása",
|
||||
"reset_achievements_description": "Ez az összes achievementet nullázza a {{game}} játékhoz",
|
||||
"reset_achievements_title": "Biztos vagy ebben?",
|
||||
"reset_achievements_success": "Achievementek sikeresen nullázva",
|
||||
"reset_achievements_error": "Achievementek nullázása sikertelen",
|
||||
"download_error_gofile_quota_exceeded": "Túllépted a Gofile havi kvótáját. Kérlek, várd meg amíg a kvóta lejár.",
|
||||
"download_error_real_debrid_account_not_authorized": "A Real-Debrid fiókod nem jogosult új letöltésekre. Kérlek, ellenőrízd a fiókbeállításaidat, majd próbáld újra.",
|
||||
"download_error_not_cached_on_real_debrid": "Ez a letöltés nem elérhető a Real-Debriden, és lekérdezni letöltési állapotot még nem lehet.",
|
||||
"update_playtime_title": "Játékidő frissítése",
|
||||
"update_playtime_description": "A(z) {{game}} játékidejének frissítése manuálisan",
|
||||
"update_playtime": "Játékidő frissítése",
|
||||
"update_playtime_success": "Játékidő sikeresen frissítve",
|
||||
"update_playtime_error": "A Játékidőnek nem sikerült frissülnie",
|
||||
"update_game_playtime": "Játékidő frissítése",
|
||||
"manual_playtime_warning": "A Játékidő „Manuálisan frissített”-ként lesz megjelölve, és ez nem visszavonható.",
|
||||
"manual_playtime_tooltip": "Ez a Játékidő manuálisan lett frissítve",
|
||||
"download_error_not_cached_on_torbox": "Ez a letöltés nem elérhető a TorBoxon, és lekérdezni letöltési állapotot még nem lehet.",
|
||||
"download_error_not_cached_on_hydra": "Ez a letöltés nem elérhető a Nimbuson.",
|
||||
"game_removed_from_favorites": "Játék eltávolítva a kedvencek közül",
|
||||
"game_added_to_favorites": "Játék hozzáadva a kedvencekhez",
|
||||
"game_removed_from_pinned": "Játék eltávolítva a kitűzöttek közül",
|
||||
"game_added_to_pinned": "Játék sikeresen kitűzve",
|
||||
"automatically_extract_downloaded_files": "Automatikus kibontása a letöltött fájloknak",
|
||||
"create_start_menu_shortcut": "Start menü parancsikon létrehozása",
|
||||
"invalid_wine_prefix_path": "Érvénytelen Wine prefix elérési útvonal",
|
||||
"invalid_wine_prefix_path_description": "Az út a Wine prefixhez érvénytelen. Ellenőrízd az elérési utat, majd próbáld újra.",
|
||||
"missing_wine_prefix": "Wine prefix szükséges a biztonsági másolat létrehozásához Linux rendszeren.",
|
||||
"artifact_renamed": "Biztonsági mentés sikeresen átnevezve",
|
||||
"rename_artifact": "Biztonsági mentés átnevezése",
|
||||
"rename_artifact_description": "Nevezd át a biztonsági másolatot egy leíróbb névre",
|
||||
"artifact_name_label": "Biztonsági másolat neve",
|
||||
"artifact_name_placeholder": "Adj egy nevet a biztonsági mentésnek",
|
||||
"save_changes": "Változtatások mentése",
|
||||
"required_field": "Ez a mező kötelező",
|
||||
"max_length_field": "Ez a mező kevesebb karakter kell legyen mint {{length}}",
|
||||
"freeze_backup": "Rögzítsd, hogy az automatikus biztonsági mentések ne írják felül",
|
||||
"unfreeze_backup": "Leválaszt",
|
||||
"backup_frozen": "Biztonsági mentés rögzítve",
|
||||
"backup_unfrozen": "Biztonsági mentés leválasztva",
|
||||
"backup_freeze_failed": "Biztonsági mentés rögzítése sikertelen",
|
||||
"backup_freeze_failed_description": "Legalább egy szabad helyet kell hagyni az automatikus biztonsági mentéseknek",
|
||||
"edit_game_modal_button": "Játékadatok testreszabása",
|
||||
"game_details": "Játék leírása",
|
||||
"currency_symbol": "Ft",
|
||||
"currency_country": "hu",
|
||||
"prices": "Árak",
|
||||
"no_prices_found": "Nincsenek található árak",
|
||||
"view_all_prices": "Összes ár megtekintése",
|
||||
"retail_price": "Bolti ár",
|
||||
"keyshop_price": "Nem hivatalos ár",
|
||||
"historical_retail": "Korábbi bolti ár",
|
||||
"historical_keyshop": "Korábbi nem hivatalos ár",
|
||||
"language": "Nyelv",
|
||||
"caption": "Felirat",
|
||||
"audio": "Hang",
|
||||
"filter_by_source": "Szűrés forrás szerint",
|
||||
"no_repacks_found": "Nem található forrás ehhez a játékhoz",
|
||||
"delete_review": "Vélemény törlése",
|
||||
"remove_review": "Vélemény eltávolítása",
|
||||
"delete_review_modal_title": "Biztos vagy abban hogy törölni szeretnéd a véleményed?",
|
||||
"delete_review_modal_description": "Ez a lépés nem vonható vissza.",
|
||||
"delete_review_modal_delete_button": "Törlés",
|
||||
"delete_review_modal_cancel_button": "Mégse"
|
||||
"repacks_modal_description": "Choose the repack you want to download",
|
||||
"select_folder_hint": "Ahhoz, hogy megváltoztasd a helyet, hozzákell férned a",
|
||||
"download_now": "Töltsd le most"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Hydra Aktiválása",
|
||||
"installation_id": "Telepítési Azonosító:",
|
||||
"enter_activation_code": "Írd be az aktiválási kódod",
|
||||
"message": "Ha nem tudod hol kérdezz efelől, akkor nem kéne ilyened legyen.",
|
||||
"installation_id": "Telepítési ID:",
|
||||
"enter_activation_code": "Add meg az aktiválási kódodat",
|
||||
"message": "Ha nem tudod, hol kérdezd meg ezt, akkor nem is kellene, hogy legyen ilyened.",
|
||||
"activate": "Aktiválás",
|
||||
"loading": "Töltés…"
|
||||
"loading": "Betöltés…"
|
||||
},
|
||||
"downloads": {
|
||||
"resume": "Folytatás",
|
||||
@@ -373,331 +91,46 @@
|
||||
"paused": "Szüneteltetve",
|
||||
"verifying": "Ellenőrzés…",
|
||||
"completed": "Befejezve",
|
||||
"removed": "Nincs letöltve",
|
||||
"cancel": "Mégse",
|
||||
"filter": "Letöltött játékok szűrése",
|
||||
"remove": "Eltávolít",
|
||||
"remove": "Eltávolítás",
|
||||
"downloading_metadata": "Metaadatok letöltése…",
|
||||
"deleting": "Telepítő törlése…",
|
||||
"delete": "Telepítő eltávolítása",
|
||||
"delete_modal_title": "Biztos vagy ebben?",
|
||||
"delete_modal_description": "Ez eltávolítja a telepítési fájlokat a számítógépedről",
|
||||
"install": "Telepít",
|
||||
"download_in_progress": "Folyamatban lévő",
|
||||
"queued_downloads": "Várakozósoron lévő letöltések",
|
||||
"downloads_completed": "Befejezett",
|
||||
"queued": "Várakozási sorban",
|
||||
"no_downloads_title": "Oly üres..",
|
||||
"no_downloads_description": "Még nem töltöttél le semmit a Hydra segítségével, de soha nem késő elkezdeni.",
|
||||
"checking_files": "Fájlok ellenőrzése…",
|
||||
"seeding": "Seedelés",
|
||||
"stop_seeding": "Seedelés leállítása",
|
||||
"resume_seeding": "Seedelés folytatása",
|
||||
"options": "Kezelés",
|
||||
"extract": "Fájlok kibontása",
|
||||
"extracting": "Fájlok kibontása…"
|
||||
"delete_modal_title": "Biztos vagy benne?",
|
||||
"delete_modal_description": "Ez eltávolít minden telepítési fájlt a számítógépedről",
|
||||
"install": "Telepítés"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "Letöltési útvonalak",
|
||||
"downloads_path": "Letöltések helye",
|
||||
"change": "Frissítés",
|
||||
"notifications": "Értesítések",
|
||||
"enable_download_notifications": "Amikor egy letöltés befejeződik",
|
||||
"enable_repack_list_notifications": "Amikor új repack kerül feltöltésre",
|
||||
"real_debrid_api_token_label": "Real-Debrid API token",
|
||||
"quit_app_instead_hiding": "Hydra elrejtésének tiltása bezáráskor",
|
||||
"launch_with_system": "Hydra automatikus indítása rendszer indításakor",
|
||||
"general": "Általános",
|
||||
"behavior": "Működés",
|
||||
"download_sources": "Letöltési források",
|
||||
"language": "Nyelv",
|
||||
"api_token": "API Token",
|
||||
"enable_real_debrid": "Real-Debrid Bekapcsolása",
|
||||
"real_debrid_description": "A Real-Debrid egy korlátozásmentes letöltőprogram, ami lehetővé teszi a fájlok gyors letöltését, és csak az internetkapcsolat sebessége szab határt.",
|
||||
"debrid_invalid_token": "Érvénytelen API token",
|
||||
"debrid_api_token_hint": "Az API tokened <0>itt</0> található",
|
||||
"real_debrid_free_account_error": "Ez a fiók: \"{{username}}\" egy ingyenes fiók. Kérlek iratkozz fel a Real-Debridre",
|
||||
"debrid_linked_message": "Fiók összekapcsolva: \"{{username}}\" ",
|
||||
"save_changes": "Változtatások mentése",
|
||||
"changes_saved": "Változtatások sikeresen mentve",
|
||||
"download_sources_description": "A Hydra lefogja tölteni a letöltési linkeket a forrásokból. Az URL forrásnak közvetlen linknek kell lennie egy .json fájlhoz, ami tartalmazza a linkeket.",
|
||||
"validate_download_source": "Érvényesítés",
|
||||
"remove_download_source": "Eltávolítás",
|
||||
"add_download_source": "Forrás hozáadása",
|
||||
"download_count_zero": "Nincs letöltési opció",
|
||||
"download_count_one": "{{countFormatted}} letöltési opció",
|
||||
"download_count_other": "{{countFormatted}} letöltési opció",
|
||||
"download_source_url": "URL forrás:",
|
||||
"add_download_source_description": "Helyezd be a .json fájl URL-jét",
|
||||
"download_source_up_to_date": "Naprakész",
|
||||
"download_source_errored": "Hiba történt",
|
||||
"sync_download_sources": "Források szinkronizálása",
|
||||
"removed_download_source": "Letöltési forrás eltávolítva",
|
||||
"removed_download_sources": "Letöltési források eltávolítva",
|
||||
"cancel_button_confirmation_delete_all_sources": "Nem",
|
||||
"confirm_button_confirmation_delete_all_sources": "Igen, törölj mindent",
|
||||
"title_confirmation_delete_all_sources": "Az összes letöltési forrás törlése",
|
||||
"description_confirmation_delete_all_sources": "Az összes letöltési forrást törölni fogod ezáltal",
|
||||
"button_delete_all_sources": "Összes eltávolítása",
|
||||
"added_download_source": "Letöltési forrás hozzáadva",
|
||||
"download_sources_synced": "Az összes letöltési forrás szinkronizálva",
|
||||
"insert_valid_json_url": "Adj meg egy érvényes JSON url-t",
|
||||
"found_download_option_zero": "Nincs letöltési opció",
|
||||
"found_download_option_one": "{{countFormatted}} Letöltési opció találva",
|
||||
"found_download_option_other": "{{countFormatted}} Letöltési opciók találva",
|
||||
"import": "Importálás",
|
||||
"public": "Publikus",
|
||||
"private": "Privát",
|
||||
"friends_only": "Csak barátok",
|
||||
"privacy": "Adatvédelem",
|
||||
"profile_visibility": "Profil láthatósága",
|
||||
"profile_visibility_description": "Válaszd ki, ki láthatja a profilod és könyvtárad",
|
||||
"required_field": "Ez a mező kötelező",
|
||||
"source_already_exists": "Ez a forrás már használatban",
|
||||
"must_be_valid_url": "A forrás egy érvényes URL kell legyen",
|
||||
"blocked_users": "Letiltott felhasználók",
|
||||
"user_unblocked": "Felhasználó letiltva",
|
||||
"enable_achievement_notifications": "Amikor egy achievement feloldódik",
|
||||
"launch_minimized": "Hydra indítása minimalizálva",
|
||||
"disable_nsfw_alert": "NSFW figyelmeztetés kikapcsolása",
|
||||
"seed_after_download_complete": "Letöltés utáni seedelés",
|
||||
"show_hidden_achievement_description": "Rejtett achievementek leírásának megjelenítése feloldás előtt",
|
||||
"account": "Fiók",
|
||||
"no_users_blocked": "Nincsenek letiltott felhasználóid",
|
||||
"subscription_active_until": "Hydra Cloud előfizetésed aktív, eddig: {{date}}",
|
||||
"manage_subscription": "Előfizetés kezelése",
|
||||
"update_email": "Email változtatása",
|
||||
"update_password": "Jelszó változtatása",
|
||||
"current_email": "Jelenlegi email:",
|
||||
"no_email_account": "Még nincs beállított emailed",
|
||||
"account_data_updated_successfully": "Fiókadatok változtatása sikeres",
|
||||
"renew_subscription": "Hydra Cloud Megújítása",
|
||||
"subscription_expired_at": "Az előfizetésed lejárt, ekkor: {{date}}",
|
||||
"no_subscription": "Élvezd a Hydrát a lehető legjobb módon",
|
||||
"become_subscriber": "Légy Hydra Cloud tag",
|
||||
"subscription_renew_cancelled": "Automatikus megújítás kikapcsolva",
|
||||
"subscription_renews_on": "Az előfizetésed megújul, ekkor: {{date}}",
|
||||
"bill_sent_until": "A következő számlát ezen napon küldjük",
|
||||
"no_themes": "Úgy látszik nincs egyetlen témád sem még, de ne aggódj, kattints ide hogy elkészítsd a remekművedet.",
|
||||
"editor_tab_code": "Code",
|
||||
"editor_tab_info": "Info",
|
||||
"editor_tab_save": "Mentés",
|
||||
"web_store": "Webáruház",
|
||||
"clear_themes": "Törlés",
|
||||
"create_theme": "Létrehozás",
|
||||
"create_theme_modal_title": "Egyéni téma létrehozása",
|
||||
"create_theme_modal_description": "Hozz létre egy új témát, hogy testreszabhasd a Hydrát ahogy szeretnéd",
|
||||
"theme_name": "Téma neve",
|
||||
"insert_theme_name": "Adj a témádnak nevet",
|
||||
"set_theme": "Téma beállítása",
|
||||
"unset_theme": "Téma visszavonása",
|
||||
"delete_theme": "Téma törlése",
|
||||
"edit_theme": "Téma szerkesztése",
|
||||
"delete_all_themes": "Összes téma törlése",
|
||||
"delete_all_themes_description": "Ez törölni fogja az összes témádat",
|
||||
"delete_theme_description": "Ez törölni fogja a(z) {{theme}} témát",
|
||||
"cancel": "Mégsem",
|
||||
"appearance": "Megjelenés",
|
||||
"debrid": "Debrid",
|
||||
"debrid_description": "A Debrid szolgáltatások prémium szolgáltatások amelyek lehetővé teszik, hogy gyorsan letölts különböző fájltároló szolgáltatásokon tárolt fájlokat, csak az internet sebességed szab határt.",
|
||||
"enable_torbox": "TorBox bekapcsolása",
|
||||
"torbox_description": "A TorBox egy olyan premium seedbox szolgáltatás, amely még a piacon elérhető legjobb szerverekkel is felveszi a versenyt.",
|
||||
"torbox_account_linked": "TorBox fiók összekapcsolva",
|
||||
"create_real_debrid_account": "Kattints ide ha még nincs Real-Debrid fiókod",
|
||||
"create_torbox_account": "Kattints ide ha még nincs TorBox fiókod",
|
||||
"real_debrid_account_linked": "Real-Debrid fiók összekapcsolva",
|
||||
"name_min_length": "A téma neve legalább 3 karakter hosszú legyen",
|
||||
"import_theme": "Téma importálása",
|
||||
"import_theme_description": "Ezt a témát fogod importálni a Témaáruház-ból: {{theme}}",
|
||||
"error_importing_theme": "Hiba lépett fel a téma importálása közben",
|
||||
"theme_imported": "Téma sikeresen importálva",
|
||||
"enable_friend_request_notifications": "Amikor ismerősnek jelölnek",
|
||||
"enable_auto_install": "Frissítések letöltése automatikusan",
|
||||
"common_redist": "Alapvető Segédprogramok",
|
||||
"common_redist_description": "Egyes játékok futtatásához alapvető segédprogram fájlok szükségesek. A problémák elkerülése képpen ajánlott telepíteni őket.",
|
||||
"install_common_redist": "Telepítés",
|
||||
"installing_common_redist": "Telepítés alatt…",
|
||||
"show_download_speed_in_megabytes": "Letöltési sebesség megabájt/másodpercben lévő megjelenítése",
|
||||
"extract_files_by_default": "Fájlok kicsomagolása letöltés után",
|
||||
"enable_steam_achievements": "Steam-achievementek utáni keresés engedélyezése",
|
||||
"achievement_custom_notification_position": "Achievement-értesítések egyéni elhelyezése",
|
||||
"top-left": "Bal felső sarok",
|
||||
"top-center": "Felső közép",
|
||||
"top-right": "Jobb felső sarok",
|
||||
"bottom-left": "Bal alsó sarok",
|
||||
"bottom-center": "Alsó közép",
|
||||
"bottom-right": "Jobb alsó sarok",
|
||||
"enable_achievement_custom_notifications": "Egyéni achievement-értesítések bekapcsolása",
|
||||
"alignment": "Igazítás",
|
||||
"variation": "Variáció",
|
||||
"default": "Alapértelmezett",
|
||||
"rare": "Ritka",
|
||||
"platinum": "Platina",
|
||||
"hidden": "Rejtett",
|
||||
"test_notification": "Értesítés tesztelése",
|
||||
"notification_preview": "Achievement Értesítés Előnézete",
|
||||
"enable_friend_start_game_notifications": "Amikor egy barátod elkezd játszani egy játékot"
|
||||
"description_confirmation_delete_all_sources": "Törölni fog minden letöltési forrást",
|
||||
"title_confirmation_delete_all_sources": "Törölje az összes letöltési forrást",
|
||||
"removed_download_sources": "Betűtípusok eltávolítva",
|
||||
"button_delete_all_sources": "Távolítsa el az összes letöltési forrást",
|
||||
"enable_repack_list_notifications": "Amikor egy új repack hozzáadásra kerül"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Letöltés befejezve",
|
||||
"game_ready_to_install": "A(z) {{title}} telepítésre kész",
|
||||
"download_complete": "Letöltés befejeződött",
|
||||
"game_ready_to_install": "{{title}} telepítésre kész",
|
||||
"repack_list_updated": "Repack lista frissítve",
|
||||
"repack_count_one": "{{count}} repack hozzáadva",
|
||||
"repack_count_other": "{{count}} repack hozzáadva",
|
||||
"new_update_available": "A(z) {{version}} verzió elérhető",
|
||||
"restart_to_install_update": "Indítsd újra a Hydrát a frissítés telepítéséhez",
|
||||
"notification_achievement_unlocked_title": "Achievement feloldva: {{game}}",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} és további {{count}} feloldva",
|
||||
"new_friend_request_description": "{{displayName}} küldött neked egy barátfelkérést",
|
||||
"new_friend_request_title": "Új barátfelkérés",
|
||||
"extraction_complete": "Kicsomagolás befejezve",
|
||||
"game_extracted": "{{title}} sikeresen kicsomagolva",
|
||||
"friend_started_playing_game": "{{displayName}} játszani kezdett",
|
||||
"test_achievement_notification_title": "Ez egy teszt értesítés",
|
||||
"test_achievement_notification_description": "Elég menő, mi?"
|
||||
"repack_count_other": "{{count}} repack hozzáadva"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Hydra megnyitása",
|
||||
"quit": "Kilépés"
|
||||
},
|
||||
"game_card": {
|
||||
"available_one": "Elérhető",
|
||||
"available_other": "Elérhető",
|
||||
"no_downloads": "Nincs elérhető letöltés",
|
||||
"calculating": "Feldolgozás"
|
||||
"no_downloads": "Nincs elérhető letöltés"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "A programok nincsenek telepítve",
|
||||
"description": "Wine vagy Lutris futtatható fájlok nem találhatók a rendszereden",
|
||||
"instructions": "Ellenőrízd hogy melyiket kell helyesen telepíteni a Linux disztribúciódra, hogy a játék megfelelően fusson"
|
||||
},
|
||||
"modal": {
|
||||
"close": "Bezárás gomb"
|
||||
},
|
||||
"forms": {
|
||||
"toggle_password_visibility": "Jelszó láthatóságának állítása"
|
||||
},
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} óra",
|
||||
"amount_minutes": "{{amount}} perc",
|
||||
"amount_hours_short": "{{amount}}ó",
|
||||
"amount_minutes_short": "{{amount}}p",
|
||||
"last_time_played": "Utoljára játszva {{period}}",
|
||||
"activity": "Legutóbbi tevékenység",
|
||||
"library": "Könyvtár",
|
||||
"pinned": "Kitűzve",
|
||||
"achievements_earned": "Elért achievementek",
|
||||
"played_recently": "Nemrég játszva",
|
||||
"playtime": "Játszottidő",
|
||||
"total_play_time": "Teljes játszottidő",
|
||||
"manual_playtime_tooltip": "Ez a játszottidő manuálisan lett frissítve",
|
||||
"no_recent_activity_title": "Hmmm… itt semmi sincs",
|
||||
"no_recent_activity_description": "Mostanában nem játszottál semmivel. Hát ideje ezt megváltoztatni!",
|
||||
"display_name": "Profilnév",
|
||||
"saving": "Mentésben",
|
||||
"save": "Mentés",
|
||||
"edit_profile": "Profil Szerkesztése",
|
||||
"saved_successfully": "Sikeresen elmentve",
|
||||
"try_again": "Kérlek, próbálkozz újra",
|
||||
"sign_out_modal_title": "Biztos vagy ebben?",
|
||||
"cancel": "Mégsem",
|
||||
"successfully_signed_out": "Sikeresen kijelentkezve",
|
||||
"sign_out": "Kijelentkezés",
|
||||
"playing_for": "Játékban: {{amount}}",
|
||||
"sign_out_modal_text": "A könyvtár a jelenlegi fiókodhoz van csatolva. Kijelentkezéskor a könyvtár többé nem lesz látható, és az eddigi előrehaladás nem lesz mentve. Folytatod a kijelentkezést?",
|
||||
"add_friends": "Barát bejelölés",
|
||||
"add": "Elküld",
|
||||
"friend_code": "Barát kód",
|
||||
"see_profile": "Profil megtekintése",
|
||||
"sending": "Küldés..",
|
||||
"friend_request_sent": "Barátfelkérés elküldve",
|
||||
"friends": "Barátok",
|
||||
"friends_list": "Barát lista",
|
||||
"user_not_found": "Felhasználó nem találva",
|
||||
"block_user": "Felhasználó letiltása",
|
||||
"add_friend": "Barát bejelölése",
|
||||
"request_sent": "Kérés elküldve",
|
||||
"request_received": "Barátfelkérést kaptál",
|
||||
"accept_request": "Kérés elfogadása",
|
||||
"ignore_request": "Kérés ignorálása",
|
||||
"cancel_request": "Kérés visszavonása",
|
||||
"undo_friendship": "Barát eltávolítása",
|
||||
"request_accepted": "Barátfelkérés elfogadva",
|
||||
"user_blocked_successfully": "Felhasználó sikeresen letiltva",
|
||||
"user_block_modal_text": "Ez által letiltod őt: {{displayName}}",
|
||||
"blocked_users": "Letiltott felhasználók",
|
||||
"unblock": "Tiltás feloldása",
|
||||
"no_friends_added": "Nincs bejelölt barátod",
|
||||
"pending": "Függőben",
|
||||
"no_pending_invites": "Nincs függőben lévő barátfelkérésed",
|
||||
"no_blocked_users": "Nincs letiltott felhasználó",
|
||||
"friend_code_copied": "Barát kód kimásolva",
|
||||
"undo_friendship_modal_text": "Ezáltal megszünteted a barátságod vele: {{displayName}}",
|
||||
"privacy_hint": "Hogy beállítsd ki láthassa ezt, menj a <0>Beállítások</0> menüjébe",
|
||||
"locked_profile": "Ez a profil privát",
|
||||
"image_process_failure": "Hiba a kép feldolgozása közben",
|
||||
"required_field": "Ez a mező kötelező",
|
||||
"displayname_min_length": "A megjelenített névnek legalább 3 karakter hosszúnak kell lennie",
|
||||
"displayname_max_length": "A megjelenített név hossza legfeljebb 50 karakter lehet",
|
||||
"report_profile": "Profil bejelentése",
|
||||
"report_reason": "Miért jelented ezt a profilt?",
|
||||
"report_description": "További információ",
|
||||
"report_description_placeholder": "További információ",
|
||||
"report": "Bejelentés",
|
||||
"report_reason_hate": "Gyűlöletbeszéd",
|
||||
"report_reason_sexual_content": "Szexuális tartalom",
|
||||
"report_reason_violence": "Fenyegető",
|
||||
"report_reason_spam": "Spam",
|
||||
"report_reason_other": "Egyéb",
|
||||
"profile_reported": "Profil bejelentve",
|
||||
"your_friend_code": "A barát kódod:",
|
||||
"upload_banner": "Borítókép feltöltés",
|
||||
"uploading_banner": "Borítókép feltöltése…",
|
||||
"background_image_updated": "Borítókép frissítve",
|
||||
"stats": "Statisztikák",
|
||||
"achievements": "achievementek",
|
||||
"games": "Játékok",
|
||||
"top_percentile": "Top {{percentile}}%",
|
||||
"ranking_updated_weekly": "A rangsor hetente frissül.",
|
||||
"playing": "Játékban: {{game}}",
|
||||
"achievements_unlocked": "Achievementek feloldva",
|
||||
"earned_points": "Megszerzett pontok",
|
||||
"show_achievements_on_profile": "Mutasd az achievementjeid a profilodon",
|
||||
"show_points_on_profile": "Mutasd a megszerzett pontjaid a profilodon",
|
||||
"error_adding_friend": "Hiba, barátfelkérés sikertelen. Kérlek ellenőrízd a barát kódot",
|
||||
"friend_code_length_error": "A barát kódnak 8 karakterből kell állnia",
|
||||
"game_removed_from_pinned": "Játék eltávolítva a kitűzöttek közül",
|
||||
"game_added_to_pinned": "Játék hozzáadva a kitűzöttekhez",
|
||||
"karma": "Karma",
|
||||
"karma_count": "karma",
|
||||
"karma_description": "Pozitív értékelésekre kapott pontok alapján"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Achievement feloldva",
|
||||
"user_achievements": "{{displayName}} achievementjei",
|
||||
"your_achievements": "A te achievementjeid",
|
||||
"unlocked_at": "Feloldva: {{date}}",
|
||||
"subscription_needed": "A tartalom megtekintéséhez Hydra Cloud előfizetés szükséges",
|
||||
"new_achievements_unlocked": "{{achievementCount}} új achievement feloldva {{gameCount}} játékban",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} achievementek",
|
||||
"achievements_unlocked_for_game": "{{achievementCount}} új achievement feloldva itt: {{gameTitle}}",
|
||||
"hidden_achievement_tooltip": "Ez egy rejtett achievement",
|
||||
"achievement_earn_points": "Szerezz be {{points}} pontot ezzel az achievement-el",
|
||||
"earned_points": "Megszerzett pontok:",
|
||||
"available_points": "Elérhető pontok:",
|
||||
"how_to_earn_achievements_points": "Hogy lehet elérni achievement pontokat?"
|
||||
},
|
||||
"hydra_cloud": {
|
||||
"subscription_tour_title": "Hydra Cloud Előfizetés",
|
||||
"subscribe_now": "Előfizetés",
|
||||
"cloud_saving": "Felhőmentés",
|
||||
"cloud_achievements": "Mentsd az achievementjeid el a felhőben",
|
||||
"animated_profile_picture": "Animált profilkép",
|
||||
"premium_support": "Premium Támogatás",
|
||||
"show_and_compare_achievements": "Jelenítsd és hasonlítsd az elért achievementjeid másokéhoz",
|
||||
"animated_profile_banner": "Animált profil borítókép",
|
||||
"hydra_cloud": "Hydra Cloud",
|
||||
"hydra_cloud_feature_found": "Épp felfedeztél egy Hydra Cloud funkciót!",
|
||||
"learn_more": "Tudj meg többet",
|
||||
"debrid_description": "Akár 4x gyorsabb letöltés a Nimbusszal"
|
||||
"description": "A Wine vagy a Lutris végrehajtható fájljai nem találhatók a rendszereden",
|
||||
"instructions": "Ellenőrizd a megfelelő telepítési módot bármelyiküknek a Linux disztribúciódon, hogy a játék normálisan fusson"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Berhasil masuk"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Unggulan",
|
||||
"surprise_me": "Kejutkan saya",
|
||||
"no_results": "Tidak ada hasil ditemukan"
|
||||
},
|
||||
@@ -24,6 +25,7 @@
|
||||
},
|
||||
"header": {
|
||||
"search": "Cari game",
|
||||
|
||||
"home": "Beranda",
|
||||
"catalogue": "Katalog",
|
||||
"downloads": "Unduhan",
|
||||
@@ -39,7 +41,10 @@
|
||||
"calculating_eta": "Mengunduh {{title}}… ({{percentage}} selesai) - Menghitung waktu yang tersisa…",
|
||||
"checking_files": "Memeriksa file {{title}}… ({{percentage}} selesai)"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Halaman Berikutnya",
|
||||
"previous_page": "Halaman Sebelumnya"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Buka opsi unduhan",
|
||||
"download_options_zero": "Tidak ada opsi unduhan",
|
||||
@@ -97,6 +102,7 @@
|
||||
"open_download_location": "Lihat file yang diunduh",
|
||||
"create_shortcut": "Buat pintasan desktop",
|
||||
"remove_files": "Hapus file",
|
||||
"remove_from_library_title": "Apa kamu yakin?",
|
||||
"remove_from_library_description": "Ini akan menghapus {{game}} dari perpustakaan kamu",
|
||||
"options": "Opsi",
|
||||
"executable_section_title": "Eksekusi",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"language_name": "Italiano",
|
||||
"home": {
|
||||
"featured": "In primo piano",
|
||||
"surprise_me": "Sorprendimi",
|
||||
"no_results": "Nessun risultato trovato"
|
||||
},
|
||||
@@ -19,6 +20,7 @@
|
||||
},
|
||||
"header": {
|
||||
"search": "Cerca",
|
||||
|
||||
"home": "Home",
|
||||
"catalogue": "Catalogo",
|
||||
"downloads": "Download",
|
||||
@@ -30,7 +32,10 @@
|
||||
"downloading_metadata": "Scaricamento metadati di {{title}}…",
|
||||
"downloading": "Download di {{title}}… ({{percentage}} completato) - Conclusione {{eta}} - {{speed}}"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Pagina successiva",
|
||||
"previous_page": "Pagina precedente"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Apri opzioni di download",
|
||||
"download_options_zero": "Nessuna opzione di download",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Сәтті кіру"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Ұсынылған",
|
||||
"surprise_me": "Таңқалдыр",
|
||||
"no_results": "Ештеңе табылмады"
|
||||
},
|
||||
@@ -22,6 +23,7 @@
|
||||
"sign_in": "Кіру",
|
||||
"favorites": "Таңдаулылар"
|
||||
},
|
||||
|
||||
"header": {
|
||||
"search": "Іздеу",
|
||||
"home": "Басты бет",
|
||||
@@ -38,7 +40,10 @@
|
||||
"downloading": "Жүктеу {{title}}… ({{percentage}} аяқталды) - Аяқтау {{eta}} - {{speed}}",
|
||||
"calculating_eta": "Жүктеу {{title}}… ({{percentage}} аяқталды) - Қалған уақытты есептеу…"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Келесі бет",
|
||||
"previous_page": "Алдыңғы бет"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Жүктеу нұсқаларын ашу",
|
||||
"download_options_zero": "Жүктеу нұсқалары жоқ",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"language_name": "한국어",
|
||||
"home": {
|
||||
"featured": "추천",
|
||||
"surprise_me": "무작위 추천",
|
||||
"no_results": "결과 없음"
|
||||
},
|
||||
@@ -16,6 +17,7 @@
|
||||
"home": "홈",
|
||||
"favorites": "즐겨찾기"
|
||||
},
|
||||
|
||||
"header": {
|
||||
"search": "게임 검색하기",
|
||||
"home": "홈",
|
||||
@@ -29,7 +31,10 @@
|
||||
"downloading_metadata": "{{title}}의 메타데이터를 다운로드 중…",
|
||||
"downloading": "{{title}}의 파일들을 다운로드 중… ({{percentage}} 완료) - 완료까지 {{eta}} - {{speed}}"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "다음 페이지",
|
||||
"previous_page": "이전 페이지"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "다운로드 선택지 열기",
|
||||
"download_options_zero": "다운로드 선택지 없음",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Logget inn vellykket"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Anbefalinger",
|
||||
"surprise_me": "Overrask meg",
|
||||
"no_results": "Ingen resultater fundet",
|
||||
"start_typing": "Begynn å skrive for å søke...",
|
||||
@@ -28,6 +29,7 @@
|
||||
},
|
||||
"header": {
|
||||
"search": "Søk efter spill",
|
||||
|
||||
"home": "Hjem",
|
||||
"catalogue": "Katalog",
|
||||
"downloads": "Nedlastinger",
|
||||
@@ -43,7 +45,10 @@
|
||||
"calculating_eta": "Laster ned {{title}}… ({{percentage}} ferdig) - Regner ut resterende tid…",
|
||||
"checking_files": "Sjekker {{title}} filer… ({{percentage}} ferdig)"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Neste side",
|
||||
"previous_page": "Forrige side"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Åpne nedlastingsmuligheter",
|
||||
"download_options_zero": "Ingen nedlastingsmulighet",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"language_name": "Nederlands",
|
||||
"home": {
|
||||
"featured": "Uitgelicht",
|
||||
"surprise_me": "Verrasing",
|
||||
"no_results": "Geen resultaten gevonden"
|
||||
},
|
||||
@@ -18,6 +19,7 @@
|
||||
},
|
||||
"header": {
|
||||
"search": "Zoek spellen",
|
||||
|
||||
"home": "Home",
|
||||
"catalogue": "Bibliotheek",
|
||||
"downloads": "Downloads",
|
||||
@@ -29,7 +31,10 @@
|
||||
"downloading_metadata": "Downloading {{title}} metadata…",
|
||||
"downloading": "Downloading {{title}}… ({{percentage}} complete) - Conclusion {{eta}} - {{speed}}"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Volgende Pagina",
|
||||
"previous_page": "Vorige Pagina"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Open download Instellingen",
|
||||
"download_options_zero": "Geen download Instellingen",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"language_name": "Polski",
|
||||
"home": {
|
||||
"featured": "Wyróżnione",
|
||||
"surprise_me": "Zaskocz mnie",
|
||||
"no_results": "Nie znaleziono wyników"
|
||||
},
|
||||
@@ -19,6 +20,7 @@
|
||||
},
|
||||
"header": {
|
||||
"search": "Szukaj",
|
||||
|
||||
"home": "Główna",
|
||||
"catalogue": "Katalog",
|
||||
"downloads": "Pobrane",
|
||||
@@ -30,7 +32,10 @@
|
||||
"downloading_metadata": "Pobieranie {{title}} metadata…",
|
||||
"downloading": "Pobieranie {{title}}… (ukończone w {{percentage}}) - Podsumowanie {{eta}} - {{speed}}"
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Następna strona",
|
||||
"previous_page": "Poprzednia strona"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Otwórz opcje pobierania",
|
||||
"download_options_zero": "Brak opcji pobierania",
|
||||
|
||||
182
src/locales/pt-BR/translation.json
Executable file → Normal file
182
src/locales/pt-BR/translation.json
Executable file → Normal file
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Autenticado com sucesso"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Destaques",
|
||||
"hot": "Populares",
|
||||
"weekly": "📅 Mais baixados da semana",
|
||||
"achievements": "🏆 Pra platinar",
|
||||
@@ -26,69 +27,7 @@
|
||||
"sign_in": "Login",
|
||||
"friends": "Amigos",
|
||||
"need_help": "Precisa de ajuda?",
|
||||
"favorites": "Favoritos",
|
||||
"playable_button_title": "Mostrar apenas jogos que você pode jogar agora",
|
||||
"add_custom_game_tooltip": "Adicionar jogo personalizado",
|
||||
"show_playable_only_tooltip": "Mostrar Apenas Jogáveis",
|
||||
"custom_game_modal": "Adicionar jogo personalizado",
|
||||
"custom_game_modal_description": "Adicione um jogo personalizado à sua biblioteca selecionando um arquivo executável",
|
||||
"custom_game_modal_executable_path": "Caminho do Executável",
|
||||
"custom_game_modal_select_executable": "Selecionar arquivo executável",
|
||||
"custom_game_modal_title": "Título",
|
||||
"custom_game_modal_enter_title": "Insira o título",
|
||||
"edit_game_modal_title": "Título",
|
||||
"custom_game_modal_browse": "Buscar",
|
||||
"custom_game_modal_cancel": "Cancelar",
|
||||
"custom_game_modal_add": "Adicionar Jogo",
|
||||
"custom_game_modal_adding": "Adicionando...",
|
||||
"custom_game_modal_success": "Jogo personalizado adicionado com sucesso",
|
||||
"custom_game_modal_failed": "Falha ao adicionar jogo personalizado",
|
||||
"custom_game_modal_executable": "Executável",
|
||||
"edit_game_modal": "Personalizar detalhes",
|
||||
"edit_game_modal_description": "Personalize os recursos e detalhes do jogo",
|
||||
"edit_game_modal_enter_title": "Insira o título",
|
||||
"edit_game_modal_image": "Imagem",
|
||||
"edit_game_modal_select_image": "Selecionar imagem",
|
||||
"edit_game_modal_browse": "Buscar",
|
||||
"edit_game_modal_image_preview": "Visualização da imagem",
|
||||
"edit_game_modal_icon": "Ícone",
|
||||
"edit_game_modal_select_icon": "Selecionar ícone",
|
||||
"edit_game_modal_icon_preview": "Visualização do ícone",
|
||||
"edit_game_modal_logo": "Logo",
|
||||
"edit_game_modal_select_logo": "Selecionar logo",
|
||||
"edit_game_modal_logo_preview": "Visualização do logo",
|
||||
"edit_game_modal_hero": "Hero da Biblioteca",
|
||||
"edit_game_modal_select_hero": "Selecionar imagem hero da biblioteca",
|
||||
"edit_game_modal_hero_preview": "Visualização da imagem hero da biblioteca",
|
||||
"edit_game_modal_cancel": "Cancelar",
|
||||
"edit_game_modal_update": "Atualizar",
|
||||
"edit_game_modal_updating": "Atualizando...",
|
||||
"edit_game_modal_fill_required": "Por favor, preencha todos os campos obrigatórios",
|
||||
"edit_game_modal_success": "Recursos atualizados com sucesso",
|
||||
"edit_game_modal_failed": "Falha ao atualizar recursos",
|
||||
"edit_game_modal_image_filter": "Imagem",
|
||||
"edit_game_modal_icon_resolution": "Resolução recomendada: 256x256px",
|
||||
"edit_game_modal_logo_resolution": "Resolução recomendada: 640x360px",
|
||||
"edit_game_modal_hero_resolution": "Resolução recomendada: 1920x620px",
|
||||
"edit_game_modal_assets": "Imagens",
|
||||
"edit_game_modal_drop_icon_image_here": "Solte a imagem do ícone aqui",
|
||||
"edit_game_modal_drop_logo_image_here": "Solte a imagem do logo aqui",
|
||||
"edit_game_modal_drop_hero_image_here": "Solte a imagem hero aqui",
|
||||
"edit_game_modal_drop_to_replace_icon": "Solte para substituir o ícone",
|
||||
"edit_game_modal_drop_to_replace_logo": "Solte para substituir o logo",
|
||||
"edit_game_modal_drop_to_replace_hero": "Solte para substituir o hero",
|
||||
"install_decky_plugin": "Instalar Plugin Decky",
|
||||
"update_decky_plugin": "Atualizar Plugin Decky",
|
||||
"decky_plugin_installed_version": "Plugin Decky (v{{version}})",
|
||||
"install_decky_plugin_title": "Instalar Plugin Hydra Decky",
|
||||
"install_decky_plugin_message": "Isso irá baixar e instalar o plugin Hydra para Decky Loader. Pode ser necessário permissões elevadas. Continuar?",
|
||||
"update_decky_plugin_title": "Atualizar Plugin Hydra Decky",
|
||||
"update_decky_plugin_message": "Uma nova versão do plugin Hydra Decky está disponível. Gostaria de atualizar agora?",
|
||||
"decky_plugin_installed": "Plugin Decky v{{version}} instalado com sucesso",
|
||||
"decky_plugin_installation_failed": "Falha ao instalar plugin Decky: {{error}}",
|
||||
"decky_plugin_installation_error": "Erro ao instalar plugin Decky: {{error}}",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar"
|
||||
"favorites": "Favoritos"
|
||||
},
|
||||
"header": {
|
||||
"search": "Buscar jogos",
|
||||
@@ -137,7 +76,6 @@
|
||||
"amount_minutes": "{{amount}} minutos",
|
||||
"accuracy": "{{accuracy}}% de precisão",
|
||||
"add_to_library": "Adicionar à biblioteca",
|
||||
"already_in_library": "Já está na biblioteca",
|
||||
"remove_from_library": "Remover da biblioteca",
|
||||
"no_downloads": "Nenhum download disponível",
|
||||
"play_time": "Jogou por {{amount}}",
|
||||
@@ -167,10 +105,8 @@
|
||||
"open_folder": "Abrir pasta",
|
||||
"open_download_location": "Ver arquivos baixados",
|
||||
"create_shortcut": "Criar atalho na área de trabalho",
|
||||
"create_shortcut_simple": "Criar atalho",
|
||||
"remove_files": "Remover arquivos",
|
||||
"options": "Gerenciar",
|
||||
"properties": "Propriedades",
|
||||
"remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca",
|
||||
"remove_from_library_title": "Tem certeza?",
|
||||
"executable_section_title": "Executável",
|
||||
@@ -212,7 +148,6 @@
|
||||
"uploading_backup": "Criando backup…",
|
||||
"no_backups": "Você ainda não fez nenhum backup deste jogo",
|
||||
"backup_uploaded": "Backup criado",
|
||||
"backup_failed": "Falha no backup",
|
||||
"backup_deleted": "Backup apagado",
|
||||
"backup_restored": "Backup restaurado",
|
||||
"see_all_achievements": "Ver todas as conquistas",
|
||||
@@ -254,13 +189,6 @@
|
||||
"download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.",
|
||||
"game_removed_from_favorites": "Jogo removido dos favoritos",
|
||||
"game_added_to_favorites": "Jogo adicionado aos favoritos",
|
||||
"add_to_favorites": "Adicionar aos favoritos",
|
||||
"remove_from_favorites": "Remover dos favoritos",
|
||||
"failed_update_favorites": "Falha ao atualizar favoritos",
|
||||
"game_removed_from_library": "Jogo removido da biblioteca",
|
||||
"failed_remove_from_library": "Falha ao remover da biblioteca",
|
||||
"files_removed_success": "Arquivos removidos com sucesso",
|
||||
"failed_remove_files": "Falha ao remover arquivos",
|
||||
"automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados",
|
||||
"create_start_menu_shortcut": "Criar atalho no Menu Iniciar",
|
||||
"invalid_wine_prefix_path": "Caminho do prefixo Wine inválido",
|
||||
@@ -278,74 +206,7 @@
|
||||
"backup_frozen": "Backup fixado",
|
||||
"backup_unfrozen": "Backup removido dos fixados",
|
||||
"backup_freeze_failed": "Falha ao fixar backup",
|
||||
"backup_freeze_failed_description": "Você deve deixar pelo menos um espaço livre para backups automáticos",
|
||||
"game_details": "Detalhes do Jogo",
|
||||
"currency_symbol": "R$",
|
||||
"currency_country": "br",
|
||||
"prices": "Preços",
|
||||
"no_prices_found": "Nenhum preço encontrado",
|
||||
"view_all_prices": "Clique para ver todos os preços",
|
||||
"retail_price": "Preço de lojas oficiais",
|
||||
"keyshop_price": "Preço em keyshops",
|
||||
"historical_retail": "Preço histórico de lojas oficiais",
|
||||
"historical_keyshop": "Preço histórico em keyshops",
|
||||
"language": "Idioma",
|
||||
"caption": "Legenda",
|
||||
"audio": "Áudio",
|
||||
"filter_by_source": "Filtrar por fonte",
|
||||
"no_repacks_found": "Nenhuma fonte encontrada para este jogo",
|
||||
"edit_game_modal_button": "Alterar detalhes do jogo",
|
||||
"game_added_to_pinned": "Jogo adicionado aos fixados",
|
||||
"game_removed_from_pinned": "Jogo removido dos fixados",
|
||||
"manual_playtime_tooltip": "Este tempo de jogo foi atualizado manualmente",
|
||||
"manual_playtime_warning": "As suas horas de jogo serão marcadas como atualizadas manualmente. Esta ação não pode ser desfeita.",
|
||||
"missing_wine_prefix": "Um prefixo Wine é necessário para criar um backup no Linux",
|
||||
"update_game_playtime": "Modificar tempo de jogo",
|
||||
"update_playtime": "Modificar tempo de jogo",
|
||||
"update_playtime_description": "Atualizar manualmente o tempo de jogo de {{game}}",
|
||||
"update_playtime_error": "Falha ao atualizar tempo de jogo",
|
||||
"update_playtime_title": "Atualizar tempo de jogo",
|
||||
"update_playtime_success": "Tempo de jogo atualizado com sucesso",
|
||||
"show_more": "Mostrar mais",
|
||||
"show_less": "Mostrar menos",
|
||||
"reviews": "Avaliações",
|
||||
"leave_a_review": "Deixar uma Avaliação",
|
||||
"write_review_placeholder": "Compartilhe seus pensamentos sobre este jogo...",
|
||||
"sort_newest": "Mais Recentes",
|
||||
"sort_oldest": "Mais Antigas",
|
||||
"sort_highest_score": "Maior Nota",
|
||||
"sort_lowest_score": "Menor Nota",
|
||||
"sort_most_voted": "Mais Votadas",
|
||||
"no_reviews_yet": "Ainda não há avaliações",
|
||||
"be_first_to_review": "Seja o primeiro a compartilhar seus pensamentos sobre este jogo!",
|
||||
"rating": "Avaliação",
|
||||
"rating_stats": "Avaliação",
|
||||
"rating_very_negative": "Muito Negativo",
|
||||
"rating_negative": "Negativo",
|
||||
"rating_neutral": "Neutro",
|
||||
"rating_positive": "Positivo",
|
||||
"rating_very_positive": "Muito Positivo",
|
||||
"submit_review": "Enviar",
|
||||
"submitting": "Enviando...",
|
||||
"review_submitted_successfully": "Avaliação enviada com sucesso!",
|
||||
"review_submission_failed": "Falha ao enviar avaliação. Por favor, tente novamente.",
|
||||
"review_cannot_be_empty": "O campo de texto da avaliação não pode estar vazio.",
|
||||
"review_deleted_successfully": "Avaliação excluída com sucesso.",
|
||||
"review_deletion_failed": "Falha ao excluir avaliação. Por favor, tente novamente.",
|
||||
"loading_reviews": "Carregando avaliações...",
|
||||
"loading_more_reviews": "Carregando mais avaliações...",
|
||||
"load_more_reviews": "Carregar mais avaliações",
|
||||
"you_seemed_to_enjoy_this_game": "Parece que você gostou deste jogo",
|
||||
"would_you_recommend_this_game": "Gostaria de deixar uma avaliação para este jogo?",
|
||||
"yes": "Sim",
|
||||
"maybe_later": "Talvez mais tarde",
|
||||
"delete_review": "Excluir avaliação",
|
||||
"remove_review": "Remover Avaliação",
|
||||
"delete_review_modal_title": "Tem certeza de que deseja excluir sua avaliação?",
|
||||
"delete_review_modal_description": "Esta ação não pode ser desfeita.",
|
||||
"delete_review_modal_delete_button": "Excluir",
|
||||
"delete_review_modal_cancel_button": "Cancelar",
|
||||
"rating_count": "Avaliação"
|
||||
"backup_freeze_failed_description": "Você deve deixar pelo menos um espaço livre para backups automáticos"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Ativação",
|
||||
@@ -434,7 +295,6 @@
|
||||
"found_download_option_one": "{{countFormatted}} opção de download encontrada",
|
||||
"found_download_option_other": "{{countFormatted}} opções de download encontradas",
|
||||
"import": "Importar",
|
||||
"importing": "Importando...",
|
||||
"privacy": "Privacidade",
|
||||
"private": "Privado",
|
||||
"friends_only": "Apenas amigos",
|
||||
@@ -468,8 +328,6 @@
|
||||
"subscription_renews_on": "Sua assinatura renova dia {{date}}",
|
||||
"bill_sent_until": "Sua próxima cobrança será enviada até esse dia",
|
||||
"no_themes": "Parece que você ainda não tem nenhum tema. Não se preocupe, clique aqui para criar sua primeira obra de arte.",
|
||||
"editor_tab_code": "Código",
|
||||
"editor_tab_info": "Info",
|
||||
"editor_tab_save": "Salvar",
|
||||
"web_store": "Loja de temas",
|
||||
"clear_themes": "Limpar",
|
||||
@@ -487,8 +345,6 @@
|
||||
"delete_theme_description": "Isso irá deletar o tema {{theme}}",
|
||||
"cancel": "Cancelar",
|
||||
"appearance": "Aparência",
|
||||
"debrid": "Debrid",
|
||||
"debrid_description": "Serviços Debrid são downloaders premium sem restrições que permitem baixar rapidamente arquivos hospedados em vários serviços de hospedagem de arquivos, limitados apenas pela sua velocidade de internet.",
|
||||
"enable_torbox": "Habilitar TorBox",
|
||||
"torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.",
|
||||
"torbox_account_linked": "Conta do TorBox vinculada",
|
||||
@@ -508,7 +364,6 @@
|
||||
"installing_common_redist": "Instalando…",
|
||||
"show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo",
|
||||
"extract_files_by_default": "Extrair arquivos automaticamente após o download",
|
||||
"enable_steam_achievements": "Habilitar busca por conquistas da Steam",
|
||||
"enable_achievement_custom_notifications": "Habilitar notificações customizadas de conquistas",
|
||||
"top-left": "Superior esquerdo",
|
||||
"top-center": "Superior central",
|
||||
@@ -541,9 +396,7 @@
|
||||
"game_extracted": "{{title}} extraído com sucesso",
|
||||
"friend_started_playing_game": "{{displayName}} começou a jogar",
|
||||
"test_achievement_notification_title": "Esta é uma notificação de teste",
|
||||
"test_achievement_notification_description": "Bem legal, né?",
|
||||
"notification_achievement_unlocked_title": "Conquista desbloqueada para {{game}}",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} e outras {{count}} foram desbloqueadas"
|
||||
"test_achievement_notification_description": "Bem legal, né?"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Abrir Hydra",
|
||||
@@ -552,8 +405,7 @@
|
||||
"game_card": {
|
||||
"available_one": "Disponível",
|
||||
"available_other": "Disponíveis",
|
||||
"no_downloads": "Sem downloads disponíveis",
|
||||
"calculating": "Calculando"
|
||||
"no_downloads": "Sem downloads disponíveis"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Programas não instalados",
|
||||
@@ -580,18 +432,10 @@
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} horas",
|
||||
"amount_minutes": "{{amount}} minutos",
|
||||
"amount_hours_short": "{{amount}}h",
|
||||
"amount_minutes_short": "{{amount}}m",
|
||||
"last_time_played": "Última sessão {{period}}",
|
||||
"activity": "Atividades recentes",
|
||||
"library": "Biblioteca",
|
||||
"pinned": "Fixados",
|
||||
"sort_by": "Ordenar por:",
|
||||
"achievements_earned": "Conquistas obtidas",
|
||||
"played_recently": "Jogados recentemente",
|
||||
"playtime": "Tempo de jogo",
|
||||
"total_play_time": "Tempo total de jogo",
|
||||
"manual_playtime_tooltip": "Este tempo de jogo foi atualizado manualmente",
|
||||
"no_recent_activity_title": "Hmmm… nada por aqui",
|
||||
"no_recent_activity_description": "Parece que você não jogou nada recentemente. Que tal começar agora?",
|
||||
"display_name": "Nome de exibição",
|
||||
@@ -664,21 +508,7 @@
|
||||
"earned_points": "Pontos ganhos",
|
||||
"show_achievements_on_profile": "Exiba suas conquistas no perfil",
|
||||
"show_points_on_profile": "Exiba seus pontos ganhos no perfil",
|
||||
"error_adding_friend": "Não foi possível enviar o pedido de amizade. Verifique o código de amizade inserido",
|
||||
"friend_code_length_error": "Código de amigo deve ter 8 caracteres",
|
||||
"top_percentile": "Top {{percentile}}%",
|
||||
"playtime": "Tempo de jogo",
|
||||
"played_recently": "Jogado recentemente",
|
||||
"pinned": "Fixado",
|
||||
"amount_minutes_short": "{{amount}}m",
|
||||
"amount_hours_short": "{{amount}}h",
|
||||
"game_added_to_pinned": "Jogo adicionado aos fixados",
|
||||
"game_removed_from_pinned": "Jogo removido dos fixados",
|
||||
"achievements_earned": "Conquistas recebidas",
|
||||
"karma": "Karma",
|
||||
"karma_count": "karma",
|
||||
"karma_description": "Ganho a partir de curtidas positivas em avaliações",
|
||||
"manual_playtime_tooltip": "Este tempo de jogo foi atualizado manualmente"
|
||||
"error_adding_friend": "Não foi possível enviar o pedido de amizade. Verifique o código de amizade inserido"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Conquista desbloqueada",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Sessão iniciada com sucesso"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Destaques",
|
||||
"hot": "Populares",
|
||||
"weekly": "📅 Mais descarregados esta semana",
|
||||
"achievements": "🏆 Para completar",
|
||||
@@ -25,8 +26,7 @@
|
||||
"game_has_no_executable": "O jogo não tem um executável selecionado",
|
||||
"sign_in": "Iniciar sessão",
|
||||
"friends": "Amigos",
|
||||
"favorites": "Favoritos",
|
||||
"edit_game_modal_cancel": "Cancelar"
|
||||
"favorites": "Favoritos"
|
||||
},
|
||||
"header": {
|
||||
"search": "Procurar jogos",
|
||||
@@ -142,7 +142,6 @@
|
||||
"uploading_backup": "A criar backup…",
|
||||
"no_backups": "Ainda não fizeste nenhum backup deste jogo",
|
||||
"backup_uploaded": "Backup criado",
|
||||
"backup_failed": "Falha ao criar backup",
|
||||
"backup_deleted": "Backup apagado",
|
||||
"backup_restored": "Backup restaurado",
|
||||
"see_all_achievements": "Ver todas as conquistas",
|
||||
@@ -248,6 +247,9 @@
|
||||
"download_count_zero": "Sem downloads na lista",
|
||||
"download_count_one": "{{countFormatted}} download na lista",
|
||||
"download_count_other": "{{countFormatted}} downloads na lista",
|
||||
"download_options_zero": "Sem downloads disponíveis",
|
||||
"download_options_one": "{{countFormatted}} download disponível",
|
||||
"download_options_other": "{{countFormatted}} downloads disponíveis",
|
||||
"download_source_url": "URL da fonte",
|
||||
"add_download_source_description": "Insere o URL que contém o ficheiro .json",
|
||||
"download_source_up_to_date": "Sincronizada",
|
||||
@@ -267,7 +269,6 @@
|
||||
"found_download_option_one": "{{countFormatted}} opção de transferência encontrada",
|
||||
"found_download_option_other": "{{countFormatted}} opções de transferência encontradas",
|
||||
"import": "Importar",
|
||||
"importing": "A importar...",
|
||||
"privacy": "Privacidade",
|
||||
"private": "Privado",
|
||||
"friends_only": "Apenas amigos",
|
||||
@@ -358,6 +359,8 @@
|
||||
"instructions": "Verifica a forma correta de instalar algum deles na tua distribuição Linux, para garantir a execução normal do jogo"
|
||||
},
|
||||
"catalogue": {
|
||||
"next_page": "Página seguinte",
|
||||
"previous_page": "Página anterior",
|
||||
"search": "Filtrar…",
|
||||
"developers": "Desenvolvedores",
|
||||
"genres": "Géneros",
|
||||
@@ -377,18 +380,10 @@
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} horas",
|
||||
"amount_minutes": "{{amount}} minutos",
|
||||
"amount_hours_short": "{{amount}}h",
|
||||
"amount_minutes_short": "{{amount}}m",
|
||||
"last_time_played": "Última sessão {{period}}",
|
||||
"activity": "Atividade recente",
|
||||
"library": "Biblioteca",
|
||||
"pinned": "Fixados",
|
||||
"sort_by": "Ordenar por:",
|
||||
"achievements_earned": "Conquistas obtidas",
|
||||
"played_recently": "Jogados recentemente",
|
||||
"playtime": "Tempo de jogo",
|
||||
"total_play_time": "Tempo total de jogo",
|
||||
"manual_playtime_tooltip": "Este tempo de jogo foi atualizado manualmente",
|
||||
"no_recent_activity_title": "Hmmm… não há nada por aqui",
|
||||
"no_recent_activity_description": "Parece que não jogaste nada recentemente. Que tal começar agora?",
|
||||
"display_name": "Nome de apresentação",
|
||||
@@ -432,6 +427,7 @@
|
||||
"friend_code_copied": "Código de amigo copiado",
|
||||
"undo_friendship_modal_text": "Isto vai remover a tua amizade com {{displayName}}",
|
||||
"privacy_hint": "Para controlar quem pode ver o teu perfil, acede às <0>Definições</0>",
|
||||
"profile_locked": "Este perfil é privado",
|
||||
"image_process_failure": "Falha ao processar a imagem",
|
||||
"required_field": "Este campo é obrigatório",
|
||||
"displayname_min_length": "O nome de apresentação deve ter pelo menos 3 caracteres",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"language_name": "Română",
|
||||
"home": {
|
||||
"featured": "Recomandate",
|
||||
"surprise_me": "Surprinde-mă",
|
||||
"no_results": "Niciun rezultat găsit"
|
||||
},
|
||||
@@ -18,6 +19,7 @@
|
||||
},
|
||||
"header": {
|
||||
"search": "Caută jocuri",
|
||||
|
||||
"home": "Acasă",
|
||||
"catalogue": "Catalog",
|
||||
"downloads": "Descărcări",
|
||||
@@ -30,7 +32,10 @@
|
||||
"downloading": "Se descarcă {{title}}... ({{percentage}} complet) - Concluzie {{eta}} - {{speed}}",
|
||||
"calculating_eta": "Se descarcă {{title}}... ({{percentage}} complet) - Calculare timp rămas..."
|
||||
},
|
||||
"catalogue": {},
|
||||
"catalogue": {
|
||||
"next_page": "Pagina următoare",
|
||||
"previous_page": "Pagina anterioară"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "Deschide opțiunile de descărcare",
|
||||
"download_options_zero": "Nicio opțiune de descărcare",
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
"successfully_signed_in": "Успешный вход"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Рекомендации",
|
||||
"surprise_me": "Удиви меня",
|
||||
"no_results": "Ничего не найдено",
|
||||
"start_typing": "Начинаю вводить текст...",
|
||||
"hot": "Сейчас популярно",
|
||||
"start_typing": "Начинаю вводить текст...",
|
||||
"weekly": "📅 Лучшие игры недели",
|
||||
"achievements": "🏆 Игры с достижениями"
|
||||
},
|
||||
@@ -27,68 +28,7 @@
|
||||
"friends": "Друзья",
|
||||
"need_help": "Нужна помощь?",
|
||||
"favorites": "Избранное",
|
||||
"playable_button_title": "Показать только установленные игры.",
|
||||
"add_custom_game_tooltip": "Добавить пользовательскую игру",
|
||||
"show_playable_only_tooltip": "Показать только доступные для игры",
|
||||
"custom_game_modal": "Добавить пользовательскую игру",
|
||||
"custom_game_modal_description": "Добавьте пользовательскую игру в библиотеку, выбрав исполняемый файл",
|
||||
"custom_game_modal_executable_path": "Путь к исполняемому файлу",
|
||||
"custom_game_modal_select_executable": "Выберите исполняемый файл",
|
||||
"custom_game_modal_title": "Название игры",
|
||||
"custom_game_modal_enter_title": "Введите название игры",
|
||||
"custom_game_modal_browse": "Обзор",
|
||||
"custom_game_modal_cancel": "Отмена",
|
||||
"custom_game_modal_add": "Добавить игру",
|
||||
"custom_game_modal_adding": "Добавление игры...",
|
||||
"custom_game_modal_success": "Пользовательская игра успешно добавлена",
|
||||
"custom_game_modal_failed": "Не удалось добавить пользовательскую игру",
|
||||
"custom_game_modal_executable": "Исполняемый файл",
|
||||
"edit_game_modal": "Настроить ресурсы",
|
||||
"edit_game_modal_description": "Настройте ресурсы и детали игры",
|
||||
"edit_game_modal_title": "Название",
|
||||
"edit_game_modal_enter_title": "Введите название",
|
||||
"edit_game_modal_image": "Изображение",
|
||||
"edit_game_modal_select_image": "Выберите изображение",
|
||||
"edit_game_modal_browse": "Обзор",
|
||||
"edit_game_modal_image_preview": "Предпросмотр изображения",
|
||||
"edit_game_modal_icon": "Иконка",
|
||||
"edit_game_modal_select_icon": "Выберите иконку",
|
||||
"edit_game_modal_icon_preview": "Предпросмотр иконки",
|
||||
"edit_game_modal_logo": "Логотип",
|
||||
"edit_game_modal_select_logo": "Выберите логотип",
|
||||
"edit_game_modal_logo_preview": "Предпросмотр логотипа",
|
||||
"edit_game_modal_hero": "Изображение обложку игры",
|
||||
"edit_game_modal_select_hero": "Выберите обложку игры",
|
||||
"edit_game_modal_hero_preview": "Предпросмотр обложки игры",
|
||||
"edit_game_modal_cancel": "Отмена",
|
||||
"edit_game_modal_update": "Обновить",
|
||||
"edit_game_modal_updating": "Обновление...",
|
||||
"edit_game_modal_fill_required": "Пожалуйста, заполните все обязательные поля",
|
||||
"edit_game_modal_success": "Ресурсы успешно обновлены",
|
||||
"edit_game_modal_failed": "Не удалось обновить ресурсы",
|
||||
"edit_game_modal_image_filter": "Изображение",
|
||||
"edit_game_modal_icon_resolution": "Рекомендуемое разрешение: 256x256px",
|
||||
"edit_game_modal_logo_resolution": "Рекомендуемое разрешение: 640x360px",
|
||||
"edit_game_modal_hero_resolution": "Рекомендуемое разрешение: 1920x620px",
|
||||
"edit_game_modal_assets": "Ресурсы",
|
||||
"edit_game_modal_drop_icon_image_here": "Перетащите изображение иконки сюда",
|
||||
"edit_game_modal_drop_logo_image_here": "Перетащите изображение логотипа сюда",
|
||||
"edit_game_modal_drop_hero_image_here": "Перетащите изображение обложки сюда",
|
||||
"edit_game_modal_drop_to_replace_icon": "Перетащите для замены иконки",
|
||||
"edit_game_modal_drop_to_replace_logo": "Перетащите для замены логотипа",
|
||||
"edit_game_modal_drop_to_replace_hero": "Перетащите для замены обложки",
|
||||
"install_decky_plugin": "Установить плагин Decky",
|
||||
"update_decky_plugin": "Обновить плагин Decky",
|
||||
"decky_plugin_installed_version": "Плагин Decky (v{{version}})",
|
||||
"install_decky_plugin_title": "Установить плагин Hydra Decky",
|
||||
"install_decky_plugin_message": "Это загрузит и установит плагин Hydra для Decky Loader. Может потребоваться повышенные разрешения. Продолжить?",
|
||||
"update_decky_plugin_title": "Обновить плагин Hydra Decky",
|
||||
"update_decky_plugin_message": "Доступна новая версия плагина Hydra Decky. Хотите обновить его сейчас?",
|
||||
"decky_plugin_installed": "Плагин Decky v{{version}} успешно установлен",
|
||||
"decky_plugin_installation_failed": "Не удалось установить плагин Decky: {{error}}",
|
||||
"decky_plugin_installation_error": "Ошибка установки плагина Decky: {{error}}",
|
||||
"confirm": "Подтвердить",
|
||||
"cancel": "Отмена"
|
||||
"playable_button_title": "Показать только игры, в которые можно играть сейчас"
|
||||
},
|
||||
"header": {
|
||||
"search": "Поиск",
|
||||
@@ -149,7 +89,6 @@
|
||||
"amount_minutes": "{{amount}} минут",
|
||||
"accuracy": "точность {{accuracy}}%",
|
||||
"add_to_library": "Добавить в библиотеку",
|
||||
"already_in_library": "Уже в библиотеке",
|
||||
"remove_from_library": "Удалить из библиотеки",
|
||||
"no_downloads": "Нет доступных источников",
|
||||
"play_time": "Сыграно {{amount}}",
|
||||
@@ -178,13 +117,11 @@
|
||||
"open_folder": "Открыть папку",
|
||||
"open_download_location": "Просмотреть папку загрузок",
|
||||
"create_shortcut": "Создать ярлык на рабочем столе",
|
||||
"create_shortcut_simple": "Создать ярлык",
|
||||
"clear": "Очистить",
|
||||
"remove_files": "Удалить файлы",
|
||||
"remove_from_library_title": "Вы уверены?",
|
||||
"remove_from_library_description": "{{game}} будет удалена из вашей библиотеки.",
|
||||
"options": "Настройки",
|
||||
"properties": "Свойства",
|
||||
"executable_section_title": "Файл",
|
||||
"executable_section_description": "Путь к файлу, который будет запущен при нажатии на \"Play\"",
|
||||
"downloads_section_title": "Загрузки",
|
||||
@@ -194,71 +131,22 @@
|
||||
"download_in_progress": "Идёт загрузка",
|
||||
"download_paused": "Загрузка приостановлена",
|
||||
"last_downloaded_option": "Последний вариант загрузки",
|
||||
"create_steam_shortcut": "Создать ярлык Steam",
|
||||
"create_shortcut_success": "Ярлык создан",
|
||||
"you_might_need_to_restart_steam": "Возможно, вам потребуется перезапустить Steam, чтобы увидеть изменения",
|
||||
"create_shortcut_error": "Не удалось создать ярлык",
|
||||
"add_to_favorites": "Добавить в избранное",
|
||||
"remove_from_favorites": "Удалить из избранного",
|
||||
"failed_update_favorites": "Не удалось обновить избранное",
|
||||
"game_removed_from_library": "Игра удалена из библиотеки",
|
||||
"failed_remove_from_library": "Не удалось удалить из библиотеки",
|
||||
"files_removed_success": "Файлы успешно удалены",
|
||||
"failed_remove_files": "Не удалось удалить файлы",
|
||||
"nsfw_content_title": "Эта игра содержит неприемлемый контент",
|
||||
"nsfw_content_description": "{{title}} содержит контент, который может не подходить для всех возрастов. \nВы уверены, что хотите продолжить?",
|
||||
"allow_nsfw_content": "Продолжить",
|
||||
"download": "Скачать",
|
||||
"download_count": "Загрузки",
|
||||
"download_error": "Этот вариант загрузки недоступен",
|
||||
"executable_path_in_use": "Исполняемый файл уже используется \"{{game}}\"",
|
||||
"nsfw_content_description": "{{title}} содержит контент, который может не подходить для всех возрастов. \nВы уверены, что хотите продолжить?",
|
||||
"nsfw_content_title": "Эта игра содержит неприемлемый контент",
|
||||
"refuse_nsfw_content": "Назад",
|
||||
"stats": "Статистика",
|
||||
"download_count": "Загрузки",
|
||||
"player_count": "Активные игроки",
|
||||
"download_error": "Этот вариант загрузки недоступен",
|
||||
"download": "Скачать",
|
||||
"executable_path_in_use": "Исполняемый файл уже используется \"{{game}}\"",
|
||||
"warning": "Внимание:",
|
||||
"hydra_needs_to_remain_open": "Для этой загрузки Hydra должна оставаться открытой до завершения. Если Hydra закроется до завершения, вы потеряете прогресс.",
|
||||
"achievements": "Достижения",
|
||||
"achievements_count": "Достижения {{unlockedCount}}/{{achievementsCount}}",
|
||||
"show_more": "Показать больше",
|
||||
"show_less": "Показать меньше",
|
||||
"reviews": "Отзывы",
|
||||
"leave_a_review": "Оставить отзыв",
|
||||
"write_review_placeholder": "Поделитесь своими мыслями об этой игре...",
|
||||
"sort_newest": "Сначала новые",
|
||||
"no_reviews_yet": "Пока нет отзывов",
|
||||
"be_first_to_review": "Станьте первым, кто поделится своими мыслями об этой игре!",
|
||||
"sort_oldest": "Сначала старые",
|
||||
"sort_highest_score": "Высший балл",
|
||||
"sort_lowest_score": "Низший балл",
|
||||
"sort_most_voted": "Самые популярные",
|
||||
"rating": "Оценка",
|
||||
"rating_stats": "Оценка",
|
||||
"rating_very_negative": "Очень негативно",
|
||||
"rating_negative": "Негативно",
|
||||
"rating_neutral": "Нейтрально",
|
||||
"rating_positive": "Позитивно",
|
||||
"rating_very_positive": "Очень позитивно",
|
||||
"submit_review": "Отправить",
|
||||
"submitting": "Отправка...",
|
||||
"review_submitted_successfully": "Отзыв успешно отправлен!",
|
||||
"review_submission_failed": "Не удалось отправить отзыв. Пожалуйста, попробуйте снова.",
|
||||
"review_cannot_be_empty": "Текстовое поле отзыва не может быть пустым.",
|
||||
"review_deleted_successfully": "Отзыв успешно удален.",
|
||||
"review_deletion_failed": "Не удалось удалить отзыв. Пожалуйста, попробуйте снова.",
|
||||
"loading_reviews": "Загрузка отзывов...",
|
||||
"loading_more_reviews": "Загрузка дополнительных отзывов...",
|
||||
"load_more_reviews": "Загрузить больше отзывов",
|
||||
"you_seemed_to_enjoy_this_game": "Похоже, вам понравилась эта игра",
|
||||
"would_you_recommend_this_game": "Хотите оставить отзыв об этой игре?",
|
||||
"yes": "Да",
|
||||
"maybe_later": "Возможно позже",
|
||||
"rating_count": "Оценка",
|
||||
"delete_review": "Удалить отзыв",
|
||||
"remove_review": "Удалить отзыв",
|
||||
"delete_review_modal_title": "Вы уверены, что хотите удалить свой отзыв?",
|
||||
"delete_review_modal_description": "Это действие нельзя отменить.",
|
||||
"delete_review_modal_delete_button": "Удалить",
|
||||
"delete_review_modal_cancel_button": "Отмена",
|
||||
"cloud_save": "Облачное сохранение",
|
||||
"cloud_save_description": "Сохраняйте ваш прогресс в облаке и продолжайте играть на любом устройстве",
|
||||
"backups": "Резервные копии",
|
||||
@@ -271,7 +159,6 @@
|
||||
"uploading_backup": "Загрузка резервной копии…",
|
||||
"no_backups": "Вы еще не создали резервных копий для этой игры",
|
||||
"backup_uploaded": "Резервная копия загружена",
|
||||
"backup_failed": "Ошибка резервного копирования",
|
||||
"backup_deleted": "Резервная копия удалена",
|
||||
"backup_restored": "Резервная копия восстановлена",
|
||||
"see_all_achievements": "Просмотреть все достижения",
|
||||
@@ -308,55 +195,17 @@
|
||||
"download_error_gofile_quota_exceeded": "Вы превысили месячную квоту Gofile. Пожалуйста, подождите, пока квота не будет восстановлена.",
|
||||
"download_error_real_debrid_account_not_authorized": "Ваш аккаунт Real-Debrid не авторизован для осуществления новых загрузок. Пожалуйста, проверьте настройки учетной записи и повторите попытку.",
|
||||
"download_error_not_cached_on_real_debrid": "Эта загрузка недоступна на Real-Debrid, и получение статуса загрузки с Real-Debrid пока недоступно.",
|
||||
"update_playtime_title": "Обновить время игры",
|
||||
"update_playtime_description": "Вручную обновите время игры для {{game}}",
|
||||
"update_playtime": "Обновить время игры",
|
||||
"update_playtime_success": "Время игры успешно обновлено",
|
||||
"update_playtime_error": "Не удалось обновить время игры",
|
||||
"update_game_playtime": "Обновить время игры",
|
||||
"manual_playtime_warning": "Ваши часы будут отмечены как обновленные вручную. Это действие нельзя отменить.",
|
||||
"manual_playtime_tooltip": "Это время игры было обновлено вручную",
|
||||
"download_error_not_cached_on_torbox": "Эта загрузка недоступна на TorBox, и получить статус загрузки с TorBox пока невозможно.",
|
||||
"download_error_not_cached_on_hydra": "Эта загрузка недоступна на Nimbus.",
|
||||
"game_removed_from_favorites": "Игра удалена из избранного",
|
||||
"game_added_to_favorites": "Игра добавлена в избранное",
|
||||
"game_removed_from_pinned": "Игра удалена из закрепленных",
|
||||
"game_added_to_pinned": "Игра добавлена в закрепленные",
|
||||
"game_removed_from_favorites": "Игра удалена из избранного",
|
||||
"automatically_extract_downloaded_files": "Автоматическая распаковка загруженных файлов",
|
||||
"create_steam_shortcut": "Создать ярлык Steam",
|
||||
"you_might_need_to_restart_steam": "Возможно, вам потребуется перезапустить Steam, чтобы увидеть изменения",
|
||||
"create_start_menu_shortcut": "Создать ярлык в меню «Пуск»",
|
||||
"invalid_wine_prefix_path": "Недопустимый путь префикса Wine",
|
||||
"invalid_wine_prefix_path_description": "Путь к префиксу Wine недействителен. Пожалуйста, проверьте путь и попробуйте снова.",
|
||||
"missing_wine_prefix": "Префикс Wine необходим для создания резервной копии в Linux",
|
||||
"artifact_renamed": "Резервная копия успешно переименована",
|
||||
"rename_artifact": "Переименовать резервную копию",
|
||||
"rename_artifact_description": "Переименуйте резервную копию, присвоив ей более описательное имя.",
|
||||
"artifact_name_label": "Название резервной копии",
|
||||
"artifact_name_placeholder": "Введите название для резервной копии",
|
||||
"save_changes": "Сохранить изменения",
|
||||
"required_field": "Это поле обязательно к заполнению",
|
||||
"max_length_field": "Это поле должно содержать менее {{length}} символов",
|
||||
"freeze_backup": "Закрепить, чтобы она не была перезаписана автоматическими резервными копиями",
|
||||
"unfreeze_backup": "Открепить",
|
||||
"backup_frozen": "Резервная копия закреплена",
|
||||
"backup_unfrozen": "Резервная копия откреплена",
|
||||
"backup_freeze_failed": "Не удалось закрепить резервную копию",
|
||||
"backup_freeze_failed_description": "Вы должны оставить как минимум один свободный слот для автоматических резервных копий",
|
||||
"edit_game_modal_button": "Изменить детали игры",
|
||||
"game_details": "Детали игры",
|
||||
"currency_symbol": "₽",
|
||||
"currency_country": "ru",
|
||||
"prices": "Цены",
|
||||
"no_prices_found": "Цены не найдены",
|
||||
"view_all_prices": "Нажмите, чтобы посмотреть все цены",
|
||||
"retail_price": "Розничная цена",
|
||||
"keyshop_price": "Цена в магазине ключей",
|
||||
"historical_retail": "Исторические розничные цены",
|
||||
"historical_keyshop": "Исторические цены в магазинах ключей",
|
||||
"language": "Язык",
|
||||
"caption": "Субтитры",
|
||||
"audio": "Аудио",
|
||||
"filter_by_source": "Фильтр по источнику",
|
||||
"no_repacks_found": "Источники для этой игры не найдены"
|
||||
"download_error_not_cached_on_hydra": "Эта загрузка недоступна на Nimbus."
|
||||
},
|
||||
"activation": {
|
||||
"title": "Активировать Hydra",
|
||||
@@ -402,10 +251,13 @@
|
||||
"change": "Изменить",
|
||||
"notifications": "Уведомления",
|
||||
"enable_download_notifications": "По завершении загрузки",
|
||||
"enable_achievement_notifications": "Когда достижение разблокировано",
|
||||
"enable_repack_list_notifications": "При добавлении нового репака",
|
||||
"real_debrid_api_token_label": "Real-Debrid API-токен",
|
||||
"quit_app_instead_hiding": "Закрывать приложение вместо сворачивания в трей",
|
||||
"launch_with_system": "Запускать Hydra вместе с системой",
|
||||
"launch_minimized": "Запустить Hydra в свернутом виде",
|
||||
"disable_nsfw_alert": "Отключить предупреждение о непристойном контенте",
|
||||
"general": "Основные",
|
||||
"behavior": "Поведение",
|
||||
"download_sources": "Источники загрузки",
|
||||
@@ -432,11 +284,11 @@
|
||||
"download_source_errored": "Ошибка",
|
||||
"sync_download_sources": "Обновить источники",
|
||||
"removed_download_source": "Источник удален",
|
||||
"removed_download_sources": "Источники удалены",
|
||||
"cancel_button_confirmation_delete_all_sources": "Нет",
|
||||
"confirm_button_confirmation_delete_all_sources": "Да, удалить все",
|
||||
"title_confirmation_delete_all_sources": "Удалить все источники",
|
||||
"description_confirmation_delete_all_sources": "Вы удалите все источники",
|
||||
"title_confirmation_delete_all_sources": "Удалить все источники",
|
||||
"removed_download_sources": "Источники удалены",
|
||||
"button_delete_all_sources": "Удалить все источники",
|
||||
"added_download_source": "Источник добавлен",
|
||||
"download_sources_synced": "Все источники обновлены",
|
||||
@@ -445,21 +297,17 @@
|
||||
"found_download_option_one": "Найден {{countFormatted}} вариант загрузки",
|
||||
"found_download_option_other": "Найдено {{countFormatted}} вариантов загрузки",
|
||||
"import": "Импортировать",
|
||||
"importing": "Импортируется...",
|
||||
"public": "Публичный",
|
||||
"private": "Частный",
|
||||
"blocked_users": "Заблокированные пользователи",
|
||||
"friends_only": "Только для друзей",
|
||||
"must_be_valid_url": "У источника должен быть правильный URL",
|
||||
"privacy": "Конфиденциальность",
|
||||
"private": "Частный",
|
||||
"profile_visibility": "Видимость профиля",
|
||||
"profile_visibility_description": "Выберите, кто может видеть ваш профиль и библиотеку",
|
||||
"public": "Публичный",
|
||||
"required_field": "Это поле обязательно к заполнению",
|
||||
"source_already_exists": "Этот источник уже добавлен",
|
||||
"must_be_valid_url": "У источника должен быть правильный URL",
|
||||
"blocked_users": "Заблокированные пользователи",
|
||||
"user_unblocked": "Пользователь разблокирован",
|
||||
"enable_achievement_notifications": "Когда достижение разблокировано",
|
||||
"launch_minimized": "Запускать Hydra в свернутом виде",
|
||||
"disable_nsfw_alert": "Отключить предупреждение о непристойном контенте",
|
||||
"seed_after_download_complete": "Раздавать после завершения загрузки",
|
||||
"show_hidden_achievement_description": "Показывать описание скрытых достижений перед их получением",
|
||||
"account": "Аккаунт",
|
||||
@@ -498,14 +346,12 @@
|
||||
"delete_theme_description": "Это приведет к удалению темы {{theme}}",
|
||||
"cancel": "Отменить",
|
||||
"appearance": "Внешний вид",
|
||||
"debrid": "Debrid",
|
||||
"debrid_description": "Сервисы Debrid - это премиум-загрузчики без ограничений, которые позволяют быстро скачивать файлы с различных файлообменников, ограничиваясь только скоростью вашего интернета.",
|
||||
"enable_torbox": "Включить TorBox",
|
||||
"torbox_description": "TorBox - это ваш премиум-сервис, конкурирующий даже с лучшими серверами на рынке.",
|
||||
"torbox_account_linked": "Аккаунт TorBox привязан",
|
||||
"real_debrid_account_linked": "Аккаунт Real-Debrid привязан",
|
||||
"create_real_debrid_account": "Нажмите здесь, если у вас еще нет аккаунта Real-Debrid",
|
||||
"create_torbox_account": "Нажмите здесь, если у вас еще нет учетной записи TorBox",
|
||||
"real_debrid_account_linked": "Аккаунт Real-Debrid привязан",
|
||||
"name_min_length": "Название темы должно содержать не менее 3 символов",
|
||||
"import_theme": "Импортировать тему",
|
||||
"import_theme_description": "Вы импортируете {{theme}} из магазина тем",
|
||||
@@ -519,15 +365,14 @@
|
||||
"installing_common_redist": "Установка…",
|
||||
"show_download_speed_in_megabytes": "Показать скорость загрузки в мегабайтах в секунду",
|
||||
"extract_files_by_default": "Извлекать файлы по умолчанию после загрузки",
|
||||
"enable_steam_achievements": "Включить поиск достижений Steam",
|
||||
"achievement_custom_notification_position": "Позиция уведомлений достижений",
|
||||
"achievement_custom_notification_position": "Позиция настраиваемых уведомлений о достижениях",
|
||||
"top-left": "Верхний левый угол",
|
||||
"top-center": "Верхний центр",
|
||||
"top-right": "Верхний правый угол",
|
||||
"bottom-left": "Нижний левый угол",
|
||||
"bottom-center": "Нижний центр",
|
||||
"bottom-right": "Нижний правый угол",
|
||||
"enable_achievement_custom_notifications": "Включить уведомления о достижениях",
|
||||
"enable_achievement_custom_notifications": "Включить настраиваемые уведомления о достижениях",
|
||||
"alignment": "Выравнивание",
|
||||
"variation": "Вариация",
|
||||
"default": "По умолчанию",
|
||||
@@ -548,8 +393,8 @@
|
||||
"restart_to_install_update": "Перезапустите Hydra для установки обновления",
|
||||
"notification_achievement_unlocked_title": "Достижение разблокировано для {{game}}",
|
||||
"notification_achievement_unlocked_body": "были разблокированы {{achievement}} и другие {{count}}",
|
||||
"new_friend_request_description": "{{displayName}} отправил вам запрос в друзья",
|
||||
"new_friend_request_title": "Новый запрос на добавление в друзья",
|
||||
"new_friend_request_description": "Вы получили новый запрос на добавление в друзья",
|
||||
"extraction_complete": "Распаковка завершена",
|
||||
"game_extracted": "{{title}} успешно распакован",
|
||||
"friend_started_playing_game": "{{displayName}} начал играть в игру",
|
||||
@@ -563,8 +408,7 @@
|
||||
"game_card": {
|
||||
"available_one": "Доступный",
|
||||
"available_other": "Доступный",
|
||||
"no_downloads": "Нет доступных источников",
|
||||
"calculating": "Вычисление"
|
||||
"no_downloads": "Нет доступных источников"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Программы не установлены",
|
||||
@@ -580,17 +424,10 @@
|
||||
"user_profile": {
|
||||
"amount_hours": "{{amount}} часов",
|
||||
"amount_minutes": "{{amount}} минут",
|
||||
"amount_hours_short": "{{amount}}ч",
|
||||
"amount_minutes_short": "{{amount}}м",
|
||||
"last_time_played": "Последняя игра {{period}}",
|
||||
"activity": "Недавняя активность",
|
||||
"library": "Библиотека",
|
||||
"pinned": "Закрепленные",
|
||||
"achievements_earned": "Заработанные достижения",
|
||||
"played_recently": "Недавно сыгранные",
|
||||
"playtime": "Время игры",
|
||||
"total_play_time": "Всего сыграно",
|
||||
"manual_playtime_tooltip": "Время игры было обновлено вручную",
|
||||
"no_recent_activity_title": "Хммм... Тут ничего нет",
|
||||
"no_recent_activity_description": "Вы давно ни во что не играли. Пора это изменить!",
|
||||
"display_name": "Отображаемое имя",
|
||||
@@ -632,24 +469,24 @@
|
||||
"no_pending_invites": "У вас нет запросов ожидающих ответа",
|
||||
"no_blocked_users": "Вы не заблокировали ни одного пользователя",
|
||||
"friend_code_copied": "Код друга скопирован",
|
||||
"undo_friendship_modal_text": "Это отменит вашу дружбу с {{displayName}}.",
|
||||
"privacy_hint": "Чтобы указать, кто может это видеть, перейдите в <0>Настройки</0>.",
|
||||
"locked_profile": "Этот профиль является частным",
|
||||
"displayname_max_length": "Отображаемое имя должно содержать не более 50 символов.",
|
||||
"displayname_min_length": "Отображаемое имя должно содержать не менее 3 символов.",
|
||||
"image_process_failure": "Сбой при обработке изображения",
|
||||
"required_field": "Это поле обязательно к заполнению",
|
||||
"displayname_min_length": "Отображаемое имя должно содержать не менее 3 символов.",
|
||||
"displayname_max_length": "Отображаемое имя должно содержать не более 50 символов.",
|
||||
"report_profile": "Пожаловаться на этот профиль",
|
||||
"report_reason": "Почему вы жалуетесь на этот профиль?",
|
||||
"locked_profile": "Этот профиль является частным",
|
||||
"privacy_hint": "Чтобы указать, кто может это видеть, перейдите в <0>Настройки</0>.",
|
||||
"profile_reported": "Профиль сообщил",
|
||||
"report": "Отчет",
|
||||
"report_description": "Дополнительная информация",
|
||||
"report_description_placeholder": "Дополнительная информация",
|
||||
"report": "Пожаловаться",
|
||||
"report_profile": "Пожаловаться на этот профиль",
|
||||
"report_reason": "Почему вы жалуетесь на этот профиль?",
|
||||
"report_reason_hate": "Разжигание ненависти",
|
||||
"report_reason_other": "Другой",
|
||||
"report_reason_sexual_content": "Сексуальный контент",
|
||||
"report_reason_violence": "Насилие",
|
||||
"report_reason_spam": "Спам",
|
||||
"report_reason_other": "Другое",
|
||||
"profile_reported": "Жалоба на профиль отправлена",
|
||||
"report_reason_violence": "Насилие",
|
||||
"required_field": "Это поле обязательно к заполнению",
|
||||
"undo_friendship_modal_text": "Это отменит вашу дружбу с {{displayName}}.",
|
||||
"your_friend_code": "Код вашего друга:",
|
||||
"upload_banner": "Загрузить баннер",
|
||||
"uploading_banner": "Загрузка баннера...",
|
||||
@@ -663,14 +500,7 @@
|
||||
"achievements_unlocked": "Достижения разблокированы",
|
||||
"earned_points": "Заработано очков:",
|
||||
"show_achievements_on_profile": "Покажите свои достижения в профиле",
|
||||
"show_points_on_profile": "Показывать заработанные очки в своем профиле",
|
||||
"error_adding_friend": "Не удалось отправить запрос в друзья. Пожалуйста, проверьте код друга",
|
||||
"friend_code_length_error": "Код друга должен содержать 8 символов",
|
||||
"game_removed_from_pinned": "Игра удалена из закрепленных",
|
||||
"game_added_to_pinned": "Игра добавлена в закрепленные",
|
||||
"karma": "Карма",
|
||||
"karma_count": "карма",
|
||||
"karma_description": "Заработана положительными оценками отзывов"
|
||||
"show_points_on_profile": "Показывать заработанные очки в своем профиле"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Достижение разблокировано",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Inloggningen lyckades"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Utvalt",
|
||||
"surprise_me": "Överraska mig",
|
||||
"no_results": "Inga resultat hittades",
|
||||
"start_typing": "Börja skriva för att söka...",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Başarıyla giriş yapıldı"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Öne Çıkanlar",
|
||||
"surprise_me": "Beni Şaşırt",
|
||||
"no_results": "Sonuç bulunamadı",
|
||||
"start_typing": "Aramak için yazmaya başlayın...",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Успішний вхід в систему"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Рекомендоване",
|
||||
"surprise_me": "Здивуй мене",
|
||||
"no_results": "Результатів не знайдено",
|
||||
"start_typing": "Почніть набирати текст для пошуку...",
|
||||
@@ -27,68 +28,7 @@
|
||||
"favorites": "Улюблені",
|
||||
"friends": "Друзі",
|
||||
"need_help": "Потрібна допомога?",
|
||||
"playable_button_title": "Показати лише ігри, які можна грати зараз",
|
||||
"add_custom_game_tooltip": "Додати власну гру",
|
||||
"show_playable_only_tooltip": "Показати лише доступні для гри",
|
||||
"custom_game_modal": "Додати власну гру",
|
||||
"custom_game_modal_description": "Додайте власну гру до бібліотеки, вибравши виконуваний файл",
|
||||
"custom_game_modal_executable_path": "Шлях до виконуваного файлу",
|
||||
"custom_game_modal_select_executable": "Виберіть виконуваний файл",
|
||||
"custom_game_modal_title": "Назва гри",
|
||||
"custom_game_modal_enter_title": "Введіть назву гри",
|
||||
"custom_game_modal_browse": "Огляд",
|
||||
"custom_game_modal_cancel": "Скасувати",
|
||||
"custom_game_modal_add": "Додати гру",
|
||||
"custom_game_modal_adding": "Додавання гри...",
|
||||
"custom_game_modal_success": "Власну гру успішно додано",
|
||||
"custom_game_modal_failed": "Не вдалося додати власну гру",
|
||||
"custom_game_modal_executable": "Виконуваний файл",
|
||||
"edit_game_modal": "Налаштувати ресурси",
|
||||
"edit_game_modal_description": "Налаштуйте ресурси та деталі гри",
|
||||
"edit_game_modal_title": "Назва",
|
||||
"edit_game_modal_enter_title": "Введіть назву",
|
||||
"edit_game_modal_image": "Зображення",
|
||||
"edit_game_modal_select_image": "Виберіть зображення",
|
||||
"edit_game_modal_browse": "Огляд",
|
||||
"edit_game_modal_image_preview": "Попередній перегляд зображення",
|
||||
"edit_game_modal_icon": "Іконка",
|
||||
"edit_game_modal_select_icon": "Виберіть іконку",
|
||||
"edit_game_modal_icon_preview": "Попередній перегляд іконки",
|
||||
"edit_game_modal_logo": "Логотип",
|
||||
"edit_game_modal_select_logo": "Виберіть логотип",
|
||||
"edit_game_modal_logo_preview": "Попередній перегляд логотипу",
|
||||
"edit_game_modal_hero": "Зображення обкладинки гри",
|
||||
"edit_game_modal_select_hero": "Виберіть обкладинку гри",
|
||||
"edit_game_modal_hero_preview": "Попередній перегляд обкладинки гри",
|
||||
"edit_game_modal_cancel": "Скасувати",
|
||||
"edit_game_modal_update": "Оновити",
|
||||
"edit_game_modal_updating": "Оновлення...",
|
||||
"edit_game_modal_fill_required": "Будь ласка, заповніть всі обов'язкові поля",
|
||||
"edit_game_modal_success": "Ресурси успішно оновлено",
|
||||
"edit_game_modal_failed": "Не вдалося оновити ресурси",
|
||||
"edit_game_modal_image_filter": "Зображення",
|
||||
"edit_game_modal_icon_resolution": "Рекомендована роздільна здатність: 256x256px",
|
||||
"edit_game_modal_logo_resolution": "Рекомендована роздільна здатність: 640x360px",
|
||||
"edit_game_modal_hero_resolution": "Рекомендована роздільна здатність: 1920x620px",
|
||||
"edit_game_modal_assets": "Ресурси",
|
||||
"edit_game_modal_drop_icon_image_here": "Перетягніть зображення іконки сюди",
|
||||
"edit_game_modal_drop_logo_image_here": "Перетягніть зображення логотипу сюди",
|
||||
"edit_game_modal_drop_hero_image_here": "Перетягніть зображення обкладинки сюди",
|
||||
"edit_game_modal_drop_to_replace_icon": "Перетягніть для заміни іконки",
|
||||
"edit_game_modal_drop_to_replace_logo": "Перетягніть для заміни логотипу",
|
||||
"edit_game_modal_drop_to_replace_hero": "Перетягніть для заміни обкладинки",
|
||||
"install_decky_plugin": "Встановити плагін Decky",
|
||||
"update_decky_plugin": "Оновити плагін Decky",
|
||||
"decky_plugin_installed_version": "Плагін Decky (v{{version}})",
|
||||
"install_decky_plugin_title": "Встановити плагін Hydra Decky",
|
||||
"install_decky_plugin_message": "Це завантажить і встановить плагін Hydra для Decky Loader. Можуть знадобитися підвищені дозволи. Продовжити?",
|
||||
"update_decky_plugin_title": "Оновити плагін Hydra Decky",
|
||||
"update_decky_plugin_message": "Доступна нова версія плагіна Hydra Decky. Бажаєте оновити його зараз?",
|
||||
"decky_plugin_installed": "Плагін Decky v{{version}} успішно встановлено",
|
||||
"decky_plugin_installation_failed": "Не вдалося встановити плагін Decky: {{error}}",
|
||||
"decky_plugin_installation_error": "Помилка встановлення плагіна Decky: {{error}}",
|
||||
"confirm": "Підтвердити",
|
||||
"cancel": "Скасувати"
|
||||
"playable_button_title": "Показати лише ігри, які можна грати зараз"
|
||||
},
|
||||
"header": {
|
||||
"search": "Пошук",
|
||||
@@ -147,7 +87,6 @@
|
||||
"amount_minutes": "{{amount}} хвилин",
|
||||
"accuracy": "{{accuracy}}% точність",
|
||||
"add_to_library": "Додати до бібліотеки",
|
||||
"already_in_library": "Вже в бібліотеці",
|
||||
"remove_from_library": "Видалити з бібліотеки",
|
||||
"no_downloads": "Немає доступних завантажень",
|
||||
"play_time": "Час гри: {{amount}}",
|
||||
@@ -164,7 +103,6 @@
|
||||
"download_now": "Завантажити зараз",
|
||||
"calculating_eta": "Обчислення залишкового часу…",
|
||||
"create_shortcut": "Створити ярлик на робочому столі",
|
||||
"create_shortcut_simple": "Створити ярлик",
|
||||
"create_shortcut_success": "Ярлик успішно створено",
|
||||
"create_shortcut_error": "Виникла помилка під час створення ярлику",
|
||||
"nsfw_content_title": "Ця гра містить неприйнятний контент",
|
||||
@@ -198,7 +136,6 @@
|
||||
"open_folder": "Відкрити папку",
|
||||
"open_screenshot": "Відкрити скріншот",
|
||||
"options": "Налаштування",
|
||||
"properties": "Властивості",
|
||||
"paused": "Призупинено",
|
||||
"previous_screenshot": "Попередній скріншот",
|
||||
"remove_files": "Видалити файли",
|
||||
@@ -235,7 +172,7 @@
|
||||
"loading_save_preview": "Виконується пошук збережень гри...",
|
||||
"wine_prefix": "Префікс Wine",
|
||||
"wine_prefix_description": "Префікс Wine використовувався для запуску цієї гри",
|
||||
"launch_options": "Параметри завантаження",
|
||||
"launch_options": "Параметри загрузки",
|
||||
"launch_options_description": "Досвідчені користувачі можуть ввести власні модифікації до параметрів запуску (експериментальна функція).",
|
||||
"launch_options_placeholder": "Параметри не вказано",
|
||||
"no_download_option_info": "Немає інформації",
|
||||
@@ -262,105 +199,11 @@
|
||||
"download_error_not_cached_on_hydra": "Це завантаження недоступне через Nimbus.",
|
||||
"game_removed_from_favorites": "Гра видалена з улюбленних",
|
||||
"game_added_to_favorites": "Гра була добавлена у улюблені",
|
||||
"automatically_extract_downloaded_files": "Автоматично розархівувати завантаженні файли",
|
||||
"create_steam_shortcut": "Створити ярлик Steam",
|
||||
"you_might_need_to_restart_steam": "Можливо, вам знадобиться перезапустити Steam, щоб побачити зміни",
|
||||
"add_to_favorites": "Додати до улюбленого",
|
||||
"remove_from_favorites": "Видалити з улюбленого",
|
||||
"failed_update_favorites": "Не вдалося оновити улюблене",
|
||||
"game_removed_from_library": "Гру видалено з бібліотеки",
|
||||
"failed_remove_from_library": "Не вдалося видалити з бібліотеки",
|
||||
"files_removed_success": "Файли успішно видалено",
|
||||
"failed_remove_files": "Не вдалося видалити файли",
|
||||
"show_more": "Показати більше",
|
||||
"show_less": "Показати менше",
|
||||
"reviews": "Відгуки",
|
||||
"leave_a_review": "Залишити відгук",
|
||||
"write_review_placeholder": "Поділіться своїми думками про цю гру...",
|
||||
"sort_newest": "Спочатку нові",
|
||||
"no_reviews_yet": "Поки що немає відгуків",
|
||||
"be_first_to_review": "Станьте першим, хто поділиться своїми думками про цю гру!",
|
||||
"sort_oldest": "Спочатку старі",
|
||||
"sort_highest_score": "Найвища оцінка",
|
||||
"sort_lowest_score": "Найнижча оцінка",
|
||||
"sort_most_voted": "Найпопулярніші",
|
||||
"rating": "Оцінка",
|
||||
"rating_stats": "Оцінка",
|
||||
"rating_very_negative": "Дуже негативно",
|
||||
"rating_negative": "Негативно",
|
||||
"rating_neutral": "Нейтрально",
|
||||
"rating_positive": "Позитивно",
|
||||
"rating_very_positive": "Дуже позитивно",
|
||||
"submit_review": "Відправити",
|
||||
"submitting": "Відправка...",
|
||||
"review_submitted_successfully": "Відгук успішно відправлено!",
|
||||
"review_submission_failed": "Не вдалося відправити відгук. Будь ласка, спробуйте ще раз.",
|
||||
"review_cannot_be_empty": "Текстове поле відгуку не може бути порожнім.",
|
||||
"review_deleted_successfully": "Відгук успішно видалено.",
|
||||
"review_deletion_failed": "Не вдалося видалити відгук. Будь ласка, спробуйте ще раз.",
|
||||
"loading_reviews": "Завантаження відгуків...",
|
||||
"loading_more_reviews": "Завантаження додаткових відгуків...",
|
||||
"load_more_reviews": "Завантажити більше відгуків",
|
||||
"you_seemed_to_enjoy_this_game": "Схоже, вам сподобалася ця гра",
|
||||
"would_you_recommend_this_game": "Бажаєте залишити відгук про цю гру?",
|
||||
"yes": "Так",
|
||||
"maybe_later": "Можливо пізніше",
|
||||
"rating_count": "Оцінка",
|
||||
"delete_review": "Видалити відгук",
|
||||
"remove_review": "Видалити відгук",
|
||||
"delete_review_modal_title": "Ви впевнені, що хочете видалити свій відгук?",
|
||||
"delete_review_modal_description": "Цю дію не можна скасувати.",
|
||||
"delete_review_modal_delete_button": "Видалити",
|
||||
"delete_review_modal_cancel_button": "Скасувати",
|
||||
"backup_failed": "Помилка резервного копіювання",
|
||||
"update_playtime_title": "Оновити час гри",
|
||||
"update_playtime_description": "Вручну оновіть час гри для {{game}}",
|
||||
"update_playtime": "Оновити час гри",
|
||||
"update_playtime_success": "Час гри успішно оновлено",
|
||||
"update_playtime_error": "Не вдалося оновити час гри",
|
||||
"update_game_playtime": "Оновити час гри",
|
||||
"manual_playtime_warning": "Ваші години будуть позначені як оновлені вручну. Цю дію не можна скасувати.",
|
||||
"manual_playtime_tooltip": "Цей час гри було оновлено вручну",
|
||||
"game_removed_from_pinned": "Гру видалено із закріплених",
|
||||
"game_added_to_pinned": "Гру додано до закріплених",
|
||||
"create_start_menu_shortcut": "Створити ярлик у меню «Пуск»",
|
||||
"invalid_wine_prefix_path": "Недійсний шлях префікса Wine",
|
||||
"invalid_wine_prefix_path_description": "Шлях до префікса Wine недійсний. Будь ласка, перевірте шлях і спробуйте знову.",
|
||||
"missing_wine_prefix": "Префікс Wine необхідний для створення резервної копії в Linux",
|
||||
"artifact_renamed": "Резервну копію успішно перейменовано",
|
||||
"rename_artifact": "Перейменувати резервну копію",
|
||||
"rename_artifact_description": "Перейменуйте резервну копію, надавши їй більш описову назву.",
|
||||
"artifact_name_label": "Назва резервної копії",
|
||||
"artifact_name_placeholder": "Введіть назву для резервної копії",
|
||||
"save_changes": "Зберегти зміни",
|
||||
"required_field": "Це поле обов'язкове",
|
||||
"max_length_field": "Це поле має містити менше ніж {{length}} символів",
|
||||
"freeze_backup": "Закріпити, щоб вона не була перезаписана автоматичними резервними копіями",
|
||||
"unfreeze_backup": "Відкріпити",
|
||||
"backup_frozen": "Резервну копію закріплено",
|
||||
"backup_unfrozen": "Резервну копію відкріплено",
|
||||
"backup_freeze_failed": "Не вдалося закріпити резервну копію",
|
||||
"backup_freeze_failed_description": "Ви повинні залишити принаймні один вільний слот для автоматичних резервних копій",
|
||||
"edit_game_modal_button": "Змінити деталі гри",
|
||||
"game_details": "Деталі гри",
|
||||
"currency_symbol": "₴",
|
||||
"currency_country": "ua",
|
||||
"prices": "Ціни",
|
||||
"no_prices_found": "Ціни не знайдено",
|
||||
"view_all_prices": "Натисніть, щоб переглянути всі ціни",
|
||||
"retail_price": "Роздрібна ціна",
|
||||
"keyshop_price": "Ціна в магазині ключів",
|
||||
"historical_retail": "Історичні роздрібні ціни",
|
||||
"historical_keyshop": "Історичні ціни в магазинах ключів",
|
||||
"language": "Мова",
|
||||
"caption": "Субтитри",
|
||||
"audio": "Аудіо",
|
||||
"filter_by_source": "Фільтр за джерелом",
|
||||
"no_repacks_found": "Джерела для цієї гри не знайдено"
|
||||
"automatically_extract_downloaded_files": "Автоматично розархівувати завантаженні файли"
|
||||
},
|
||||
"activation": {
|
||||
"title": "Активувати Hydra",
|
||||
"installation_id": "ID встановлення:",
|
||||
"installation_id": "ID установки:",
|
||||
"enter_activation_code": "Введіть ваш активаційний код",
|
||||
"message": "Якщо ви не знаєте, де його запросити, то не повинні мати його.",
|
||||
"activate": "Активувати",
|
||||
@@ -384,7 +227,7 @@
|
||||
"install": "Встановити",
|
||||
"download_in_progress": "В процесі",
|
||||
"downloads_completed": "Завершено",
|
||||
"no_downloads_description": "Ви ще нічого не завантажили через Hydra, але ніколи не пізно почати",
|
||||
"no_downloads_description": "Ви ще нічого не завантажили через Hydra, але ніколи не пізно почати!",
|
||||
"no_downloads_title": "Тут так пусто...",
|
||||
"queued": "В черзі",
|
||||
"queued_downloads": "Завантаження в черзі",
|
||||
@@ -497,8 +340,6 @@
|
||||
"delete_theme_description": "Це видалить тему {{theme}}",
|
||||
"cancel": "Відмінити",
|
||||
"appearance": "Вигляд",
|
||||
"debrid": "Debrid",
|
||||
"debrid_description": "Сервіси Debrid - це преміум-завантажувачі без обмежень, які дозволяють швидко завантажувати файли з різних файлообмінників, обмежуючись лише швидкістю вашого інтернету.",
|
||||
"enable_torbox": "Включити TorBox",
|
||||
"torbox_description": "TorBox — це ваш преміум-сервіс для сідінгу, що конкурує навіть з найкращими серверами на ринку.",
|
||||
"torbox_account_linked": "TorBox акаунт прив'язано",
|
||||
@@ -517,25 +358,7 @@
|
||||
"install_common_redist": "Встановити",
|
||||
"installing_common_redist": "Встановлюється…",
|
||||
"show_download_speed_in_megabytes": "Показувати швидкість завантаження в мегабайтах на секунду",
|
||||
"extract_files_by_default": "Розпаковувати файли після завантаження",
|
||||
"enable_steam_achievements": "Увімкнути пошук досягнень Steam",
|
||||
"achievement_custom_notification_position": "Позиція сповіщень про досягнення",
|
||||
"top-left": "Верхній лівий кут",
|
||||
"top-center": "Верхній центр",
|
||||
"top-right": "Верхній правий кут",
|
||||
"bottom-left": "Нижній лівий кут",
|
||||
"bottom-center": "Нижній центр",
|
||||
"bottom-right": "Нижній правий кут",
|
||||
"enable_achievement_custom_notifications": "Увімкнути сповіщення про досягнення",
|
||||
"alignment": "Вирівнювання",
|
||||
"variation": "Варіація",
|
||||
"default": "За замовчуванням",
|
||||
"rare": "Рідкісне",
|
||||
"platinum": "Платиновий",
|
||||
"hidden": "Прихований",
|
||||
"test_notification": "Тестове сповіщення",
|
||||
"notification_preview": "Попередній перегляд сповіщення про досягнення",
|
||||
"enable_friend_start_game_notifications": "Коли друг починає грати в гру"
|
||||
"extract_files_by_default": "Розпаковувати файли після завантаження"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Завантаження завершено",
|
||||
@@ -550,10 +373,7 @@
|
||||
"new_friend_request_description": "Ви отримали новий запит на дружбу",
|
||||
"new_friend_request_title": "Новий запит на дружбу",
|
||||
"extraction_complete": "Витягування завершено",
|
||||
"game_extracted": "{{title}} успішно витягнуто",
|
||||
"friend_started_playing_game": "{{displayName}} почав грати в гру",
|
||||
"test_achievement_notification_title": "Це тестове сповіщення",
|
||||
"test_achievement_notification_description": "Досить круто, чи не так?"
|
||||
"game_extracted": "{{title}} успішно витягнуто"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "Відкрити Hydra",
|
||||
@@ -562,8 +382,7 @@
|
||||
"game_card": {
|
||||
"no_downloads": "Немає доступних завантажень",
|
||||
"available_one": "Доступний",
|
||||
"available_other": "Доступні",
|
||||
"calculating": "Обчислення"
|
||||
"available_other": "Доступні"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "Програми не встановлені",
|
||||
@@ -580,17 +399,11 @@
|
||||
"activity": "Остання активність",
|
||||
"amount_hours": "{{amount}} годин",
|
||||
"amount_minutes": "{{amount}} хвилин",
|
||||
"amount_hours_short": "{{amount}}год",
|
||||
"amount_minutes_short": "{{amount}}хв",
|
||||
"cancel": "Скасувати",
|
||||
"display_name": "Відображуване ім'я",
|
||||
"edit_profile": "Редагувати профіль",
|
||||
"last_time_played": "Остання гра {{period}}",
|
||||
"library": "Бібліотека",
|
||||
"pinned": "Закріплені",
|
||||
"achievements_earned": "Зароблені досягнення",
|
||||
"played_recently": "Недавно зіграні",
|
||||
"playtime": "Час гри",
|
||||
"no_recent_activity_description": "Ви давно не грали в ігри. Пора це змінити!",
|
||||
"no_recent_activity_title": "Хммм... Тут нічого немає",
|
||||
"playing_for": "Зіграно {{amount}}",
|
||||
@@ -602,10 +415,9 @@
|
||||
"sign_out_modal_title": "Ви впевнені?",
|
||||
"successfully_signed_out": "Успішний вихід з акаунту",
|
||||
"total_play_time": "Всього зіграно",
|
||||
"manual_playtime_tooltip": "Час гри було оновлено вручну",
|
||||
"try_again": "Будь ласка, попробуйте ще раз",
|
||||
"add_friends": "Додати друзів",
|
||||
"add": "Додати",
|
||||
"add_friends": "Добавити друзів",
|
||||
"add": "Добавити",
|
||||
"friend_code": "Код друга",
|
||||
"see_profile": "Переглянути профіль",
|
||||
"sending": "Надсилання",
|
||||
@@ -614,7 +426,7 @@
|
||||
"friends_list": "Список друзів",
|
||||
"user_not_found": "Користувача не найдено",
|
||||
"block_user": "Заблокувати користувача",
|
||||
"add_friend": "Додати друга",
|
||||
"add_friend": "Добавити друга",
|
||||
"request_sent": "надіслано запит на дружбу",
|
||||
"request_received": "Отримано запит на дружбу",
|
||||
"accept_request": "Прийняти запит",
|
||||
@@ -662,14 +474,7 @@
|
||||
"achievements_unlocked": "Досягнень розблоковано",
|
||||
"earned_points": "Отримано балів",
|
||||
"show_achievements_on_profile": "Покажіть свої досягнення у своєму профілі",
|
||||
"show_points_on_profile": "Покажіть ваші отриманні бали у своєму профілі",
|
||||
"error_adding_friend": "Не вдалося відправити запит на дружбу. Будь ласка, перевірте код друга",
|
||||
"friend_code_length_error": "Код друга має містити 8 символів",
|
||||
"game_removed_from_pinned": "Гру видалено із закріплених",
|
||||
"game_added_to_pinned": "Гру додано до закріплених",
|
||||
"karma": "Карма",
|
||||
"karma_count": "карма",
|
||||
"karma_description": "Зароблена позитивними оцінками на відгуках"
|
||||
"show_points_on_profile": "Покажіть ваші отриманні бали у своєму профілі"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "Досягнення розблоковано",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"successfully_signed_in": "Kirish muvaffaqiyatli amalga oshirildi"
|
||||
},
|
||||
"home": {
|
||||
"featured": "Tavsiya etilgan",
|
||||
"surprise_me": "Hayratda qoldir",
|
||||
"no_results": "Natijalar topilmadi",
|
||||
"hot": "Eng mashhur",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"language_name": "简体中文",
|
||||
"language_name": "中文",
|
||||
"app": {
|
||||
"successfully_signed_in": "已成功登录"
|
||||
},
|
||||
"home": {
|
||||
"featured": "特色推荐",
|
||||
"surprise_me": "向我推荐",
|
||||
"no_results": "没有找到结果",
|
||||
"start_typing": "键入以开始搜素...",
|
||||
@@ -25,9 +26,7 @@
|
||||
"game_has_no_executable": "未选择游戏的可执行文件",
|
||||
"sign_in": "登入",
|
||||
"friends": "好友",
|
||||
"favorites": "收藏",
|
||||
"need_help": "需要帮助?",
|
||||
"playable_button_title": "仅显示现在可以游玩的游戏"
|
||||
"favorites": "收藏"
|
||||
},
|
||||
"header": {
|
||||
"search": "搜索游戏",
|
||||
@@ -44,21 +43,11 @@
|
||||
"downloading_metadata": "正在下载{{title}}的元数据…",
|
||||
"downloading": "正在下载{{title}}… ({{percentage}}完成) - 剩余时间{{eta}} - 速度{{speed}}",
|
||||
"calculating_eta": "正在下载 {{title}}… (已完成{{percentage}}.) - 正在计算剩余时间...",
|
||||
"checking_files": "正在校验 {{title}} 的文件... ({{percentage}} 已完成)",
|
||||
"installation_complete": "安装完成",
|
||||
"installation_complete_message": "通用可再发行组件安装成功",
|
||||
"installing_common_redist": "{{log}}…"
|
||||
"checking_files": "正在校验 {{title}} 的文件... ({{percentage}} 已完成)"
|
||||
},
|
||||
"catalogue": {
|
||||
"clear_filters": "清除已选的 {{filterCount}} 项",
|
||||
"developers": "开发商",
|
||||
"download_sources": "下载源",
|
||||
"filter_count": "{{filterCount}} 项可用",
|
||||
"genres": "类型",
|
||||
"publishers": "发行商",
|
||||
"result_count": "{{resultCount}} 个结果",
|
||||
"search": "筛选…",
|
||||
"tags": "标签"
|
||||
"next_page": "下一页",
|
||||
"previous_page": "上一页"
|
||||
},
|
||||
"game_details": {
|
||||
"open_download_options": "打开下载菜单",
|
||||
@@ -177,48 +166,7 @@
|
||||
"manage_files_description": "管理哪些文件要备份和恢复",
|
||||
"select_folder": "选择文件夹",
|
||||
"backup_from": "{{date}} 时备份",
|
||||
"custom_backup_location_set": "自定义备份文件位置",
|
||||
"artifact_name_label": "备份名称",
|
||||
"artifact_name_placeholder": "为备份输入名称",
|
||||
"artifact_renamed": "备份重命名成功",
|
||||
"automatic_backup_from": "{{date}} 的自动备份",
|
||||
"automatically_extract_downloaded_files": "自动解压下载的文件",
|
||||
"backup_freeze_failed": "固定备份失败",
|
||||
"backup_freeze_failed_description": "您必须至少保留一个空位用于自动备份",
|
||||
"backup_frozen": "备份已固定",
|
||||
"backup_unfrozen": "备份已取消固定",
|
||||
"clear": "清除",
|
||||
"create_start_menu_shortcut": "创建开始菜单快捷方式",
|
||||
"create_steam_shortcut": "创建Steam快捷方式",
|
||||
"download_error_gofile_quota_exceeded": "您已超出Gofile的月度配额。请等待配额重置。",
|
||||
"download_error_not_cached_on_hydra": "此下载在Nimbus上不可用。",
|
||||
"download_error_not_cached_on_real_debrid": "此下载在Real-Debrid上不可用,且暂不支持从Real-Debrid轮询下载状态。",
|
||||
"download_error_not_cached_on_torbox": "此下载在TorBox上不可用,且暂不支持从TorBox轮询下载状态。",
|
||||
"download_error_real_debrid_account_not_authorized": "您的Real-Debrid账户未被授权进行新下载。请检查您的账户设置并重试。",
|
||||
"enable_automatic_cloud_sync": "启用自动云同步",
|
||||
"freeze_backup": "固定以免被自动备份覆盖",
|
||||
"game_added_to_favorites": "游戏已添加到收藏",
|
||||
"game_removed_from_favorites": "游戏已从收藏中移除",
|
||||
"invalid_wine_prefix_path": "无效的Wine前置路径",
|
||||
"invalid_wine_prefix_path_description": "Wine前置的路径无效。请检查路径并重试。",
|
||||
"launch_options": "启动选项",
|
||||
"launch_options_description": "高级用户可以选择修改启动选项(实验性功能)",
|
||||
"launch_options_placeholder": "未指定参数",
|
||||
"max_length_field": "此字段必须少于 {{length}} 个字符",
|
||||
"missing_wine_prefix": "在Linux上创建备份需要Wine前置",
|
||||
"no_directory_selected": "未选择目录",
|
||||
"no_write_permission": "无法下载到此目录。点击此处了解更多。",
|
||||
"rename_artifact": "重命名备份",
|
||||
"rename_artifact_description": "将备份重命名为更具描述性的名称",
|
||||
"required_field": "此字段为必填项",
|
||||
"reset_achievements": "重置成就",
|
||||
"reset_achievements_description": "这将重置 {{game}} 的所有成就",
|
||||
"reset_achievements_error": "重置成就失败",
|
||||
"reset_achievements_success": "成就重置成功",
|
||||
"reset_achievements_title": "您确定吗?",
|
||||
"save_changes": "保存更改",
|
||||
"unfreeze_backup": "取消固定",
|
||||
"you_might_need_to_restart_steam": "您可能需要重启Steam才能看到更改"
|
||||
"custom_backup_location_set": "自定义备份文件位置"
|
||||
},
|
||||
"activation": {
|
||||
"title": "激活 Hydra",
|
||||
@@ -251,13 +199,7 @@
|
||||
"queued": "下载列表",
|
||||
"no_downloads_title": "空空如也",
|
||||
"no_downloads_description": "你还未使用Hydra下载任何游戏,但什么时候开始,都为时不晚。",
|
||||
"checking_files": "正在校验文件…",
|
||||
"extract": "解压文件",
|
||||
"extracting": "正在解压文件…",
|
||||
"options": "管理",
|
||||
"resume_seeding": "恢复做种",
|
||||
"seeding": "做种中",
|
||||
"stop_seeding": "停止做种"
|
||||
"checking_files": "正在校验文件…"
|
||||
},
|
||||
"settings": {
|
||||
"downloads_path": "下载路径",
|
||||
@@ -318,83 +260,7 @@
|
||||
"must_be_valid_url": "来源必须是有效的 URL",
|
||||
"blocked_users": "已屏蔽用户",
|
||||
"user_unblocked": "用户已经被屏蔽",
|
||||
"enable_achievement_notifications": "当成就解锁时",
|
||||
"account": "账户",
|
||||
"account_data_updated_successfully": "账户数据更新成功",
|
||||
"achievement_custom_notification_position": "成就自定义通知位置",
|
||||
"alignment": "对齐",
|
||||
"appearance": "外观",
|
||||
"become_subscriber": "成为Hydra Cloud用户",
|
||||
"bill_sent_until": "您的下一张账单将在此日期前发送",
|
||||
"bottom-center": "底部中央",
|
||||
"bottom-left": "底部左侧",
|
||||
"bottom-right": "底部右侧",
|
||||
"cancel": "取消",
|
||||
"clear_themes": "清除",
|
||||
"common_redist": "通用可再发行组件",
|
||||
"common_redist_description": "运行某些游戏需要通用可再发行组件。建议安装以避免问题。",
|
||||
"create_real_debrid_account": "如果您还没有Real-Debrid账户,请点击此处",
|
||||
"create_theme": "创建",
|
||||
"create_theme_modal_description": "创建新主题以自定义Hydra的外观",
|
||||
"create_theme_modal_title": "创建自定义主题",
|
||||
"create_torbox_account": "如果您还没有TorBox账户,请点击此处",
|
||||
"current_email": "当前邮箱:",
|
||||
"default": "默认",
|
||||
"delete_all_themes": "删除所有主题",
|
||||
"delete_all_themes_description": "这将删除所有您的自定义主题",
|
||||
"delete_theme": "删除主题",
|
||||
"delete_theme_description": "这将删除主题 {{theme}}",
|
||||
"disable_nsfw_alert": "禁用NSFW警告",
|
||||
"edit_theme": "编辑主题",
|
||||
"editor_tab_code": "代码",
|
||||
"editor_tab_info": "信息",
|
||||
"editor_tab_save": "保存",
|
||||
"enable_achievement_custom_notifications": "启用成就自定义通知",
|
||||
"enable_auto_install": "自动下载更新",
|
||||
"enable_friend_request_notifications": "当收到好友请求时",
|
||||
"enable_friend_start_game_notifications": "当好友开始游戏时",
|
||||
"enable_torbox": "启用TorBox",
|
||||
"error_importing_theme": "导入主题时出错",
|
||||
"extract_files_by_default": "下载后默认解压文件",
|
||||
"hidden": "隐藏",
|
||||
"import_theme": "导入主题",
|
||||
"import_theme_description": "您将从主题商店导入 {{theme}}",
|
||||
"insert_theme_name": "输入主题名称",
|
||||
"install_common_redist": "安装",
|
||||
"installing_common_redist": "正在安装…",
|
||||
"launch_minimized": "最小化启动Hydra",
|
||||
"manage_subscription": "管理订阅",
|
||||
"name_min_length": "主题名称必须至少3个字符长",
|
||||
"no_email_account": "您尚未设置邮箱",
|
||||
"no_subscription": "以最佳方式享受Hydra",
|
||||
"no_themes": "看起来您还没有任何主题,但别担心,点击这里创建您的第一个杰作。",
|
||||
"no_users_blocked": "您没有屏蔽任何用户",
|
||||
"notification_preview": "成就通知预览",
|
||||
"platinum": "白金",
|
||||
"rare": "稀有",
|
||||
"real_debrid_account_linked": "Real-Debrid账户已连接",
|
||||
"renew_subscription": "续费Hydra Cloud",
|
||||
"seed_after_download_complete": "下载完成后做种",
|
||||
"set_theme": "设置主题",
|
||||
"show_download_speed_in_megabytes": "以兆字节每秒显示下载速度",
|
||||
"show_hidden_achievement_description": "在解锁前显示隐藏成就描述",
|
||||
"subscription_active_until": "您的Hydra Cloud活跃至 {{date}}",
|
||||
"subscription_expired_at": "您的订阅已于 {{date}} 到期",
|
||||
"subscription_renew_cancelled": "自动续费已禁用",
|
||||
"subscription_renews_on": "您的订阅将于 {{date}} 续费",
|
||||
"test_notification": "测试通知",
|
||||
"theme_imported": "主题导入成功",
|
||||
"theme_name": "名称",
|
||||
"top-center": "顶部中央",
|
||||
"top-left": "顶部左侧",
|
||||
"top-right": "顶部右侧",
|
||||
"torbox_account_linked": "TorBox账户已连接",
|
||||
"torbox_description": "TorBox是您的高级种子盒服务,甚至可与市场上最好的服务器相媲美。",
|
||||
"unset_theme": "取消设置主题",
|
||||
"update_email": "更新邮箱",
|
||||
"update_password": "更新密码",
|
||||
"variation": "变体",
|
||||
"web_store": "网络商店"
|
||||
"enable_achievement_notifications": "当成就解锁时"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "下载完成",
|
||||
@@ -405,23 +271,14 @@
|
||||
"new_update_available": "版本 {{version}} 可用",
|
||||
"restart_to_install_update": "重启 Hydra 以安装更新",
|
||||
"notification_achievement_unlocked_title": "{{game}} 的成绩已解锁",
|
||||
"notification_achievement_unlocked_body": "{{achievement}} 和其他 {{count}} 已解锁",
|
||||
"extraction_complete": "解压完成",
|
||||
"friend_started_playing_game": "{{displayName}} 开始玩游戏",
|
||||
"game_extracted": "{{title}} 解压成功",
|
||||
"new_friend_request_description": "{{displayName}} 向您发送了好友请求",
|
||||
"new_friend_request_title": "新好友请求",
|
||||
"test_achievement_notification_description": "非常酷,对吧?",
|
||||
"test_achievement_notification_title": "这是一个测试通知"
|
||||
"notification_achievement_unlocked_body": "{{achievement}} 和其他 {{count}} 已解锁"
|
||||
},
|
||||
"system_tray": {
|
||||
"open": "打开 Hydra",
|
||||
"quit": "退出"
|
||||
},
|
||||
"game_card": {
|
||||
"no_downloads": "无可用下载选项",
|
||||
"available_one": "可用",
|
||||
"available_other": "可用"
|
||||
"no_downloads": "无可用下载选项"
|
||||
},
|
||||
"binary_not_found_modal": {
|
||||
"title": "程序未安装",
|
||||
@@ -494,7 +351,7 @@
|
||||
"report_description": "额外信息",
|
||||
"report_description_placeholder": "额外信息",
|
||||
"report": "举报",
|
||||
"report_reason_hate": "仇恨言论",
|
||||
"report_reason_hate": "Hate speech",
|
||||
"report_reason_sexual_content": "色情内容",
|
||||
"report_reason_violence": "暴力",
|
||||
"report_reason_spam": "骚扰",
|
||||
@@ -503,19 +360,7 @@
|
||||
"your_friend_code": "你的好友代码:",
|
||||
"upload_banner": "上传横幅",
|
||||
"uploading_banner": "上传横幅中…",
|
||||
"background_image_updated": "背景图片已更新",
|
||||
"achievements": "成就",
|
||||
"achievements_unlocked": "成就已解锁",
|
||||
"earned_points": "获得积分",
|
||||
"error_adding_friend": "无法发送好友请求。请检查好友代码",
|
||||
"friend_code_length_error": "好友代码必须为8个字符",
|
||||
"games": "游戏",
|
||||
"playing": "正在玩 {{game}}",
|
||||
"ranking_updated_weekly": "排名每周更新",
|
||||
"show_achievements_on_profile": "在您的个人资料上显示成就",
|
||||
"show_points_on_profile": "在您的个人资料上显示获得的积分",
|
||||
"stats": "统计",
|
||||
"top_percentile": "前 {{percentile}}%"
|
||||
"background_image_updated": "背景图片已更新"
|
||||
},
|
||||
"achievement": {
|
||||
"achievement_unlocked": "成就已解锁",
|
||||
@@ -523,14 +368,7 @@
|
||||
"your_achievements": "你的成就",
|
||||
"unlocked_at": "解锁于: {{date}}",
|
||||
"subscription_needed": "需要订阅 Hydra Cloud 才能看到此内容",
|
||||
"new_achievements_unlocked": "从 {{gameCount}} 游戏中解锁 {{achievementCount}} 新成就",
|
||||
"achievement_earn_points": "通过此成就获得 {{points}} 积分",
|
||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} 成就",
|
||||
"achievements_unlocked_for_game": "为 {{gameTitle}} 解锁了 {{achievementCount}} 个新成就",
|
||||
"available_points": "可用积分:",
|
||||
"earned_points": "获得积分:",
|
||||
"hidden_achievement_tooltip": "这是一个隐藏成就",
|
||||
"how_to_earn_achievements_points": "如何获得成就积分?"
|
||||
"new_achievements_unlocked": "从 {{gameCount}} 游戏中解锁 {{achievementCount}} 新成就"
|
||||
},
|
||||
"hydra_cloud": {
|
||||
"subscription_tour_title": "Hydra 云订阅",
|
||||
@@ -540,10 +378,6 @@
|
||||
"animated_profile_picture": "动画头像",
|
||||
"premium_support": "高级技术支持",
|
||||
"show_and_compare_achievements": "展示并与其他用户比较您的成就",
|
||||
"animated_profile_banner": "动态个人简介横幅",
|
||||
"debrid_description": "使用Nimbus下载速度提升4倍",
|
||||
"hydra_cloud": "Hydra Cloud",
|
||||
"hydra_cloud_feature_found": "您刚刚发现了一个Hydra Cloud功能!",
|
||||
"learn_more": "了解更多"
|
||||
"animated_profile_banner": "动态个人简介横幅"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,15 +41,4 @@ export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");
|
||||
|
||||
export const ASSETS_PATH = path.join(SystemPath.getPath("userData"), "Assets");
|
||||
|
||||
export const MAIN_LOOP_INTERVAL = 2000;
|
||||
|
||||
export const DECKY_PLUGINS_LOCATION = path.join(
|
||||
SystemPath.getPath("home"),
|
||||
"homebrew",
|
||||
"plugins"
|
||||
);
|
||||
|
||||
export const HYDRA_DECKY_PLUGIN_LOCATION = path.join(
|
||||
DECKY_PLUGINS_LOCATION,
|
||||
"Hydra"
|
||||
);
|
||||
export const MAIN_LOOP_INTERVAL = 1500;
|
||||
|
||||
22
src/main/events/catalogue/get-catalogue.ts
Normal file
22
src/main/events/catalogue/get-catalogue.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { CatalogueCategory } from "@shared";
|
||||
import { ShopAssets } from "@types";
|
||||
|
||||
const getCatalogue = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
category: CatalogueCategory
|
||||
) => {
|
||||
const params = new URLSearchParams({
|
||||
take: "12",
|
||||
skip: "0",
|
||||
});
|
||||
|
||||
return HydraApi.get<ShopAssets[]>(
|
||||
`/catalogue/${category}?${params.toString()}`,
|
||||
{},
|
||||
{ needsAuth: false }
|
||||
);
|
||||
};
|
||||
|
||||
registerEvent("getCatalogue", getCatalogue);
|
||||
10
src/main/events/catalogue/get-developers.ts
Normal file
10
src/main/events/catalogue/get-developers.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const getDevelopers = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
return HydraApi.get<string[]>(`/catalogue/developers`, null, {
|
||||
needsAuth: false,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("getDevelopers", getDevelopers);
|
||||
@@ -1,51 +0,0 @@
|
||||
import type { GameShop, ShopAssets } from "@types";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
||||
|
||||
const LOCAL_CACHE_EXPIRATION = 1000 * 60 * 60 * 8; // 8 hours
|
||||
|
||||
export const getGameAssets = async (objectId: string, shop: GameShop) => {
|
||||
const cachedAssets = await gamesShopAssetsSublevel.get(
|
||||
levelKeys.game(shop, objectId)
|
||||
);
|
||||
|
||||
if (
|
||||
cachedAssets &&
|
||||
cachedAssets.updatedAt + LOCAL_CACHE_EXPIRATION > Date.now()
|
||||
) {
|
||||
return cachedAssets;
|
||||
}
|
||||
|
||||
return HydraApi.get<ShopAssets | null>(
|
||||
`/games/${shop}/${objectId}/assets`,
|
||||
null,
|
||||
{
|
||||
needsAuth: false,
|
||||
}
|
||||
).then(async (assets) => {
|
||||
if (!assets) return null;
|
||||
|
||||
// Preserve existing title if it differs from the incoming title (indicating it was customized)
|
||||
const shouldPreserveTitle =
|
||||
cachedAssets?.title && cachedAssets.title !== assets.title;
|
||||
|
||||
await gamesShopAssetsSublevel.put(levelKeys.game(shop, objectId), {
|
||||
...assets,
|
||||
title: shouldPreserveTitle ? cachedAssets.title : assets.title,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
|
||||
return assets;
|
||||
});
|
||||
};
|
||||
|
||||
const getGameAssetsEvent = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => {
|
||||
return getGameAssets(objectId, shop);
|
||||
};
|
||||
|
||||
registerEvent("getGameAssets", getGameAssetsEvent);
|
||||
@@ -1,36 +1,17 @@
|
||||
import type { GameShop, GameStats } from "@types";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { gamesStatsCacheSublevel, levelKeys } from "@main/level";
|
||||
|
||||
const LOCAL_CACHE_EXPIRATION = 1000 * 60 * 30; // 30 minutes
|
||||
|
||||
const getGameStats = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => {
|
||||
const cachedStats = await gamesStatsCacheSublevel.get(
|
||||
levelKeys.game(shop, objectId)
|
||||
return HydraApi.get<GameStats>(
|
||||
`/games/stats`,
|
||||
{ objectId, shop },
|
||||
{ needsAuth: false }
|
||||
);
|
||||
|
||||
if (
|
||||
cachedStats &&
|
||||
cachedStats.updatedAt + LOCAL_CACHE_EXPIRATION > Date.now()
|
||||
) {
|
||||
return cachedStats;
|
||||
}
|
||||
|
||||
return HydraApi.get<GameStats>(`/games/${shop}/${objectId}/stats`, null, {
|
||||
needsAuth: false,
|
||||
}).then(async (data) => {
|
||||
await gamesStatsCacheSublevel.put(levelKeys.game(shop, objectId), {
|
||||
...data,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
|
||||
return data;
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("getGameStats", getGameStats);
|
||||
|
||||
21
src/main/events/catalogue/get-how-long-to-beat.ts
Normal file
21
src/main/events/catalogue/get-how-long-to-beat.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { GameShop, HowLongToBeatCategory } from "@types";
|
||||
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const getHowLongToBeat = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
): Promise<HowLongToBeatCategory[] | null> => {
|
||||
const params = new URLSearchParams({
|
||||
objectId,
|
||||
shop,
|
||||
});
|
||||
|
||||
return HydraApi.get(`/games/how-long-to-beat?${params.toString()}`, null, {
|
||||
needsAuth: false,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("getHowLongToBeat", getHowLongToBeat);
|
||||
10
src/main/events/catalogue/get-publishers.ts
Normal file
10
src/main/events/catalogue/get-publishers.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const getPublishers = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
return HydraApi.get<string[]>(`/catalogue/publishers`, null, {
|
||||
needsAuth: false,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("getPublishers", getPublishers);
|
||||
22
src/main/events/catalogue/get-trending-games.ts
Normal file
22
src/main/events/catalogue/get-trending-games.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { db, levelKeys } from "@main/level";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { TrendingGame } from "@types";
|
||||
|
||||
const getTrendingGames = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const language = await db
|
||||
.get<string, string>(levelKeys.language, {
|
||||
valueEncoding: "utf8",
|
||||
})
|
||||
.then((language) => language || "en");
|
||||
|
||||
const trendingGames = await HydraApi.get<TrendingGame[]>(
|
||||
"/games/featured",
|
||||
{ language },
|
||||
{ needsAuth: false }
|
||||
).catch(() => []);
|
||||
|
||||
return trendingGames.slice(0, 1);
|
||||
};
|
||||
|
||||
registerEvent("getTrendingGames", getTrendingGames);
|
||||
14
src/main/events/catalogue/save-game-shop-assets.ts
Normal file
14
src/main/events/catalogue/save-game-shop-assets.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { GameShop, ShopAssets } from "@types";
|
||||
import { gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const saveGameShopAssets = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
assets: ShopAssets
|
||||
): Promise<void> => {
|
||||
return gamesShopAssetsSublevel.put(levelKeys.game(shop, objectId), assets);
|
||||
};
|
||||
|
||||
registerEvent("saveGameShopAssets", saveGameShopAssets);
|
||||
18
src/main/events/catalogue/search-games.ts
Normal file
18
src/main/events/catalogue/search-games.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { CatalogueSearchPayload } from "@types";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const searchGames = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
payload: CatalogueSearchPayload,
|
||||
take: number,
|
||||
skip: number
|
||||
) => {
|
||||
return HydraApi.post(
|
||||
"/catalogue/search",
|
||||
{ ...payload, take, skip },
|
||||
{ needsAuth: false }
|
||||
);
|
||||
};
|
||||
|
||||
registerEvent("searchGames", searchGames);
|
||||
12
src/main/events/cloud-save/delete-game-artifact.ts
Normal file
12
src/main/events/cloud-save/delete-game-artifact.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const deleteGameArtifact = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
gameArtifactId: string
|
||||
) =>
|
||||
HydraApi.delete<{ ok: boolean }>(
|
||||
`/profile/games/artifacts/${gameArtifactId}`
|
||||
);
|
||||
|
||||
registerEvent("deleteGameArtifact", deleteGameArtifact);
|
||||
33
src/main/events/cloud-save/get-game-artifacts.ts
Normal file
33
src/main/events/cloud-save/get-game-artifacts.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
import type { GameArtifact, GameShop } from "@types";
|
||||
import { SubscriptionRequiredError, UserNotLoggedInError } from "@shared";
|
||||
|
||||
const getGameArtifacts = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
) => {
|
||||
const params = new URLSearchParams({
|
||||
objectId,
|
||||
shop,
|
||||
});
|
||||
|
||||
return HydraApi.get<GameArtifact[]>(
|
||||
`/profile/games/artifacts?${params.toString()}`,
|
||||
{},
|
||||
{ needsSubscription: true }
|
||||
).catch((err) => {
|
||||
if (err instanceof SubscriptionRequiredError) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (err instanceof UserNotLoggedInError) {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("getGameArtifacts", getGameArtifacts);
|
||||
14
src/main/events/cloud-save/rename-game-artifact.ts
Normal file
14
src/main/events/cloud-save/rename-game-artifact.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const renameGameArtifact = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
gameArtifactId: string,
|
||||
label: string
|
||||
) => {
|
||||
await HydraApi.put(`/profile/games/artifacts/${gameArtifactId}`, {
|
||||
label,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("renameGameArtifact", renameGameArtifact);
|
||||
16
src/main/events/cloud-save/toggle-artifact-freeze.ts
Normal file
16
src/main/events/cloud-save/toggle-artifact-freeze.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const toggleArtifactFreeze = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
gameArtifactId: string,
|
||||
freeze: boolean
|
||||
) => {
|
||||
if (freeze) {
|
||||
await HydraApi.put(`/profile/games/artifacts/${gameArtifactId}/freeze`);
|
||||
} else {
|
||||
await HydraApi.put(`/profile/games/artifacts/${gameArtifactId}/unfreeze`);
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("toggleArtifactFreeze", toggleArtifactFreeze);
|
||||
@@ -1,76 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { downloadSourcesSublevel, repacksSublevel } from "@main/level";
|
||||
import { HydraApi, logger } from "@main/services";
|
||||
import { importDownloadSourceToLocal } from "./helpers";
|
||||
|
||||
const addDownloadSource = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
url: string
|
||||
) => {
|
||||
const result = await importDownloadSourceToLocal(url, true);
|
||||
if (!result) {
|
||||
throw new Error("Failed to import download source");
|
||||
}
|
||||
|
||||
// Verify that repacks were actually written to the database (read-after-write)
|
||||
// This ensures all async operations are complete before proceeding
|
||||
let repackCount = 0;
|
||||
for await (const [, repack] of repacksSublevel.iterator()) {
|
||||
if (repack.downloadSourceId === result.id) {
|
||||
repackCount++;
|
||||
}
|
||||
}
|
||||
|
||||
await HydraApi.post("/profile/download-sources", {
|
||||
urls: [url],
|
||||
});
|
||||
|
||||
const { fingerprint } = await HydraApi.put<{ fingerprint: string }>(
|
||||
"/download-sources",
|
||||
{
|
||||
objectIds: result.objectIds,
|
||||
},
|
||||
{ needsAuth: false }
|
||||
);
|
||||
|
||||
// Update the source with fingerprint
|
||||
const updatedSource = await downloadSourcesSublevel.get(`${result.id}`);
|
||||
if (updatedSource) {
|
||||
await downloadSourcesSublevel.put(`${result.id}`, {
|
||||
...updatedSource,
|
||||
fingerprint,
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
// Final verification: ensure the source with fingerprint is persisted
|
||||
const finalSource = await downloadSourcesSublevel.get(`${result.id}`);
|
||||
if (!finalSource || !finalSource.fingerprint) {
|
||||
throw new Error("Failed to persist download source with fingerprint");
|
||||
}
|
||||
|
||||
// Verify repacks still exist after fingerprint update
|
||||
let finalRepackCount = 0;
|
||||
for await (const [, repack] of repacksSublevel.iterator()) {
|
||||
if (repack.downloadSourceId === result.id) {
|
||||
finalRepackCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (finalRepackCount !== repackCount) {
|
||||
logger.warn(
|
||||
`Repack count mismatch! Before: ${repackCount}, After: ${finalRepackCount}`
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
`Final verification passed: ${finalRepackCount} repacks confirmed`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...result,
|
||||
fingerprint,
|
||||
};
|
||||
};
|
||||
|
||||
registerEvent("addDownloadSource", addDownloadSource);
|
||||
@@ -1,17 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { downloadSourcesSublevel } from "@main/level";
|
||||
|
||||
const checkDownloadSourceExists = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
url: string
|
||||
): Promise<boolean> => {
|
||||
for await (const [, source] of downloadSourcesSublevel.iterator()) {
|
||||
if (source.url === url) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
registerEvent("checkDownloadSourceExists", checkDownloadSourceExists);
|
||||
13
src/main/events/download-sources/create-download-sources.ts
Normal file
13
src/main/events/download-sources/create-download-sources.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const createDownloadSources = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
urls: string[]
|
||||
) => {
|
||||
await HydraApi.post("/profile/download-sources", {
|
||||
urls,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("createDownloadSources", createDownloadSources);
|
||||
@@ -1,13 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { downloadSourcesSublevel, repacksSublevel } from "@main/level";
|
||||
import { invalidateIdCaches } from "./helpers";
|
||||
|
||||
const deleteAllDownloadSources = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
) => {
|
||||
await Promise.all([repacksSublevel.clear(), downloadSourcesSublevel.clear()]);
|
||||
|
||||
invalidateIdCaches();
|
||||
};
|
||||
|
||||
registerEvent("deleteAllDownloadSources", deleteAllDownloadSources);
|
||||
@@ -1,28 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { downloadSourcesSublevel, repacksSublevel } from "@main/level";
|
||||
import { invalidateIdCaches } from "./helpers";
|
||||
|
||||
const deleteDownloadSource = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
id: number
|
||||
) => {
|
||||
const repacksToDelete: string[] = [];
|
||||
|
||||
for await (const [key, repack] of repacksSublevel.iterator()) {
|
||||
if (repack.downloadSourceId === id) {
|
||||
repacksToDelete.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
const batch = repacksSublevel.batch();
|
||||
for (const key of repacksToDelete) {
|
||||
batch.del(key);
|
||||
}
|
||||
await batch.write();
|
||||
|
||||
await downloadSourcesSublevel.del(`${id}`);
|
||||
|
||||
invalidateIdCaches();
|
||||
};
|
||||
|
||||
registerEvent("deleteDownloadSource", deleteDownloadSource);
|
||||
@@ -1,19 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { downloadSourcesSublevel, DownloadSource } from "@main/level";
|
||||
|
||||
const getDownloadSourcesList = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const sources: DownloadSource[] = [];
|
||||
|
||||
for await (const [, source] of downloadSourcesSublevel.iterator()) {
|
||||
sources.push(source);
|
||||
}
|
||||
|
||||
// Sort by createdAt descending
|
||||
sources.sort(
|
||||
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
||||
);
|
||||
|
||||
return sources;
|
||||
};
|
||||
|
||||
registerEvent("getDownloadSourcesList", getDownloadSourcesList);
|
||||
@@ -1,367 +0,0 @@
|
||||
import axios from "axios";
|
||||
import { z } from "zod";
|
||||
import { downloadSourcesSublevel, repacksSublevel } from "@main/level";
|
||||
import { DownloadSourceStatus } from "@shared";
|
||||
import crypto from "node:crypto";
|
||||
import { logger, ResourceCache } from "@main/services";
|
||||
|
||||
export const downloadSourceSchema = z.object({
|
||||
name: z.string().max(255),
|
||||
downloads: z.array(
|
||||
z.object({
|
||||
title: z.string().max(255),
|
||||
uris: z.array(z.string()),
|
||||
uploadDate: z.string().max(255),
|
||||
fileSize: z.string().max(255),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export type TitleHashMapping = Record<string, number[]>;
|
||||
|
||||
let titleHashMappingCache: TitleHashMapping | null = null;
|
||||
|
||||
export const getTitleHashMapping = async (): Promise<TitleHashMapping> => {
|
||||
if (titleHashMappingCache) {
|
||||
return titleHashMappingCache;
|
||||
}
|
||||
|
||||
try {
|
||||
const cached =
|
||||
ResourceCache.getCachedData<TitleHashMapping>("sources-manifest");
|
||||
if (cached) {
|
||||
titleHashMappingCache = cached;
|
||||
return cached;
|
||||
}
|
||||
|
||||
const fetched = await ResourceCache.fetchAndCache<TitleHashMapping>(
|
||||
"sources-manifest",
|
||||
"https://cdn.losbroxas.org/sources-manifest.json",
|
||||
10000
|
||||
);
|
||||
titleHashMappingCache = fetched;
|
||||
return fetched;
|
||||
} catch (error) {
|
||||
logger.error("Failed to fetch title hash mapping:", error);
|
||||
return {} as TitleHashMapping;
|
||||
}
|
||||
};
|
||||
|
||||
export const hashTitle = (title: string): string => {
|
||||
return crypto.createHash("sha256").update(title).digest("hex");
|
||||
};
|
||||
|
||||
export type SteamGamesByLetter = Record<string, { id: string; name: string }[]>;
|
||||
export type FormattedSteamGame = {
|
||||
id: string;
|
||||
name: string;
|
||||
formattedName: string;
|
||||
};
|
||||
export type FormattedSteamGamesByLetter = Record<string, FormattedSteamGame[]>;
|
||||
|
||||
export const formatName = (name: string) => {
|
||||
return name
|
||||
.normalize("NFD")
|
||||
.replaceAll(/[\u0300-\u036f]/g, "")
|
||||
.toLowerCase()
|
||||
.replaceAll(/[^a-z0-9]/g, "");
|
||||
};
|
||||
|
||||
export const formatRepackName = (name: string) => {
|
||||
return formatName(name.replace("[DL]", ""));
|
||||
};
|
||||
|
||||
interface DownloadSource {
|
||||
id: number;
|
||||
url: string;
|
||||
name: string;
|
||||
etag: string | null;
|
||||
status: number;
|
||||
downloadCount: number;
|
||||
objectIds: string[];
|
||||
fingerprint?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
const getDownloadSourcesMap = async (): Promise<
|
||||
Map<string, DownloadSource>
|
||||
> => {
|
||||
const map = new Map();
|
||||
for await (const [key, source] of downloadSourcesSublevel.iterator()) {
|
||||
map.set(key, source);
|
||||
}
|
||||
|
||||
return map;
|
||||
};
|
||||
|
||||
export const checkUrlExists = async (url: string): Promise<boolean> => {
|
||||
const sources = await getDownloadSourcesMap();
|
||||
for (const source of sources.values()) {
|
||||
if (source.url === url) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
let steamGamesFormattedCache: FormattedSteamGamesByLetter | null = null;
|
||||
|
||||
export const getSteamGames = async (): Promise<FormattedSteamGamesByLetter> => {
|
||||
if (steamGamesFormattedCache) {
|
||||
return steamGamesFormattedCache;
|
||||
}
|
||||
|
||||
let steamGames: SteamGamesByLetter;
|
||||
|
||||
const cached = ResourceCache.getCachedData<SteamGamesByLetter>(
|
||||
"steam-games-by-letter"
|
||||
);
|
||||
if (cached) {
|
||||
steamGames = cached;
|
||||
} else {
|
||||
steamGames = await ResourceCache.fetchAndCache<SteamGamesByLetter>(
|
||||
"steam-games-by-letter",
|
||||
`${import.meta.env.MAIN_VITE_EXTERNAL_RESOURCES_URL}/steam-games-by-letter.json`
|
||||
);
|
||||
}
|
||||
|
||||
const formattedData: FormattedSteamGamesByLetter = {};
|
||||
for (const [letter, games] of Object.entries(steamGames)) {
|
||||
formattedData[letter] = games.map((game) => ({
|
||||
...game,
|
||||
formattedName: formatName(game.name),
|
||||
}));
|
||||
}
|
||||
|
||||
steamGamesFormattedCache = formattedData;
|
||||
return formattedData;
|
||||
};
|
||||
|
||||
export type SublevelIterator = AsyncIterable<[string, { id: number }]>;
|
||||
|
||||
export interface SublevelWithId {
|
||||
iterator: () => SublevelIterator;
|
||||
}
|
||||
|
||||
let maxRepackId: number | null = null;
|
||||
let maxDownloadSourceId: number | null = null;
|
||||
|
||||
export const getNextId = async (sublevel: SublevelWithId): Promise<number> => {
|
||||
const isRepackSublevel = sublevel === repacksSublevel;
|
||||
const isDownloadSourceSublevel = sublevel === downloadSourcesSublevel;
|
||||
|
||||
if (isRepackSublevel && maxRepackId !== null) {
|
||||
return ++maxRepackId;
|
||||
}
|
||||
|
||||
if (isDownloadSourceSublevel && maxDownloadSourceId !== null) {
|
||||
return ++maxDownloadSourceId;
|
||||
}
|
||||
|
||||
let maxId = 0;
|
||||
for await (const [, value] of sublevel.iterator()) {
|
||||
if (value.id > maxId) {
|
||||
maxId = value.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRepackSublevel) {
|
||||
maxRepackId = maxId;
|
||||
} else if (isDownloadSourceSublevel) {
|
||||
maxDownloadSourceId = maxId;
|
||||
}
|
||||
|
||||
return maxId + 1;
|
||||
};
|
||||
|
||||
export const invalidateIdCaches = () => {
|
||||
maxRepackId = null;
|
||||
maxDownloadSourceId = null;
|
||||
};
|
||||
|
||||
export const addNewDownloads = async (
|
||||
downloadSource: { id: number; name: string },
|
||||
downloads: z.infer<typeof downloadSourceSchema>["downloads"],
|
||||
steamGames: FormattedSteamGamesByLetter
|
||||
) => {
|
||||
const now = new Date();
|
||||
const objectIdsOnSource = new Set<string>();
|
||||
|
||||
let nextRepackId = await getNextId(repacksSublevel);
|
||||
|
||||
const batch = repacksSublevel.batch();
|
||||
|
||||
const titleHashMapping = await getTitleHashMapping();
|
||||
let hashMatchCount = 0;
|
||||
let fuzzyMatchCount = 0;
|
||||
let noMatchCount = 0;
|
||||
|
||||
for (const download of downloads) {
|
||||
let objectIds: string[] = [];
|
||||
let usedHashMatch = false;
|
||||
|
||||
const titleHash = hashTitle(download.title);
|
||||
const steamIdsFromHash = titleHashMapping[titleHash];
|
||||
|
||||
if (steamIdsFromHash && steamIdsFromHash.length > 0) {
|
||||
hashMatchCount++;
|
||||
usedHashMatch = true;
|
||||
|
||||
objectIds = steamIdsFromHash.map(String);
|
||||
}
|
||||
|
||||
if (!usedHashMatch) {
|
||||
let gamesInSteam: FormattedSteamGame[] = [];
|
||||
const formattedTitle = formatRepackName(download.title);
|
||||
|
||||
if (formattedTitle && formattedTitle.length > 0) {
|
||||
const [firstLetter] = formattedTitle;
|
||||
const games = steamGames[firstLetter] || [];
|
||||
|
||||
gamesInSteam = games.filter((game) =>
|
||||
formattedTitle.startsWith(game.formattedName)
|
||||
);
|
||||
|
||||
if (gamesInSteam.length === 0) {
|
||||
gamesInSteam = games.filter(
|
||||
(game) =>
|
||||
formattedTitle.includes(game.formattedName) ||
|
||||
game.formattedName.includes(formattedTitle)
|
||||
);
|
||||
}
|
||||
|
||||
if (gamesInSteam.length === 0) {
|
||||
for (const letter of Object.keys(steamGames)) {
|
||||
const letterGames = steamGames[letter] || [];
|
||||
const matches = letterGames.filter(
|
||||
(game) =>
|
||||
formattedTitle.includes(game.formattedName) ||
|
||||
game.formattedName.includes(formattedTitle)
|
||||
);
|
||||
if (matches.length > 0) {
|
||||
gamesInSteam = matches;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gamesInSteam.length > 0) {
|
||||
fuzzyMatchCount++;
|
||||
objectIds = gamesInSteam.map((game) => String(game.id));
|
||||
} else {
|
||||
noMatchCount++;
|
||||
}
|
||||
} else {
|
||||
noMatchCount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of objectIds) {
|
||||
objectIdsOnSource.add(id);
|
||||
}
|
||||
|
||||
const repack = {
|
||||
id: nextRepackId++,
|
||||
objectIds: objectIds,
|
||||
title: download.title,
|
||||
uris: download.uris,
|
||||
fileSize: download.fileSize,
|
||||
repacker: downloadSource.name,
|
||||
uploadDate: download.uploadDate,
|
||||
downloadSourceId: downloadSource.id,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
batch.put(`${repack.id}`, repack);
|
||||
}
|
||||
|
||||
await batch.write();
|
||||
|
||||
logger.info(
|
||||
`Matching stats for ${downloadSource.name}: Hash=${hashMatchCount}, Fuzzy=${fuzzyMatchCount}, None=${noMatchCount}`
|
||||
);
|
||||
|
||||
const existingSource = await downloadSourcesSublevel.get(
|
||||
`${downloadSource.id}`
|
||||
);
|
||||
if (existingSource) {
|
||||
await downloadSourcesSublevel.put(`${downloadSource.id}`, {
|
||||
...existingSource,
|
||||
objectIds: Array.from(objectIdsOnSource),
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(objectIdsOnSource);
|
||||
};
|
||||
|
||||
export const importDownloadSourceToLocal = async (
|
||||
url: string,
|
||||
throwOnDuplicate = false
|
||||
) => {
|
||||
const urlExists = await checkUrlExists(url);
|
||||
if (urlExists) {
|
||||
if (throwOnDuplicate) {
|
||||
throw new Error("Download source with this URL already exists");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const response = await axios.get<z.infer<typeof downloadSourceSchema>>(url);
|
||||
|
||||
const steamGames = await getSteamGames();
|
||||
|
||||
const now = new Date();
|
||||
|
||||
const nextId = await getNextId(downloadSourcesSublevel);
|
||||
|
||||
const downloadSource = {
|
||||
id: nextId,
|
||||
url,
|
||||
name: response.data.name,
|
||||
etag: response.headers["etag"] || null,
|
||||
status: DownloadSourceStatus.UpToDate,
|
||||
downloadCount: response.data.downloads.length,
|
||||
objectIds: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
await downloadSourcesSublevel.put(`${downloadSource.id}`, downloadSource);
|
||||
|
||||
const objectIds = await addNewDownloads(
|
||||
downloadSource,
|
||||
response.data.downloads,
|
||||
steamGames
|
||||
);
|
||||
|
||||
// Invalidate ID caches after creating new repacks to prevent ID collisions
|
||||
invalidateIdCaches();
|
||||
|
||||
return {
|
||||
...downloadSource,
|
||||
objectIds,
|
||||
};
|
||||
};
|
||||
|
||||
export const updateDownloadSourcePreservingTimestamp = async (
|
||||
existingSource: DownloadSource,
|
||||
url: string
|
||||
) => {
|
||||
const response = await axios.get<z.infer<typeof downloadSourceSchema>>(url);
|
||||
|
||||
const updatedSource = {
|
||||
...existingSource,
|
||||
name: response.data.name,
|
||||
etag: response.headers["etag"] || null,
|
||||
status: DownloadSourceStatus.UpToDate,
|
||||
downloadCount: response.data.downloads.length,
|
||||
updatedAt: new Date(),
|
||||
// Preserve the original createdAt timestamp
|
||||
};
|
||||
|
||||
await downloadSourcesSublevel.put(`${existingSource.id}`, updatedSource);
|
||||
|
||||
return updatedSource;
|
||||
};
|
||||
17
src/main/events/download-sources/put-download-source.ts
Normal file
17
src/main/events/download-sources/put-download-source.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const putDownloadSource = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
objectIds: string[]
|
||||
) => {
|
||||
return HydraApi.put<{ fingerprint: string }>(
|
||||
"/download-sources",
|
||||
{
|
||||
objectIds,
|
||||
},
|
||||
{ needsAuth: false }
|
||||
);
|
||||
};
|
||||
|
||||
registerEvent("putDownloadSource", putDownloadSource);
|
||||
@@ -1,19 +0,0 @@
|
||||
import { HydraApi, logger } from "@main/services";
|
||||
import { importDownloadSourceToLocal, checkUrlExists } from "./helpers";
|
||||
|
||||
export const syncDownloadSourcesFromApi = async () => {
|
||||
try {
|
||||
const apiSources = await HydraApi.get<
|
||||
{ url: string; createdAt: string; updatedAt: string }[]
|
||||
>("/profile/download-sources");
|
||||
|
||||
for (const apiSource of apiSources) {
|
||||
const exists = await checkUrlExists(apiSource.url);
|
||||
if (!exists) {
|
||||
await importDownloadSourceToLocal(apiSource.url, false);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Failed to sync download sources from API:", error);
|
||||
}
|
||||
};
|
||||
@@ -1,115 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import { downloadSourcesSublevel, repacksSublevel } from "@main/level";
|
||||
import { DownloadSourceStatus } from "@shared";
|
||||
import {
|
||||
invalidateIdCaches,
|
||||
downloadSourceSchema,
|
||||
getSteamGames,
|
||||
addNewDownloads,
|
||||
} from "./helpers";
|
||||
|
||||
const syncDownloadSources = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
): Promise<number> => {
|
||||
let newRepacksCount = 0;
|
||||
|
||||
try {
|
||||
const downloadSources: Array<{
|
||||
id: number;
|
||||
url: string;
|
||||
name: string;
|
||||
etag: string | null;
|
||||
status: number;
|
||||
downloadCount: number;
|
||||
objectIds: string[];
|
||||
fingerprint?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}> = [];
|
||||
for await (const [, source] of downloadSourcesSublevel.iterator()) {
|
||||
downloadSources.push(source);
|
||||
}
|
||||
|
||||
const existingRepacks: Array<{
|
||||
id: number;
|
||||
title: string;
|
||||
uris: string[];
|
||||
repacker: string;
|
||||
fileSize: string | null;
|
||||
objectIds: string[];
|
||||
uploadDate: Date | string | null;
|
||||
downloadSourceId: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}> = [];
|
||||
for await (const [, repack] of repacksSublevel.iterator()) {
|
||||
existingRepacks.push(repack);
|
||||
}
|
||||
|
||||
// Handle sources with missing fingerprints individually, don't delete all sources
|
||||
const sourcesWithFingerprints = downloadSources.filter(
|
||||
(source) => source.fingerprint
|
||||
);
|
||||
const sourcesWithoutFingerprints = downloadSources.filter(
|
||||
(source) => !source.fingerprint
|
||||
);
|
||||
|
||||
// For sources without fingerprints, just continue with normal sync
|
||||
// They will get fingerprints updated later by updateMissingFingerprints
|
||||
const allSourcesToSync = [
|
||||
...sourcesWithFingerprints,
|
||||
...sourcesWithoutFingerprints,
|
||||
];
|
||||
|
||||
for (const downloadSource of allSourcesToSync) {
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
if (downloadSource.etag) {
|
||||
headers["If-None-Match"] = downloadSource.etag;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get(downloadSource.url, {
|
||||
headers,
|
||||
});
|
||||
|
||||
const source = downloadSourceSchema.parse(response.data);
|
||||
const steamGames = await getSteamGames();
|
||||
|
||||
const repacks = source.downloads.filter(
|
||||
(download) =>
|
||||
!existingRepacks.some((repack) => repack.title === download.title)
|
||||
);
|
||||
|
||||
await downloadSourcesSublevel.put(`${downloadSource.id}`, {
|
||||
...downloadSource,
|
||||
etag: response.headers["etag"] || null,
|
||||
downloadCount: source.downloads.length,
|
||||
status: DownloadSourceStatus.UpToDate,
|
||||
});
|
||||
|
||||
await addNewDownloads(downloadSource, repacks, steamGames);
|
||||
|
||||
newRepacksCount += repacks.length;
|
||||
} catch (err: unknown) {
|
||||
const isNotModified = (err as AxiosError).response?.status === 304;
|
||||
|
||||
await downloadSourcesSublevel.put(`${downloadSource.id}`, {
|
||||
...downloadSource,
|
||||
status: isNotModified
|
||||
? DownloadSourceStatus.UpToDate
|
||||
: DownloadSourceStatus.Errored,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
invalidateIdCaches();
|
||||
|
||||
return newRepacksCount;
|
||||
} catch (err) {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("syncDownloadSources", syncDownloadSources);
|
||||
@@ -1,67 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { downloadSourcesSublevel } from "@main/level";
|
||||
import { HydraApi, logger } from "@main/services";
|
||||
|
||||
const updateMissingFingerprints = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
): Promise<number> => {
|
||||
const sourcesNeedingFingerprints: Array<{
|
||||
id: number;
|
||||
objectIds: string[];
|
||||
}> = [];
|
||||
|
||||
for await (const [, source] of downloadSourcesSublevel.iterator()) {
|
||||
if (
|
||||
!source.fingerprint &&
|
||||
source.objectIds &&
|
||||
source.objectIds.length > 0
|
||||
) {
|
||||
sourcesNeedingFingerprints.push({
|
||||
id: source.id,
|
||||
objectIds: source.objectIds,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (sourcesNeedingFingerprints.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Updating fingerprints for ${sourcesNeedingFingerprints.length} sources`
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
sourcesNeedingFingerprints.map(async (source) => {
|
||||
try {
|
||||
const { fingerprint } = await HydraApi.put<{ fingerprint: string }>(
|
||||
"/download-sources",
|
||||
{
|
||||
objectIds: source.objectIds,
|
||||
},
|
||||
{ needsAuth: false }
|
||||
);
|
||||
|
||||
const existingSource = await downloadSourcesSublevel.get(
|
||||
`${source.id}`
|
||||
);
|
||||
if (existingSource) {
|
||||
await downloadSourcesSublevel.put(`${source.id}`, {
|
||||
...existingSource,
|
||||
fingerprint,
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to update fingerprint for source ${source.id}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return sourcesNeedingFingerprints.length;
|
||||
};
|
||||
|
||||
registerEvent("updateMissingFingerprints", updateMissingFingerprints);
|
||||
@@ -1,32 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import axios from "axios";
|
||||
import { z } from "zod";
|
||||
|
||||
const downloadSourceSchema = z.object({
|
||||
name: z.string().max(255),
|
||||
downloads: z.array(
|
||||
z.object({
|
||||
title: z.string().max(255),
|
||||
uris: z.array(z.string()),
|
||||
uploadDate: z.string().max(255),
|
||||
fileSize: z.string().max(255),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
const validateDownloadSource = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
url: string
|
||||
) => {
|
||||
const response = await axios.get<z.infer<typeof downloadSourceSchema>>(url);
|
||||
|
||||
const { name } = downloadSourceSchema.parse(response.data);
|
||||
|
||||
return {
|
||||
name,
|
||||
etag: response.headers["etag"] || null,
|
||||
downloadCount: response.data.downloads.length,
|
||||
};
|
||||
};
|
||||
|
||||
registerEvent("validateDownloadSource", validateDownloadSource);
|
||||
@@ -1,13 +1,10 @@
|
||||
import { DiskUsage } from "@types";
|
||||
import disk from "diskusage";
|
||||
|
||||
import { registerEvent } from "../register-event";
|
||||
import checkDiskSpace from "check-disk-space";
|
||||
|
||||
const getDiskFreeSpace = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
path: string
|
||||
): Promise<DiskUsage> => {
|
||||
const result = await checkDiskSpace(path);
|
||||
return { free: result.free, total: result.size };
|
||||
};
|
||||
) => disk.check(path);
|
||||
|
||||
registerEvent("getDiskFreeSpace", getDiskFreeSpace);
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
import { appVersion, defaultDownloadsPath, isStaging } from "@main/constants";
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import "./catalogue/get-catalogue";
|
||||
import "./catalogue/get-game-shop-details";
|
||||
import "./catalogue/save-game-shop-assets";
|
||||
import "./catalogue/get-how-long-to-beat";
|
||||
import "./catalogue/get-random-game";
|
||||
import "./catalogue/search-games";
|
||||
import "./catalogue/get-game-stats";
|
||||
import "./catalogue/get-trending-games";
|
||||
import "./catalogue/get-publishers";
|
||||
import "./catalogue/get-developers";
|
||||
import "./hardware/get-disk-free-space";
|
||||
import "./hardware/check-folder-write-permission";
|
||||
import "./library/add-game-to-library";
|
||||
import "./library/add-custom-game-to-library";
|
||||
import "./library/update-custom-game";
|
||||
import "./library/update-game-custom-assets";
|
||||
import "./library/add-game-to-favorites";
|
||||
import "./library/remove-game-from-favorites";
|
||||
import "./library/toggle-game-pin";
|
||||
import "./library/create-game-shortcut";
|
||||
import "./library/close-game";
|
||||
import "./library/delete-game-folder";
|
||||
import "./library/get-game-by-object-id";
|
||||
import "./library/sync-game-by-object-id";
|
||||
import "./library/get-library";
|
||||
import "./library/extract-game-download";
|
||||
import "./library/open-game";
|
||||
@@ -30,24 +34,17 @@ import "./library/remove-game";
|
||||
import "./library/remove-game-from-library";
|
||||
import "./library/select-game-wine-prefix";
|
||||
import "./library/reset-game-achievements";
|
||||
import "./library/change-game-playtime";
|
||||
import "./library/toggle-automatic-cloud-sync";
|
||||
import "./library/get-default-wine-prefix-selection-path";
|
||||
import "./library/cleanup-unused-assets";
|
||||
import "./library/create-steam-shortcut";
|
||||
import "./library/copy-custom-game-asset";
|
||||
import "./misc/open-checkout";
|
||||
import "./misc/open-external";
|
||||
import "./misc/show-open-dialog";
|
||||
import "./misc/get-features";
|
||||
import "./misc/show-item-in-folder";
|
||||
import "./misc/get-badges";
|
||||
import "./misc/install-common-redist";
|
||||
import "./misc/can-install-common-redist";
|
||||
import "./misc/save-temp-file";
|
||||
import "./misc/delete-temp-file";
|
||||
import "./misc/install-hydra-decky-plugin";
|
||||
import "./misc/get-hydra-decky-plugin-info";
|
||||
import "./misc/check-homebrew-folder-exists";
|
||||
import "./misc/hydra-api-call";
|
||||
import "./torrenting/cancel-game-download";
|
||||
import "./torrenting/pause-game-download";
|
||||
import "./torrenting/resume-game-download";
|
||||
@@ -62,29 +59,36 @@ import "./autoupdater/check-for-updates";
|
||||
import "./autoupdater/restart-and-install-update";
|
||||
import "./user-preferences/authenticate-real-debrid";
|
||||
import "./user-preferences/authenticate-torbox";
|
||||
import "./download-sources/add-download-source";
|
||||
import "./download-sources/update-missing-fingerprints";
|
||||
import "./download-sources/delete-download-source";
|
||||
import "./download-sources/delete-all-download-sources";
|
||||
import "./download-sources/validate-download-source";
|
||||
import "./download-sources/sync-download-sources";
|
||||
import "./download-sources/get-download-sources-list";
|
||||
import "./download-sources/check-download-source-exists";
|
||||
import "./repacks/get-all-repacks";
|
||||
import "./download-sources/put-download-source";
|
||||
import "./auth/sign-out";
|
||||
import "./auth/open-auth-window";
|
||||
import "./auth/get-session-hash";
|
||||
import "./user/get-user";
|
||||
import "./user/get-blocked-users";
|
||||
import "./user/block-user";
|
||||
import "./user/unblock-user";
|
||||
import "./user/get-user-friends";
|
||||
import "./user/get-auth";
|
||||
import "./user/get-user-stats";
|
||||
import "./user/report-user";
|
||||
import "./user/get-unlocked-achievements";
|
||||
import "./user/get-compared-unlocked-achievements";
|
||||
import "./profile/get-friend-requests";
|
||||
import "./profile/get-me";
|
||||
import "./profile/undo-friendship";
|
||||
import "./profile/update-friend-request";
|
||||
import "./profile/update-profile";
|
||||
import "./profile/process-profile-image";
|
||||
import "./profile/send-friend-request";
|
||||
import "./profile/sync-friend-requests";
|
||||
import "./cloud-save/download-game-artifact";
|
||||
import "./cloud-save/get-game-artifacts";
|
||||
import "./cloud-save/get-game-backup-preview";
|
||||
import "./cloud-save/upload-save-game";
|
||||
import "./cloud-save/delete-game-artifact";
|
||||
import "./cloud-save/select-game-backup-path";
|
||||
import "./cloud-save/toggle-artifact-freeze";
|
||||
import "./cloud-save/rename-game-artifact";
|
||||
import "./notifications/publish-new-repacks-notification";
|
||||
import "./notifications/update-achievement-notification-window";
|
||||
import "./notifications/show-achievement-test-notification";
|
||||
@@ -98,6 +102,7 @@ import "./themes/get-custom-theme-by-id";
|
||||
import "./themes/get-active-custom-theme";
|
||||
import "./themes/close-editor-window";
|
||||
import "./themes/toggle-custom-theme";
|
||||
import "./download-sources/create-download-sources";
|
||||
import "./download-sources/remove-download-source";
|
||||
import "./download-sources/get-download-sources";
|
||||
import { isPortableVersion } from "@main/helpers";
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
const addCustomGameToLibrary = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
title: string,
|
||||
executablePath: string,
|
||||
iconUrl?: string,
|
||||
logoImageUrl?: string,
|
||||
libraryHeroImageUrl?: string
|
||||
) => {
|
||||
const objectId = randomUUID();
|
||||
const shop: GameShop = "custom";
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
|
||||
const existingGames = await gamesSublevel.iterator().all();
|
||||
const existingGame = existingGames.find(
|
||||
([_key, game]) => game.executablePath === executablePath && !game.isDeleted
|
||||
);
|
||||
|
||||
if (existingGame) {
|
||||
throw new Error(
|
||||
"A game with this executable path already exists in your library"
|
||||
);
|
||||
}
|
||||
|
||||
const assets = {
|
||||
updatedAt: Date.now(),
|
||||
objectId,
|
||||
shop,
|
||||
title,
|
||||
iconUrl: iconUrl || null,
|
||||
libraryHeroImageUrl: libraryHeroImageUrl || "",
|
||||
libraryImageUrl: iconUrl || "",
|
||||
logoImageUrl: logoImageUrl || "",
|
||||
logoPosition: null,
|
||||
coverImageUrl: iconUrl || "",
|
||||
};
|
||||
await gamesShopAssetsSublevel.put(gameKey, assets);
|
||||
|
||||
const game = {
|
||||
title,
|
||||
iconUrl: iconUrl || null,
|
||||
logoImageUrl: logoImageUrl || null,
|
||||
libraryHeroImageUrl: libraryHeroImageUrl || null,
|
||||
objectId,
|
||||
shop,
|
||||
remoteId: null,
|
||||
isDeleted: false,
|
||||
playTimeInMilliseconds: 0,
|
||||
lastTimePlayed: null,
|
||||
executablePath,
|
||||
launchOptions: null,
|
||||
favorite: false,
|
||||
automaticCloudSync: false,
|
||||
hasManuallyUpdatedPlaytime: false,
|
||||
};
|
||||
|
||||
await gamesSublevel.put(gameKey, game);
|
||||
|
||||
return game;
|
||||
};
|
||||
|
||||
registerEvent("addCustomGameToLibrary", addCustomGameToLibrary);
|
||||
@@ -1,13 +1,13 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import type { GameShop } from "@types";
|
||||
import { createGame } from "@main/services/library-sync";
|
||||
import { updateLocalUnlockedAchievements } from "@main/services/achievements/update-local-unlocked-achivements";
|
||||
import {
|
||||
downloadsSublevel,
|
||||
gamesShopAssetsSublevel,
|
||||
gamesSublevel,
|
||||
levelKeys,
|
||||
} from "@main/level";
|
||||
import { AchievementWatcherManager } from "@main/services/achievements/achievement-watcher-manager";
|
||||
|
||||
const addGameToLibrary = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
@@ -30,8 +30,6 @@ const addGameToLibrary = async (
|
||||
game = {
|
||||
title,
|
||||
iconUrl: gameAssets?.iconUrl ?? null,
|
||||
libraryHeroImageUrl: gameAssets?.libraryHeroImageUrl ?? null,
|
||||
logoImageUrl: gameAssets?.logoImageUrl ?? null,
|
||||
objectId,
|
||||
shop,
|
||||
remoteId: null,
|
||||
@@ -43,14 +41,9 @@ const addGameToLibrary = async (
|
||||
await gamesSublevel.put(gameKey, game);
|
||||
}
|
||||
|
||||
if (game) {
|
||||
await createGame(game).catch(() => {});
|
||||
await createGame(game).catch(() => {});
|
||||
|
||||
AchievementWatcherManager.firstSyncWithRemoteIfNeeded(
|
||||
game.shop,
|
||||
game.objectId
|
||||
);
|
||||
}
|
||||
updateLocalUnlockedAchievements(game);
|
||||
};
|
||||
|
||||
registerEvent("addGameToLibrary", addGameToLibrary);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { HydraApi } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { GameShop } from "@types";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
|
||||
const changeGamePlaytime = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
playTimeInSeconds: number
|
||||
) => {
|
||||
try {
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
const game = await gamesSublevel.get(gameKey);
|
||||
if (!game) return;
|
||||
|
||||
if (game.remoteId) {
|
||||
await HydraApi.put(`/profile/games/${shop}/${objectId}/playtime`, {
|
||||
playTimeInSeconds,
|
||||
});
|
||||
}
|
||||
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...game,
|
||||
playTimeInMilliseconds: playTimeInSeconds * 1000,
|
||||
hasManuallyUpdatedPlaytime: true,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to update game playtime: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("changeGamePlayTime", changeGamePlaytime);
|
||||
@@ -1,74 +0,0 @@
|
||||
import { ipcMain } from "electron";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { ASSETS_PATH } from "@main/constants";
|
||||
|
||||
const getCustomGamesAssetsPath = () => {
|
||||
return path.join(ASSETS_PATH, "custom-games");
|
||||
};
|
||||
|
||||
const getAllCustomGameAssets = async (): Promise<string[]> => {
|
||||
const assetsPath = getCustomGamesAssetsPath();
|
||||
|
||||
if (!fs.existsSync(assetsPath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = await fs.promises.readdir(assetsPath);
|
||||
return files.map((file) => path.join(assetsPath, file));
|
||||
};
|
||||
|
||||
const getUsedAssetPaths = async (): Promise<Set<string>> => {
|
||||
const { gamesSublevel } = await import("@main/level");
|
||||
const allGames = await gamesSublevel.iterator().all();
|
||||
|
||||
const customGames = allGames
|
||||
.map(([_key, game]) => game)
|
||||
.filter((game) => game.shop === "custom" && !game.isDeleted);
|
||||
|
||||
const usedPaths = new Set<string>();
|
||||
|
||||
customGames.forEach((game) => {
|
||||
if (game.iconUrl?.startsWith("local:")) {
|
||||
usedPaths.add(game.iconUrl.replace("local:", ""));
|
||||
}
|
||||
if (game.logoImageUrl?.startsWith("local:")) {
|
||||
usedPaths.add(game.logoImageUrl.replace("local:", ""));
|
||||
}
|
||||
if (game.libraryHeroImageUrl?.startsWith("local:")) {
|
||||
usedPaths.add(game.libraryHeroImageUrl.replace("local:", ""));
|
||||
}
|
||||
});
|
||||
|
||||
return usedPaths;
|
||||
};
|
||||
|
||||
export const cleanupUnusedAssets = async (): Promise<{
|
||||
deletedCount: number;
|
||||
errors: string[];
|
||||
}> => {
|
||||
try {
|
||||
const allAssets = await getAllCustomGameAssets();
|
||||
const usedAssets = await getUsedAssetPaths();
|
||||
|
||||
const errors: string[] = [];
|
||||
let deletedCount = 0;
|
||||
|
||||
for (const assetPath of allAssets) {
|
||||
if (!usedAssets.has(assetPath)) {
|
||||
try {
|
||||
await fs.promises.unlink(assetPath);
|
||||
deletedCount++;
|
||||
} catch (error) {
|
||||
errors.push(`Failed to delete ${assetPath}: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { deletedCount, errors };
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to cleanup unused assets: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
ipcMain.handle("cleanupUnusedAssets", cleanupUnusedAssets);
|
||||
@@ -1,36 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { ASSETS_PATH } from "@main/constants";
|
||||
|
||||
const copyCustomGameAsset = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
sourcePath: string,
|
||||
assetType: "icon" | "logo" | "hero"
|
||||
): Promise<string> => {
|
||||
if (!sourcePath || !fs.existsSync(sourcePath)) {
|
||||
throw new Error("Source file does not exist");
|
||||
}
|
||||
|
||||
if (!fs.existsSync(ASSETS_PATH)) {
|
||||
fs.mkdirSync(ASSETS_PATH, { recursive: true });
|
||||
}
|
||||
|
||||
const customGamesAssetsPath = path.join(ASSETS_PATH, "custom-games");
|
||||
if (!fs.existsSync(customGamesAssetsPath)) {
|
||||
fs.mkdirSync(customGamesAssetsPath, { recursive: true });
|
||||
}
|
||||
|
||||
const fileExtension = path.extname(sourcePath);
|
||||
|
||||
const uniqueId = randomUUID();
|
||||
const fileName = `${assetType}-${uniqueId}${fileExtension}`;
|
||||
const destinationPath = path.join(customGamesAssetsPath, fileName);
|
||||
|
||||
await fs.promises.copyFile(sourcePath, destinationPath);
|
||||
|
||||
return `local:${destinationPath}`;
|
||||
};
|
||||
|
||||
registerEvent("copyCustomGameAsset", copyCustomGameAsset);
|
||||
@@ -1,11 +1,12 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import type { GameShop, ShopAssets } from "@types";
|
||||
import type { GameShop, GameStats } from "@types";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
import {
|
||||
composeSteamShortcut,
|
||||
getSteamLocation,
|
||||
getSteamShortcuts,
|
||||
getSteamUsersIds,
|
||||
HydraApi,
|
||||
logger,
|
||||
SystemPath,
|
||||
writeSteamShortcuts,
|
||||
@@ -14,7 +15,6 @@ import fs from "node:fs";
|
||||
import axios from "axios";
|
||||
import path from "node:path";
|
||||
import { ASSETS_PATH } from "@main/constants";
|
||||
import { getGameAssets } from "../catalogue/get-game-assets";
|
||||
|
||||
const downloadAsset = async (downloadPath: string, url?: string | null) => {
|
||||
try {
|
||||
@@ -41,7 +41,7 @@ const downloadAsset = async (downloadPath: string, url?: string | null) => {
|
||||
const downloadAssetsFromSteam = async (
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
assets: ShopAssets | null
|
||||
assets: GameStats["assets"]
|
||||
) => {
|
||||
const gameAssetsPath = path.join(ASSETS_PATH, `${shop}-${objectId}`);
|
||||
|
||||
@@ -86,13 +86,15 @@ const createSteamShortcut = async (
|
||||
throw new Error("No executable path found for game");
|
||||
}
|
||||
|
||||
const assets = await getGameAssets(objectId, shop);
|
||||
const { assets } = await HydraApi.get<GameStats>(
|
||||
`/games/stats?objectId=${objectId}&shop=${shop}`
|
||||
);
|
||||
|
||||
const steamUserIds = await getSteamUsersIds();
|
||||
|
||||
if (!steamUserIds.length) {
|
||||
logger.error("No Steam user ID found");
|
||||
throw new Error("No Steam user ID found");
|
||||
return;
|
||||
}
|
||||
|
||||
const [iconImage, heroImage, logoImage, coverImage, libraryImage] =
|
||||
|
||||
@@ -23,10 +23,7 @@ const getLibrary = async (): Promise<LibraryGame[]> => {
|
||||
...game,
|
||||
download: download ?? null,
|
||||
...gameAssets,
|
||||
// Ensure compatibility with LibraryGame type
|
||||
libraryHeroImageUrl:
|
||||
game.libraryHeroImageUrl ?? gameAssets?.libraryHeroImageUrl,
|
||||
} as LibraryGame;
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,70 +1,7 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi, logger } from "@main/services";
|
||||
import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
||||
import type { GameShop, Game } from "@types";
|
||||
import fs from "node:fs";
|
||||
|
||||
const collectAssetPathsToDelete = (game: Game): string[] => {
|
||||
const assetPathsToDelete: string[] = [];
|
||||
|
||||
const assetUrls =
|
||||
game.shop === "custom"
|
||||
? [game.iconUrl, game.logoImageUrl, game.libraryHeroImageUrl]
|
||||
: [game.customIconUrl, game.customLogoImageUrl, game.customHeroImageUrl];
|
||||
|
||||
for (const url of assetUrls) {
|
||||
if (url?.startsWith("local:")) {
|
||||
assetPathsToDelete.push(url.replace("local:", ""));
|
||||
}
|
||||
}
|
||||
|
||||
return assetPathsToDelete;
|
||||
};
|
||||
|
||||
const updateGameAsDeleted = async (
|
||||
game: Game,
|
||||
gameKey: string
|
||||
): Promise<void> => {
|
||||
const updatedGame = {
|
||||
...game,
|
||||
isDeleted: true,
|
||||
executablePath: null,
|
||||
...(game.shop !== "custom" && {
|
||||
customIconUrl: null,
|
||||
customLogoImageUrl: null,
|
||||
customHeroImageUrl: null,
|
||||
}),
|
||||
};
|
||||
|
||||
await gamesSublevel.put(gameKey, updatedGame);
|
||||
};
|
||||
|
||||
const resetShopAssets = async (gameKey: string): Promise<void> => {
|
||||
const existingAssets = await gamesShopAssetsSublevel.get(gameKey);
|
||||
if (existingAssets) {
|
||||
const resetAssets = {
|
||||
...existingAssets,
|
||||
title: existingAssets.title,
|
||||
};
|
||||
await gamesShopAssetsSublevel.put(gameKey, resetAssets);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteAssetFiles = async (
|
||||
assetPathsToDelete: string[]
|
||||
): Promise<void> => {
|
||||
if (assetPathsToDelete.length === 0) return;
|
||||
|
||||
for (const assetPath of assetPathsToDelete) {
|
||||
try {
|
||||
if (fs.existsSync(assetPath)) {
|
||||
await fs.promises.unlink(assetPath);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to delete asset ${assetPath}:`, error);
|
||||
}
|
||||
}
|
||||
};
|
||||
import { HydraApi } from "@main/services";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
const removeGameFromLibrary = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
@@ -74,21 +11,17 @@ const removeGameFromLibrary = async (
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
const game = await gamesSublevel.get(gameKey);
|
||||
|
||||
if (!game) return;
|
||||
if (game) {
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...game,
|
||||
isDeleted: true,
|
||||
executablePath: null,
|
||||
});
|
||||
|
||||
const assetPathsToDelete = collectAssetPathsToDelete(game);
|
||||
|
||||
await updateGameAsDeleted(game, gameKey);
|
||||
|
||||
if (game.shop !== "custom") {
|
||||
await resetShopAssets(gameKey);
|
||||
if (game?.remoteId) {
|
||||
HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
if (game?.remoteId) {
|
||||
HydraApi.delete(`/profile/games/${game.remoteId}`).catch(() => {});
|
||||
}
|
||||
|
||||
await deleteAssetFiles(assetPathsToDelete);
|
||||
};
|
||||
|
||||
registerEvent("removeGameFromLibrary", removeGameFromLibrary);
|
||||
|
||||
@@ -16,8 +16,7 @@ const resetGameAchievements = async (
|
||||
objectId: string
|
||||
) => {
|
||||
try {
|
||||
const levelKey = levelKeys.game(shop, objectId);
|
||||
const game = await gamesSublevel.get(levelKey);
|
||||
const game = await gamesSublevel.get(levelKeys.game(shop, objectId));
|
||||
|
||||
if (!game) return;
|
||||
|
||||
@@ -30,6 +29,8 @@ const resetGameAchievements = async (
|
||||
}
|
||||
}
|
||||
|
||||
const levelKey = levelKeys.game(game.shop, game.objectId);
|
||||
|
||||
await gameAchievementsSublevel
|
||||
.get(levelKey)
|
||||
.then(async (gameAchievements) => {
|
||||
|
||||
32
src/main/events/library/sync-game-by-object-id.ts
Normal file
32
src/main/events/library/sync-game-by-object-id.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { GameShop, UserGameDetails } from "@types";
|
||||
|
||||
const syncGameByObjectId = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
shop: GameShop,
|
||||
objectId: string
|
||||
) => {
|
||||
return HydraApi.get<UserGameDetails>(
|
||||
`/profile/games/${shop}/${objectId}`
|
||||
).then(async (res) => {
|
||||
const { id, playTimeInSeconds, isFavorite, ...rest } = res;
|
||||
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
|
||||
const currentData = await gamesSublevel.get(gameKey);
|
||||
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...currentData,
|
||||
...rest,
|
||||
remoteId: id,
|
||||
playTimeInMilliseconds: playTimeInSeconds * 1000,
|
||||
favorite: isFavorite ?? currentData?.favorite,
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("syncGameByObjectId", syncGameByObjectId);
|
||||
@@ -1,43 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
import { HydraApi, logger } from "@main/services";
|
||||
import type { GameShop, UserGame } from "@types";
|
||||
|
||||
const toggleGamePin = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
shop: GameShop,
|
||||
objectId: string,
|
||||
pin: boolean
|
||||
) => {
|
||||
try {
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
|
||||
const game = await gamesSublevel.get(gameKey);
|
||||
if (!game) return;
|
||||
|
||||
if (pin) {
|
||||
const response = await HydraApi.put<UserGame>(
|
||||
`/profile/games/${shop}/${objectId}/pin`
|
||||
);
|
||||
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...game,
|
||||
isPinned: pin,
|
||||
pinnedDate: new Date(response.pinnedDate!),
|
||||
});
|
||||
} else {
|
||||
await HydraApi.put(`/profile/games/${shop}/${objectId}/unpin`);
|
||||
|
||||
await gamesSublevel.put(gameKey, {
|
||||
...game,
|
||||
isPinned: pin,
|
||||
pinnedDate: null,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Failed to update game pinned status", error);
|
||||
throw new Error(`Failed to update game pinned status: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("toggleGamePin", toggleGamePin);
|
||||
@@ -1,98 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
||||
import type { GameShop } from "@types";
|
||||
import fs from "node:fs";
|
||||
import { logger } from "@main/services";
|
||||
|
||||
interface UpdateCustomGameParams {
|
||||
shop: GameShop;
|
||||
objectId: string;
|
||||
title: string;
|
||||
iconUrl?: string;
|
||||
logoImageUrl?: string;
|
||||
libraryHeroImageUrl?: string;
|
||||
originalIconPath?: string;
|
||||
originalLogoPath?: string;
|
||||
originalHeroPath?: string;
|
||||
}
|
||||
|
||||
const updateCustomGame = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
params: UpdateCustomGameParams
|
||||
) => {
|
||||
const {
|
||||
shop,
|
||||
objectId,
|
||||
title,
|
||||
iconUrl,
|
||||
logoImageUrl,
|
||||
libraryHeroImageUrl,
|
||||
originalIconPath,
|
||||
originalLogoPath,
|
||||
originalHeroPath,
|
||||
} = params;
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
|
||||
const existingGame = await gamesSublevel.get(gameKey);
|
||||
if (!existingGame) {
|
||||
throw new Error("Game not found");
|
||||
}
|
||||
|
||||
const oldAssetPaths: string[] = [];
|
||||
|
||||
const assetPairs = [
|
||||
{ existing: existingGame.iconUrl, new: iconUrl },
|
||||
{ existing: existingGame.logoImageUrl, new: logoImageUrl },
|
||||
{ existing: existingGame.libraryHeroImageUrl, new: libraryHeroImageUrl },
|
||||
];
|
||||
|
||||
for (const { existing, new: newUrl } of assetPairs) {
|
||||
if (existing?.startsWith("local:") && (!newUrl || existing !== newUrl)) {
|
||||
oldAssetPaths.push(existing.replace("local:", ""));
|
||||
}
|
||||
}
|
||||
|
||||
const updatedGame = {
|
||||
...existingGame,
|
||||
title,
|
||||
iconUrl: iconUrl || null,
|
||||
logoImageUrl: logoImageUrl || null,
|
||||
libraryHeroImageUrl: libraryHeroImageUrl || null,
|
||||
originalIconPath: originalIconPath || existingGame.originalIconPath || null,
|
||||
originalLogoPath: originalLogoPath || existingGame.originalLogoPath || null,
|
||||
originalHeroPath: originalHeroPath || existingGame.originalHeroPath || null,
|
||||
};
|
||||
|
||||
await gamesSublevel.put(gameKey, updatedGame);
|
||||
|
||||
const existingAssets = await gamesShopAssetsSublevel.get(gameKey);
|
||||
if (existingAssets) {
|
||||
const updatedAssets = {
|
||||
...existingAssets,
|
||||
title,
|
||||
iconUrl: iconUrl || null,
|
||||
libraryHeroImageUrl: libraryHeroImageUrl || "",
|
||||
libraryImageUrl: iconUrl || "",
|
||||
logoImageUrl: logoImageUrl || "",
|
||||
coverImageUrl: iconUrl || "",
|
||||
};
|
||||
|
||||
await gamesShopAssetsSublevel.put(gameKey, updatedAssets);
|
||||
}
|
||||
|
||||
if (oldAssetPaths.length > 0) {
|
||||
for (const assetPath of oldAssetPaths) {
|
||||
try {
|
||||
if (fs.existsSync(assetPath)) {
|
||||
await fs.promises.unlink(assetPath);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to delete old asset ${assetPath}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updatedGame;
|
||||
};
|
||||
|
||||
registerEvent("updateCustomGame", updateCustomGame);
|
||||
@@ -1,162 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
||||
import type { GameShop, Game } from "@types";
|
||||
import fs from "node:fs";
|
||||
import { logger } from "@main/services";
|
||||
|
||||
const collectOldAssetPaths = (
|
||||
existingGame: Game,
|
||||
customIconUrl?: string | null,
|
||||
customLogoImageUrl?: string | null,
|
||||
customHeroImageUrl?: string | null
|
||||
): string[] => {
|
||||
const oldAssetPaths: string[] = [];
|
||||
|
||||
const assetPairs = [
|
||||
{ existing: existingGame.customIconUrl, new: customIconUrl },
|
||||
{ existing: existingGame.customLogoImageUrl, new: customLogoImageUrl },
|
||||
{ existing: existingGame.customHeroImageUrl, new: customHeroImageUrl },
|
||||
];
|
||||
|
||||
for (const { existing, new: newUrl } of assetPairs) {
|
||||
if (
|
||||
existing &&
|
||||
newUrl !== undefined &&
|
||||
existing !== newUrl &&
|
||||
existing.startsWith("local:")
|
||||
) {
|
||||
oldAssetPaths.push(existing.replace("local:", ""));
|
||||
}
|
||||
}
|
||||
|
||||
return oldAssetPaths;
|
||||
};
|
||||
|
||||
interface UpdateGameDataParams {
|
||||
gameKey: string;
|
||||
existingGame: Game;
|
||||
title: string;
|
||||
customIconUrl?: string | null;
|
||||
customLogoImageUrl?: string | null;
|
||||
customHeroImageUrl?: string | null;
|
||||
customOriginalIconPath?: string | null;
|
||||
customOriginalLogoPath?: string | null;
|
||||
customOriginalHeroPath?: string | null;
|
||||
}
|
||||
|
||||
const updateGameData = async (params: UpdateGameDataParams): Promise<Game> => {
|
||||
const {
|
||||
gameKey,
|
||||
existingGame,
|
||||
title,
|
||||
customIconUrl,
|
||||
customLogoImageUrl,
|
||||
customHeroImageUrl,
|
||||
customOriginalIconPath,
|
||||
customOriginalLogoPath,
|
||||
customOriginalHeroPath,
|
||||
} = params;
|
||||
const updatedGame = {
|
||||
...existingGame,
|
||||
title,
|
||||
...(customIconUrl !== undefined && { customIconUrl }),
|
||||
...(customLogoImageUrl !== undefined && { customLogoImageUrl }),
|
||||
...(customHeroImageUrl !== undefined && { customHeroImageUrl }),
|
||||
...(customOriginalIconPath !== undefined && { customOriginalIconPath }),
|
||||
...(customOriginalLogoPath !== undefined && { customOriginalLogoPath }),
|
||||
...(customOriginalHeroPath !== undefined && { customOriginalHeroPath }),
|
||||
};
|
||||
|
||||
await gamesSublevel.put(gameKey, updatedGame);
|
||||
return updatedGame;
|
||||
};
|
||||
|
||||
const updateShopAssets = async (
|
||||
gameKey: string,
|
||||
title: string
|
||||
): Promise<void> => {
|
||||
const existingAssets = await gamesShopAssetsSublevel.get(gameKey);
|
||||
if (existingAssets) {
|
||||
const updatedAssets = {
|
||||
...existingAssets,
|
||||
title,
|
||||
};
|
||||
await gamesShopAssetsSublevel.put(gameKey, updatedAssets);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteOldAssetFiles = async (oldAssetPaths: string[]): Promise<void> => {
|
||||
if (oldAssetPaths.length === 0) return;
|
||||
|
||||
for (const assetPath of oldAssetPaths) {
|
||||
try {
|
||||
if (fs.existsSync(assetPath)) {
|
||||
await fs.promises.unlink(assetPath);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to delete old custom asset ${assetPath}:`, error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interface UpdateGameCustomAssetsParams {
|
||||
shop: GameShop;
|
||||
objectId: string;
|
||||
title: string;
|
||||
customIconUrl?: string | null;
|
||||
customLogoImageUrl?: string | null;
|
||||
customHeroImageUrl?: string | null;
|
||||
customOriginalIconPath?: string | null;
|
||||
customOriginalLogoPath?: string | null;
|
||||
customOriginalHeroPath?: string | null;
|
||||
}
|
||||
|
||||
const updateGameCustomAssets = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
params: UpdateGameCustomAssetsParams
|
||||
) => {
|
||||
const {
|
||||
shop,
|
||||
objectId,
|
||||
title,
|
||||
customIconUrl,
|
||||
customLogoImageUrl,
|
||||
customHeroImageUrl,
|
||||
customOriginalIconPath,
|
||||
customOriginalLogoPath,
|
||||
customOriginalHeroPath,
|
||||
} = params;
|
||||
const gameKey = levelKeys.game(shop, objectId);
|
||||
|
||||
const existingGame = await gamesSublevel.get(gameKey);
|
||||
if (!existingGame) {
|
||||
throw new Error("Game not found");
|
||||
}
|
||||
|
||||
const oldAssetPaths = collectOldAssetPaths(
|
||||
existingGame,
|
||||
customIconUrl,
|
||||
customLogoImageUrl,
|
||||
customHeroImageUrl
|
||||
);
|
||||
|
||||
const updatedGame = await updateGameData({
|
||||
gameKey,
|
||||
existingGame,
|
||||
title,
|
||||
customIconUrl,
|
||||
customLogoImageUrl,
|
||||
customHeroImageUrl,
|
||||
customOriginalIconPath,
|
||||
customOriginalLogoPath,
|
||||
customOriginalHeroPath,
|
||||
});
|
||||
|
||||
await updateShopAssets(gameKey, title);
|
||||
|
||||
await deleteOldAssetFiles(oldAssetPaths);
|
||||
|
||||
return updatedGame;
|
||||
};
|
||||
|
||||
registerEvent("updateGameCustomAssets", updateGameCustomAssets);
|
||||
@@ -1,13 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { DECKY_PLUGINS_LOCATION } from "@main/constants";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const checkHomebrewFolderExists = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
): Promise<boolean> => {
|
||||
const homebrewPath = path.dirname(DECKY_PLUGINS_LOCATION);
|
||||
return fs.existsSync(homebrewPath);
|
||||
};
|
||||
|
||||
registerEvent("checkHomebrewFolderExists", checkHomebrewFolderExists);
|
||||
@@ -1,18 +0,0 @@
|
||||
import fs from "node:fs";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const deleteTempFile = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
filePath: string
|
||||
): Promise<void> => {
|
||||
try {
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently fail - temp files will be cleaned up by OS eventually
|
||||
console.warn(`Failed to delete temp file: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("deleteTempFile", deleteTempFile);
|
||||
22
src/main/events/misc/get-badges.ts
Normal file
22
src/main/events/misc/get-badges.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Badge } from "@types";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { db, levelKeys } from "@main/level";
|
||||
|
||||
const getBadges = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const language = await db
|
||||
.get<string, string>(levelKeys.language, {
|
||||
valueEncoding: "utf8",
|
||||
})
|
||||
.then((language) => language || "en");
|
||||
|
||||
const params = new URLSearchParams({
|
||||
locale: language,
|
||||
});
|
||||
|
||||
return HydraApi.get<Badge[]>(`/badges?${params.toString()}`, null, {
|
||||
needsAuth: false,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("getBadges", getBadges);
|
||||
8
src/main/events/misc/get-features.ts
Normal file
8
src/main/events/misc/get-features.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const getFeatures = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
return HydraApi.get<string[]>("/features", null, { needsAuth: false });
|
||||
};
|
||||
|
||||
registerEvent("getFeatures", getFeatures);
|
||||
@@ -1,94 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { logger, HydraApi } from "@main/services";
|
||||
import { HYDRA_DECKY_PLUGIN_LOCATION } from "@main/constants";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
interface DeckyReleaseInfo {
|
||||
version: string;
|
||||
downloadUrl: string;
|
||||
}
|
||||
|
||||
const getHydraDeckyPluginInfo = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
): Promise<{
|
||||
installed: boolean;
|
||||
version: string | null;
|
||||
path: string;
|
||||
outdated: boolean;
|
||||
expectedVersion: string | null;
|
||||
}> => {
|
||||
try {
|
||||
// Fetch the expected version from API
|
||||
let expectedVersion: string | null = null;
|
||||
try {
|
||||
const releaseInfo = await HydraApi.get<DeckyReleaseInfo>(
|
||||
"/decky/release",
|
||||
{},
|
||||
{ needsAuth: false }
|
||||
);
|
||||
expectedVersion = releaseInfo.version;
|
||||
} catch (error) {
|
||||
logger.error("Failed to fetch Decky release info:", error);
|
||||
}
|
||||
|
||||
// Check if plugin folder exists
|
||||
if (!fs.existsSync(HYDRA_DECKY_PLUGIN_LOCATION)) {
|
||||
logger.log("Hydra Decky plugin not installed");
|
||||
return {
|
||||
installed: false,
|
||||
version: null,
|
||||
path: HYDRA_DECKY_PLUGIN_LOCATION,
|
||||
outdated: true,
|
||||
expectedVersion,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if package.json exists
|
||||
const packageJsonPath = path.join(
|
||||
HYDRA_DECKY_PLUGIN_LOCATION,
|
||||
"package.json"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
logger.log("Hydra Decky plugin package.json not found");
|
||||
return {
|
||||
installed: false,
|
||||
version: null,
|
||||
path: HYDRA_DECKY_PLUGIN_LOCATION,
|
||||
outdated: true,
|
||||
expectedVersion,
|
||||
};
|
||||
}
|
||||
|
||||
// Read and parse package.json
|
||||
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
|
||||
const packageJson = JSON.parse(packageJsonContent);
|
||||
const version = packageJson.version;
|
||||
|
||||
const outdated = expectedVersion ? version !== expectedVersion : false;
|
||||
|
||||
logger.log(
|
||||
`Hydra Decky plugin installed, version: ${version}, expected: ${expectedVersion}, outdated: ${outdated}`
|
||||
);
|
||||
|
||||
return {
|
||||
installed: true,
|
||||
version,
|
||||
path: HYDRA_DECKY_PLUGIN_LOCATION,
|
||||
outdated,
|
||||
expectedVersion,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error("Failed to get plugin info:", error);
|
||||
return {
|
||||
installed: false,
|
||||
version: null,
|
||||
path: HYDRA_DECKY_PLUGIN_LOCATION,
|
||||
outdated: true,
|
||||
expectedVersion: null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("getHydraDeckyPluginInfo", getHydraDeckyPluginInfo);
|
||||
@@ -1,38 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
interface HydraApiCallPayload {
|
||||
method: "get" | "post" | "put" | "patch" | "delete";
|
||||
url: string;
|
||||
data?: unknown;
|
||||
params?: unknown;
|
||||
options?: {
|
||||
needsAuth?: boolean;
|
||||
needsSubscription?: boolean;
|
||||
ifModifiedSince?: Date;
|
||||
};
|
||||
}
|
||||
|
||||
const hydraApiCall = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
payload: HydraApiCallPayload
|
||||
) => {
|
||||
const { method, url, data, params, options } = payload;
|
||||
|
||||
switch (method) {
|
||||
case "get":
|
||||
return HydraApi.get(url, params, options);
|
||||
case "post":
|
||||
return HydraApi.post(url, data, options);
|
||||
case "put":
|
||||
return HydraApi.put(url, data, options);
|
||||
case "patch":
|
||||
return HydraApi.patch(url, data, options);
|
||||
case "delete":
|
||||
return HydraApi.delete(url, options);
|
||||
default:
|
||||
throw new Error(`Unsupported HTTP method: ${method}`);
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("hydraApiCall", hydraApiCall);
|
||||
@@ -1,50 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { logger, DeckyPlugin } from "@main/services";
|
||||
import { HYDRA_DECKY_PLUGIN_LOCATION } from "@main/constants";
|
||||
|
||||
const installHydraDeckyPlugin = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
path: string;
|
||||
currentVersion: string | null;
|
||||
expectedVersion: string;
|
||||
error?: string;
|
||||
}> => {
|
||||
try {
|
||||
logger.log("Installing/updating Hydra Decky plugin...");
|
||||
|
||||
const result = await DeckyPlugin.checkPluginVersion();
|
||||
|
||||
if (result.exists && !result.outdated) {
|
||||
logger.log("Plugin installed successfully");
|
||||
return {
|
||||
success: true,
|
||||
path: HYDRA_DECKY_PLUGIN_LOCATION,
|
||||
currentVersion: result.currentVersion,
|
||||
expectedVersion: result.expectedVersion,
|
||||
};
|
||||
} else {
|
||||
logger.error("Failed to install plugin");
|
||||
return {
|
||||
success: false,
|
||||
path: HYDRA_DECKY_PLUGIN_LOCATION,
|
||||
currentVersion: result.currentVersion,
|
||||
expectedVersion: result.expectedVersion,
|
||||
error: "Plugin installation failed",
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
logger.error("Failed to install plugin:", error);
|
||||
return {
|
||||
success: false,
|
||||
path: HYDRA_DECKY_PLUGIN_LOCATION,
|
||||
currentVersion: null,
|
||||
expectedVersion: "unknown",
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("installHydraDeckyPlugin", installHydraDeckyPlugin);
|
||||
@@ -1,27 +0,0 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { app } from "electron";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const saveTempFile = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
fileName: string,
|
||||
fileData: Uint8Array
|
||||
): Promise<string> => {
|
||||
try {
|
||||
const tempDir = app.getPath("temp");
|
||||
const tempFilePath = path.join(
|
||||
tempDir,
|
||||
`hydra-temp-${Date.now()}-${fileName}`
|
||||
);
|
||||
|
||||
// Write the file data to temp directory
|
||||
fs.writeFileSync(tempFilePath, fileData);
|
||||
|
||||
return tempFilePath;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to save temp file: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
registerEvent("saveTempFile", saveTempFile);
|
||||
11
src/main/events/profile/get-friend-requests.ts
Normal file
11
src/main/events/profile/get-friend-requests.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { FriendRequest } from "@types";
|
||||
|
||||
const getFriendRequests = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
): Promise<FriendRequest[]> => {
|
||||
return HydraApi.get(`/profile/friend-requests`).catch(() => []);
|
||||
};
|
||||
|
||||
registerEvent("getFriendRequests", getFriendRequests);
|
||||
11
src/main/events/profile/send-friend-request.ts
Normal file
11
src/main/events/profile/send-friend-request.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const sendFriendRequest = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
userId: string
|
||||
) => {
|
||||
return HydraApi.post("/profile/friend-requests", { friendCode: userId });
|
||||
};
|
||||
|
||||
registerEvent("sendFriendRequest", sendFriendRequest);
|
||||
11
src/main/events/profile/undo-friendship.ts
Normal file
11
src/main/events/profile/undo-friendship.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const undoFriendship = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
userId: string
|
||||
) => {
|
||||
await HydraApi.delete(`/profile/friends/${userId}`);
|
||||
};
|
||||
|
||||
registerEvent("undoFriendship", undoFriendship);
|
||||
19
src/main/events/profile/update-friend-request.ts
Normal file
19
src/main/events/profile/update-friend-request.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { FriendRequestAction } from "@types";
|
||||
|
||||
const updateFriendRequest = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
userId: string,
|
||||
action: FriendRequestAction
|
||||
) => {
|
||||
if (action == "CANCEL") {
|
||||
return HydraApi.delete(`/profile/friend-requests/${userId}`);
|
||||
}
|
||||
|
||||
return HydraApi.patch(`/profile/friend-requests/${userId}`, {
|
||||
requestState: action,
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("updateFriendRequest", updateFriendRequest);
|
||||
@@ -1,16 +0,0 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { repacksSublevel, GameRepack } from "@main/level";
|
||||
|
||||
const getAllRepacks = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
const repacks: GameRepack[] = [];
|
||||
|
||||
for await (const [, repack] of repacksSublevel.iterator()) {
|
||||
if (Array.isArray(repack.objectIds)) {
|
||||
repacks.push(repack);
|
||||
}
|
||||
}
|
||||
|
||||
return repacks;
|
||||
};
|
||||
|
||||
registerEvent("getAllRepacks", getAllRepacks);
|
||||
@@ -53,8 +53,6 @@ const startGameDownload = async (
|
||||
await gamesSublevel.put(gameKey, {
|
||||
title,
|
||||
iconUrl: gameAssets?.iconUrl ?? null,
|
||||
libraryHeroImageUrl: gameAssets?.libraryHeroImageUrl ?? null,
|
||||
logoImageUrl: gameAssets?.logoImageUrl ?? null,
|
||||
objectId,
|
||||
shop,
|
||||
remoteId: null,
|
||||
@@ -93,9 +91,14 @@ const startGameDownload = async (
|
||||
|
||||
await Promise.all([
|
||||
createGame(updatedGame!).catch(() => {}),
|
||||
HydraApi.post(`/games/${shop}/${objectId}/download`, null, {
|
||||
needsAuth: false,
|
||||
}).catch(() => {}),
|
||||
HydraApi.post(
|
||||
"/games/download",
|
||||
{
|
||||
objectId,
|
||||
shop,
|
||||
},
|
||||
{ needsAuth: false }
|
||||
).catch(() => {}),
|
||||
]);
|
||||
|
||||
return { ok: true };
|
||||
|
||||
11
src/main/events/user/block-user.ts
Normal file
11
src/main/events/user/block-user.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
|
||||
const blockUser = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
userId: string
|
||||
) => {
|
||||
await HydraApi.post(`/users/${userId}/block`);
|
||||
};
|
||||
|
||||
registerEvent("blockUser", blockUser);
|
||||
19
src/main/events/user/get-blocked-users.ts
Normal file
19
src/main/events/user/get-blocked-users.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { UserNotLoggedInError } from "@shared";
|
||||
import type { UserBlocks } from "@types";
|
||||
|
||||
export const getBlockedUsers = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
take: number,
|
||||
skip: number
|
||||
): Promise<UserBlocks> => {
|
||||
return HydraApi.get(`/profile/blocks`, { take, skip }).catch((err) => {
|
||||
if (err instanceof UserNotLoggedInError) {
|
||||
return { blocks: [] };
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
registerEvent("getBlockedUsers", getBlockedUsers);
|
||||
@@ -3,7 +3,6 @@ import { registerEvent } from "../register-event";
|
||||
|
||||
import { HydraApi } from "@main/services";
|
||||
import { db, levelKeys } from "@main/level";
|
||||
import { AchievementWatcherManager } from "@main/services/achievements/achievement-watcher-manager";
|
||||
|
||||
const getComparedUnlockedAchievements = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
@@ -11,8 +10,6 @@ const getComparedUnlockedAchievements = async (
|
||||
shop: GameShop,
|
||||
userId: string
|
||||
) => {
|
||||
await AchievementWatcherManager.firstSyncWithRemoteIfNeeded(shop, objectId);
|
||||
|
||||
const userPreferences = await db.get<string, UserPreferences | null>(
|
||||
levelKeys.userPreferences,
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { GameShop, UserAchievement, UserPreferences } from "@types";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data";
|
||||
import { db, gameAchievementsSublevel, levelKeys } from "@main/level";
|
||||
import { AchievementWatcherManager } from "@main/services/achievements/achievement-watcher-manager";
|
||||
|
||||
export const getUnlockedAchievements = async (
|
||||
objectId: string,
|
||||
@@ -63,7 +62,7 @@ export const getUnlockedAchievements = async (
|
||||
!achievementData.hidden || showHiddenAchievementsDescription
|
||||
? achievementData.description
|
||||
: undefined,
|
||||
};
|
||||
} as UserAchievement;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
if (a.unlocked && !b.unlocked) return -1;
|
||||
@@ -80,7 +79,6 @@ const getUnlockedAchievementsEvent = async (
|
||||
objectId: string,
|
||||
shop: GameShop
|
||||
): Promise<UserAchievement[]> => {
|
||||
await AchievementWatcherManager.firstSyncWithRemoteIfNeeded(shop, objectId);
|
||||
return getUnlockedAchievements(objectId, shop, false);
|
||||
};
|
||||
|
||||
|
||||
32
src/main/events/user/get-user-friends.ts
Normal file
32
src/main/events/user/get-user-friends.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { db } from "@main/level";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { User, UserFriends } from "@types";
|
||||
import { levelKeys } from "@main/level/sublevels";
|
||||
|
||||
export const getUserFriends = async (
|
||||
userId: string,
|
||||
take: number,
|
||||
skip: number
|
||||
): Promise<UserFriends> => {
|
||||
const user = await db.get<string, User>(levelKeys.user, {
|
||||
valueEncoding: "json",
|
||||
});
|
||||
|
||||
if (user?.id === userId) {
|
||||
return HydraApi.get(`/profile/friends`, { take, skip });
|
||||
}
|
||||
|
||||
return HydraApi.get(`/users/${userId}/friends`, { take, skip });
|
||||
};
|
||||
|
||||
const getUserFriendsEvent = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
userId: string,
|
||||
take: number,
|
||||
skip: number
|
||||
) => {
|
||||
return getUserFriends(userId, take, skip);
|
||||
};
|
||||
|
||||
registerEvent("getUserFriends", getUserFriendsEvent);
|
||||
12
src/main/events/user/get-user-stats.ts
Normal file
12
src/main/events/user/get-user-stats.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { UserStats } from "@types";
|
||||
|
||||
export const getUserStats = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
userId: string
|
||||
): Promise<UserStats> => {
|
||||
return HydraApi.get(`/users/${userId}/stats`);
|
||||
};
|
||||
|
||||
registerEvent("getUserStats", getUserStats);
|
||||
12
src/main/events/user/get-user.ts
Normal file
12
src/main/events/user/get-user.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import type { UserProfile } from "@types";
|
||||
|
||||
const getUser = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
userId: string
|
||||
): Promise<UserProfile | null> => {
|
||||
return HydraApi.get<UserProfile>(`/users/${userId}`).catch(() => null);
|
||||
};
|
||||
|
||||
registerEvent("getUser", getUser);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user