Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc2d383166 | ||
|
|
f30fdc5ed1 | ||
|
|
290d3c31a2 | ||
|
|
27fb83ba04 | ||
|
|
0e78fe02e4 | ||
|
|
7914e9e9bb | ||
|
|
06d34dff5e | ||
|
|
c7e48c16f8 | ||
|
|
4ee0f524d4 | ||
|
|
43c7be55c6 | ||
|
|
d9fa00bb38 | ||
|
|
0a042df8e0 | ||
|
|
a60a2f8e39 | ||
|
|
d27f387d6d | ||
|
|
ed675f90d8 | ||
|
|
4492aa3425 | ||
|
|
56d93b6128 | ||
|
|
398fb274fa | ||
|
|
0343d48c13 | ||
|
|
a3892c0fd3 | ||
|
|
f7df315695 | ||
|
|
aec996a9ce | ||
|
|
c252e60f7f |
+5
-1
@@ -26,7 +26,7 @@ set(DRAGONX_VERSION_SUFFIX "")
|
||||
# ObsidianDragonLite is versioned INDEPENDENTLY of the full-node app above. The active variant's
|
||||
# version flows to the generated header, the Windows .rc/manifest, and build.sh's release names via
|
||||
# DRAGONX_APP_VERSION* (resolved in the lite/full block below).
|
||||
set(DRAGONX_LITE_VERSION "1.0.0")
|
||||
set(DRAGONX_LITE_VERSION "1.1.0")
|
||||
set(DRAGONX_LITE_VERSION_SUFFIX "")
|
||||
|
||||
# C++17 standard
|
||||
@@ -521,6 +521,8 @@ set(APP_SOURCES
|
||||
src/ui/windows/settings_window.cpp
|
||||
src/ui/pages/settings_page.cpp
|
||||
src/ui/windows/about_dialog.cpp
|
||||
src/ui/windows/faq_dialog.cpp
|
||||
src/ui/windows/faq_content.cpp
|
||||
src/ui/windows/key_export_dialog.cpp
|
||||
src/ui/windows/transaction_details_dialog.cpp
|
||||
src/ui/windows/qr_popup_dialog.cpp
|
||||
@@ -657,6 +659,8 @@ set(APP_HEADERS
|
||||
src/ui/windows/console_tab_helpers.h
|
||||
src/ui/windows/settings_window.h
|
||||
src/ui/windows/about_dialog.h
|
||||
src/ui/windows/faq_dialog.h
|
||||
src/ui/windows/faq_content.h
|
||||
src/ui/windows/key_export_dialog.h
|
||||
src/ui/windows/transaction_details_dialog.h
|
||||
src/ui/windows/qr_popup_dialog.h
|
||||
|
||||
@@ -213,6 +213,21 @@ require_wallet_rebuild_helper() {
|
||||
info " wallet-rebuild helper present: $helper"
|
||||
}
|
||||
|
||||
# Vendored Berkeley DB depends directory for the macOS wallet-rebuild helper, by TARGET arch. The daemon
|
||||
# links a static libdb-6.2; the helper must match the build arch, so derive the triple from it rather than
|
||||
# hardcoding one. Single-arch: x86_64 -> x86_64-apple-darwin, arm64 -> aarch64-apple-darwin. "universal"
|
||||
# (or anything unexpected) falls back to the host arch — a universal .app needs a universal libdb, so if
|
||||
# only a single-arch libdb is vendored, build single-arch via DRAGONX_MAC_ARCHS.
|
||||
mac_bdb_dir() {
|
||||
local arch="$1" triple
|
||||
case "$arch" in
|
||||
x86_64) triple="x86_64-apple-darwin" ;;
|
||||
arm64) triple="aarch64-apple-darwin" ;;
|
||||
*) [[ "$(uname -m)" == "arm64" ]] && triple="aarch64-apple-darwin" || triple="x86_64-apple-darwin" ;;
|
||||
esac
|
||||
printf '%s/external/dragonx/depends/%s\n' "$SCRIPT_DIR" "$triple"
|
||||
}
|
||||
|
||||
# ── Helper: find resource files ──────────────────────────────────────────────
|
||||
find_sapling_params() {
|
||||
local dirs=(
|
||||
@@ -842,6 +857,11 @@ HDR
|
||||
cmake --build . -j "$JOBS"
|
||||
|
||||
[[ -f "bin/${APP_BASENAME}.exe" ]] || { err "Windows build failed"; exit 1; }
|
||||
# Strip the app exe — the Linux and macOS release paths already strip theirs, and even the Windows
|
||||
# helper (dragonx-wallet-rebuild.exe) is stripped, but the main app exe was shipping unstripped
|
||||
# (~5MB of symbols on the full node, ~19MB on the params-heavy lite build). Best-effort.
|
||||
x86_64-w64-mingw32-strip --strip-all "bin/${APP_BASENAME}.exe" 2>/dev/null \
|
||||
|| warn " strip unavailable — shipping unstripped ${APP_BASENAME}.exe"
|
||||
info "Binary: $(du -h "bin/${APP_BASENAME}.exe" | cut -f1)"
|
||||
|
||||
# A full-node release MUST include the recovery helper — fail loudly, never ship without it.
|
||||
@@ -1078,6 +1098,12 @@ TOOLCHAIN
|
||||
fi
|
||||
|
||||
info "Configuring (cross-compile via osxcross) ..."
|
||||
# Vendored static libdb for the recovery helper (full-node only), by target arch; see mac_bdb_dir.
|
||||
local mac_bdb; mac_bdb="$(mac_bdb_dir "$MAC_ARCH")"
|
||||
local BDB_ARGS=()
|
||||
if should_bundle_full_node_assets && [[ -f "$mac_bdb/lib/libdb-6.2.a" && -f "$mac_bdb/include/db.h" ]]; then
|
||||
BDB_ARGS=( -DBDB_INCLUDE_DIR="$mac_bdb/include" -DBDB_LIBRARY="$mac_bdb/lib/libdb-6.2.a" )
|
||||
fi
|
||||
cmake "$SCRIPT_DIR" \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$bd/osxcross-toolchain.cmake" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
@@ -1085,6 +1111,7 @@ TOOLCHAIN
|
||||
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||
${COMPILER_RT:+-DOSXCROSS_COMPILER_RT="$COMPILER_RT"} \
|
||||
"${BDB_ARGS[@]}" \
|
||||
"${CMAKE_LITE_ARGS[@]}"
|
||||
else
|
||||
# Build libsodium as universal if needed
|
||||
@@ -1106,12 +1133,19 @@ TOOLCHAIN
|
||||
fi
|
||||
|
||||
info "Configuring (native macOS, arch: $MAC_ARCHS) ..."
|
||||
# Point CMake at the vendored static libdb for the recovery helper (full-node only); see mac_bdb_dir.
|
||||
local mac_bdb; mac_bdb="$(mac_bdb_dir "$MAC_ARCH")"
|
||||
local BDB_ARGS=()
|
||||
if should_bundle_full_node_assets && [[ -f "$mac_bdb/lib/libdb-6.2.a" && -f "$mac_bdb/include/db.h" ]]; then
|
||||
BDB_ARGS=( -DBDB_INCLUDE_DIR="$mac_bdb/include" -DBDB_LIBRARY="$mac_bdb/lib/libdb-6.2.a" )
|
||||
fi
|
||||
cmake "$SCRIPT_DIR" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
||||
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="$MAC_ARCHS" \
|
||||
"${BDB_ARGS[@]}" \
|
||||
"${CMAKE_LITE_ARGS[@]}"
|
||||
fi
|
||||
|
||||
@@ -1123,7 +1157,7 @@ TOOLCHAIN
|
||||
# A full-node release MUST include the recovery helper. macOS needs a static libdb-6.2 (Homebrew
|
||||
# berkeley-db for a native build, or a vendored external/dragonx/depends/<triple>) — otherwise CMake
|
||||
# skips the target and this fails loudly rather than shipping a mac release with no recovery option.
|
||||
require_wallet_rebuild_helper "bin/dragonx-wallet-rebuild" "$SCRIPT_DIR/external/dragonx/depends/aarch64-apple-darwin"
|
||||
require_wallet_rebuild_helper "bin/dragonx-wallet-rebuild" "$(mac_bdb_dir "$MAC_ARCH")"
|
||||
|
||||
# Strip — use osxcross strip for cross-builds
|
||||
if $IS_CROSS; then
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
**DragonX (DRGX) Lite Desktop Wallet — v1.0.0**
|
||||
|
||||
ObsidianDragonLite is a lightweight companion to the ObsidianDragon full-node wallet. It skips the embedded full node entirely — **no multi-gigabyte blockchain download and near-instant startup** — by connecting to a DragonX lite-wallet (lightwalletd-style) server, while keeping the same native ImGui interface and shielded-first workflow.
|
||||
|
||||
This is the **first release** of the Lite variant.
|
||||
|
||||
---
|
||||
|
||||
## Why Lite?
|
||||
|
||||
- **No blockchain to download.** Sync in seconds instead of hours; a few MB of state instead of many GB.
|
||||
- **Same wallet, lighter footprint.** The familiar ObsidianDragon UI, shielded and transparent addresses, and address book — without running a node or miner.
|
||||
- **Portable.** A single self-contained binary that stores its data in its own `ObsidianDragonLite` folder, so it coexists cleanly alongside the full-node wallet.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Fast, node-free operation** — connect to a DragonX lite-wallet server and sync in seconds; minimal disk and RAM.
|
||||
- **Wallet lifecycle** — create a new wallet, restore from a seed phrase, or open an existing one; wallets auto-open on startup, with a first-run welcome prompt and **guided seed backup** on creation.
|
||||
- **Shielded + transparent** — send, shield, and receive DRGX; per-address balances computed from unspent notes and UTXOs.
|
||||
- **Keys & seed** — export your seed phrase and keys, and import keys, from Settings.
|
||||
- **Passphrase encryption** — encrypt, unlock, lock, and decrypt the wallet; you're prompted to unlock at send time and on startup when the wallet is locked.
|
||||
- **Encrypted messaging (HushChat)** — built-in **Contacts** and **Chat** with a seed-derived, **SilentDragonXLite-compatible** identity and a seed-encrypted local message store, so you can message other DragonX users end-to-end.
|
||||
- **Server management** — a built-in **server browser with automatic failover**; switch servers with live reconnect/recovery, and a **"Redownload blocks"** action to rescan from the server.
|
||||
- **Status & diagnostics** — a **Network tab** showing connection and sync status, plus an **interactive Console** for running backend commands and copyable error output.
|
||||
- **Multi-language UI** — full internationalization covering 8 languages: German, Spanish, French, Japanese, Korean, Portuguese, Russian, and Chinese.
|
||||
- **Cross-platform** — native builds for Linux (AppImage + zip), Windows (portable exe + zip), and macOS (DMG + .app), x86-64.
|
||||
- QR code generation for receiving addresses
|
||||
- Extensive theme and appearance options
|
||||
|
||||
---
|
||||
|
||||
## How it differs from the full-node wallet
|
||||
|
||||
- **No embedded `dragonxd`, no mining, no local blockchain explorer.** Chain data comes from the lite-wallet server rather than being verified locally by a full node — a lighter footprint in exchange for trusting the server for chain state.
|
||||
- Everything key-related stays **on your device**: seed, keys, and (when enabled) the encryption passphrase never leave the machine.
|
||||
|
||||
---
|
||||
|
||||
## Downloads
|
||||
|
||||
| File | SHA-256 |
|
||||
|------|---------|
|
||||
| ObsidianDragonLite-1.0.0.AppImage | `a1459d6081124a6b8df47aa898b60c0237875e8cbb132e4fd8ae637673055ed4` |
|
||||
| ObsidianDragonLite-1.0.0-Linux-x64.zip | `573af502a6372334572e4dc88bd26e0cf36ed543b9df1e835fd8e4abd295108d` |
|
||||
| ObsidianDragonLite-1.0.0.exe | `28659efb770fa573bd1b1d0fcab1c1a3f9c7e46574185c3916cc9f669684852c` |
|
||||
| ObsidianDragonLite-1.0.0-Windows-x64.zip | `f6434a931e873e5936ebe249b044d6c7a413694767c667374c91ee438259b50c` |
|
||||
| ObsidianDragonLite-1.0.0-macOS-x86_64.dmg | `10f976f1453e6b1978cb7a85026fd033c85a10c1e66f436475aa628379a298c7` |
|
||||
| ObsidianDragonLite-1.0.0-macOS-x86_64.app.zip | `3e7259c363f7be2e9027c5282c94b742f4be280c998203752dc0a0551f6ab68b` |
|
||||
|
||||
## System Requirements
|
||||
|
||||
- **Linux:** x86-64, glibc 2.31+ (Ubuntu 20.04+, Fedora 33+, etc.)
|
||||
- **Windows:** x86-64, Windows 10 or later
|
||||
- **macOS:** x86-64 (Intel; runs on Apple Silicon via Rosetta 2), macOS 10.15 Catalina or later
|
||||
- Network access to a DragonX lite-wallet server.
|
||||
|
||||
## License
|
||||
|
||||
Released under the **GPLv3**. See [LICENSE](https://git.dragonx.is/DragonX/ObsidianDragon/src/branch/master/LICENSE) for details.
|
||||
@@ -0,0 +1,86 @@
|
||||
**DragonX (DRGX) Full-Node Desktop Wallet — v2.0.0**
|
||||
|
||||
This is ObsidianDragon, a native ImGui-based wallet for the DragonX network. It ships with an embedded full-node daemon (`dragonxd`) and an integrated CPU miner (DRG-XMRig), giving users a complete, self-contained experience on Linux, Windows, and macOS.
|
||||
|
||||
---
|
||||
|
||||
## What's New in v2.0.0
|
||||
|
||||
v2.0.0 is a major release. It adds **end-to-end encrypted HushChat messaging** and **BIP39 seed-phrase wallets** (with migrate-to-seed for legacy wallets), lets the wallet **update its own node and miner** with each install cryptographically verified, and ships a **security-hardening pass** across the updater, RPC, and secret-storage surfaces — plus full support for the **new multi-threaded `dragonxd`** and a large stability and UX overhaul.
|
||||
|
||||
### New Features
|
||||
|
||||
- **Encrypted messaging (HushChat)** — new **Contacts** and **Chat** tabs bring DRGX-native, end-to-end encrypted messaging. Your chat identity is derived from your wallet seed, messages are encrypted with libsodium (XChaCha20-Poly1305 / secretstream) and stored in a **seed-encrypted local database**, and the wire format is interoperable with SilentDragonX / SilentDragonXLite.
|
||||
- **BIP39 seed-phrase wallets & migrate-to-seed** — new wallets are backed by a mnemonic **seed phrase** you can back up from Settings. Existing **legacy wallets can be migrated into a seed wallet** — the wallet mints a new mnemonic wallet, sweeps your funds to it, and adopts it only once the sweep is mined, keeping a timestamped `wallet.dat` backup throughout. (The bundled `dragonxd` supports the required mnemonic RPCs; older nodes degrade gracefully.)
|
||||
- **In-app daemon updater** — Settings → **Node & Security → Daemon binary → "Check for updates…"** downloads the latest `dragonxd` from the project Gitea, verifies its **SHA-256 and a detached ed25519 signature** before installing, and replaces the binary **atomically while the node keeps running** (the new build takes effect on the next daemon start). A two-pane version picker lets you pin, downgrade, or install any published/pre-release build.
|
||||
- **In-app miner updater** — an **"Update miner…"** action in the Mining tab fetches, verifies (SHA-256 + ed25519), and installs the latest DRG-XMRig, with the same version picker and a display of current vs. latest.
|
||||
- **Daemon binary management** — the wallet no longer auto-overwrites a node you've dropped in; a dedicated Settings panel shows the managed binary and gathers all node actions (restart, rescan, repair) onto one toolbar.
|
||||
- **Repair Wallet** — a one-click `-zapwallettxes=2` recovery in Settings, plus automatic wallet reconciliation after a bootstrap and runtime rescan support for pruned nodes.
|
||||
- **Explorer search** — fuzzy, live (debounced) filtering of the block list by partial hash or height.
|
||||
- **History sorting & fast load** — sort transactions by date or amount, a "Loading older history (N%)" indicator during the initial bulk load, and persisted history that surfaces pending sends immediately.
|
||||
- **Smarter mining** — the thread benchmark now measures **sustained (thermally-throttled) hashrate** instead of an inflated initial burst, with GPU-aware idle mining and corrected idle thread scaling.
|
||||
|
||||
### Security & Integrity
|
||||
|
||||
- **Mandatory signature verification** — both the daemon and miner updaters **require** a valid ed25519 signature (checked against a key pinned in the wallet) in addition to the SHA-256; an install is refused if the signature is missing or invalid.
|
||||
- **Hardened secret handling** — RPC credentials are wiped from memory after use, RPC responses are size-capped and scrubbed of secrets in logs, generated node config is written **owner-only (0600)**, and secrets copied to the clipboard **auto-clear**.
|
||||
- **Safe on-disk writes** — settings, address book, and secret files are written **atomically with owner-only permissions**; the PIN vault fsyncs its secure-delete overwrite; SQLite cache growth is bounded.
|
||||
- **Recipient validation** — sends validate recipient address checksums (Base58Check + Bech32) before building a transaction.
|
||||
- **Path-traversal protection** — archive extraction (chain bootstrap and the updaters) rejects any entry that would escape the target directory (zip-slip), and the bootstrap fails closed on a missing checksum rather than trusting an unverified archive.
|
||||
- **Robustness** — malformed RPC error JSON is guarded and sends are single-flight, preventing duplicate/ill-formed submissions.
|
||||
|
||||
### Improvements
|
||||
|
||||
- **New multi-threaded daemon support** — live Sapling note-witness rebuild progress, accurate sync-speed display, reliable rescan-completion detection, and RPC polling throttled during sync so block download isn't slowed.
|
||||
- **Networking** — DragonX DNS seed nodes, `-maxconnections` passed to the daemon, and a peer count that stays current on every tab.
|
||||
- **Node resilience** — non-blocking warmup so you can connect while the daemon is still initializing, a live daemon-console tail on the startup overlay, fast-failing connect probes, and mid-session disconnect detection that keeps the UI responsive (in-flight RPC calls abort cleanly on disconnect/shutdown).
|
||||
- **Address list** — modernized with drag-to-transfer, labels, and view-only handling.
|
||||
- **UI / DPI** — overlay dialogs scale correctly with the font/DPI setting, improved CJK font rendering, native language names in the language picker, and format-incompatible translations are rejected.
|
||||
- **Settings** — an "Open data folder" button and confirmation modals for rescan and restart-daemon.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
Extensive fixes across history (shielded-tx ordering, stuck "refreshing history" banners, unconfirmed-badge stickiness), sends (correct fee passed to `z_sendmany`, fast-lane worker restart on reconnect, note-selection fee-gap workaround), rescan (accurate completion detection, no false "complete", no per-second error flood), RPC (mid-session disconnect handling, stale-refresh invalidation), and UI layout/i18n. See the commit history for the full list.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Full-node wallet** — send, receive, and verify DRGX transactions with a bundled `dragonxd` daemon; no external setup required.
|
||||
- **Self-updating** — verify-and-install the latest node and miner from within the app (SHA-256 + ed25519 signature enforced).
|
||||
- **Encrypted messaging** — built-in HushChat with a seed-derived identity, end-to-end encryption, and a seed-encrypted local message store, interoperable with SilentDragonX / SilentDragonXLite.
|
||||
- **Seed-phrase wallets** — BIP39 mnemonic backup, and migrate-to-seed to upgrade a legacy wallet into a seed wallet.
|
||||
- **Built-in CPU mining** — start/stop DRG-XMRig from the Mining tab with real-time hashrate and pool statistics, mine-when-idle support with configurable delay, and sustained-hashrate benchmarking.
|
||||
- **Multi-language UI** — full internationalization covering 8 languages: German, Spanish, French, Japanese, Korean, Portuguese, Russian, and Chinese.
|
||||
- **Cross-platform** — native builds for Linux (AppImage + zip), Windows (portable exe + zip), and macOS (**universal** DMG + .app); Linux/Windows x86-64, macOS Intel + Apple Silicon.
|
||||
- Shielded (z-address) and transparent (t-address) send/receive
|
||||
- QR code generation for receiving addresses
|
||||
- Transaction history with memo support, date/amount sorting, and pending-send tracking
|
||||
- Blockchain explorer with live block/transaction search
|
||||
- Blockchain rescan and Sapling witness-rebuild with status-bar progress
|
||||
- Repair Wallet and daemon-binary management in Settings
|
||||
- Built-in console
|
||||
- Extensive theme and appearance options
|
||||
|
||||
---
|
||||
|
||||
## Downloads
|
||||
|
||||
| File | SHA-256 |
|
||||
|------|---------|
|
||||
| ObsidianDragon-2.0.0.AppImage | `34c9ea57ec27bf415e59d2890d995ed9069bfce04d979d7d8d58aa18f4570aa1` |
|
||||
| ObsidianDragon-2.0.0-Linux-x64.zip | `563004b4e45650dbfa9411d62fa1c59a3ca2199bd43a647b1614f2683bbdb5fa` |
|
||||
| ObsidianDragon-2.0.0.exe | `56100ae59dc3c67445d015cac5412ca40465bf459ba182c5ad2477a3b95ff548` |
|
||||
| ObsidianDragon-2.0.0-Windows-x64.zip | `ce9d067f36c5296288a473d3c2cceca7fcf807672a93a2084afc53c3b6e780d9` |
|
||||
| ObsidianDragon-2.0.0-macOS-universal.dmg | `8a01b6c0c2b5bebd64a222ba4a5ad04a1b8851138f28458b5c7f767fbb65db66` |
|
||||
| ObsidianDragon-2.0.0-macOS-universal.app.zip | `1bce1e5be15d9a2f3c6f42c95b23a0e89df142a73d65895252e4d8f50d19f541` |
|
||||
|
||||
## System Requirements
|
||||
|
||||
- **Linux:** x86-64, glibc 2.31+ (Ubuntu 20.04+, Fedora 33+, etc.)
|
||||
- **Windows:** x86-64, Windows 10 or later
|
||||
- **macOS:** Intel & Apple Silicon (universal binary), macOS 10.15 Catalina or later
|
||||
|
||||
## License
|
||||
|
||||
Released under the **GPLv3**. See [LICENSE](https://git.dragonx.is/DragonX/ObsidianDragon/src/branch/master/LICENSE) for details.
|
||||
Binary file not shown.
@@ -19,6 +19,7 @@
|
||||
"about_license_text": "Diese Software wird unter der GNU General Public License v3 (GPLv3) veröffentlicht. Sie dürfen diese Software gemäß den Lizenzbedingungen frei verwenden, modifizieren und verbreiten.",
|
||||
"about_peers_count": "%zu Peers",
|
||||
"about_release": "Release",
|
||||
"about_source": "Quelle",
|
||||
"about_title": "Über ObsidianDragon",
|
||||
"about_version": "Version:",
|
||||
"about_website": "Webseite",
|
||||
@@ -60,7 +61,84 @@
|
||||
"amount_label": "Betrag:",
|
||||
"animate_avatars": "Avatare animieren",
|
||||
"appearance": "ERSCHEINUNGSBILD",
|
||||
"appx_back": "Zurück",
|
||||
"appx_back_up_seed_phrase_title": "Sichern Sie Ihre Seed-Phrase",
|
||||
"appx_birthday_block_height": "Geburtstag (Blockhöhe): %llu — sichern Sie auch dies.",
|
||||
"appx_blockchain_data_deleted": "Blockchain-Daten gelöscht (%d Elemente). Der Daemon wird neu gestartet, um sich erneut mit dem Netzwerk zu synchronisieren.",
|
||||
"appx_blockchain_maintenance_in_progress": "Eine Blockchain-Wartung läuft bereits.",
|
||||
"appx_blockchain_rescan_complete": "Blockchain-Neuscan abgeschlossen",
|
||||
"appx_bootstrap_complete_reconciling": "Bootstrap abgeschlossen — Ihr Wallet wird mit den neuen Chain-Daten abgeglichen.",
|
||||
"appx_cancel": "Abbrechen",
|
||||
"appx_cleaning_up": "Aufräumen...",
|
||||
"appx_confirm_your_backup": "Backup bestätigen",
|
||||
"appx_copied_clipboard_autoclears": "Kopiert — Zwischenablage wird in 45s automatisch geleert",
|
||||
"appx_copy": "Kopieren",
|
||||
"appx_could_not_start_restore": "Wiederherstellung konnte nicht gestartet werden",
|
||||
"appx_create_failed_prefix": "Erstellen fehlgeschlagen: ",
|
||||
"appx_creating_your_wallet": "Ihr Wallet wird erstellt…",
|
||||
"appx_daemon_error": "Daemon-Fehler",
|
||||
"appx_daemon_reinstall_in_progress": "Die Daemon-Neuinstallation läuft bereits.",
|
||||
"appx_disconnecting": "Verbindung wird getrennt...",
|
||||
"appx_done": "Fertig",
|
||||
"appx_dragonxd_output": "dragonxd-Ausgabe",
|
||||
"appx_encrypting_wallet": "Wallet wird verschlüsselt...",
|
||||
"appx_fullnode_lifecycle_unavailable_lite": "Full-Node-Lebenszyklusaktionen sind im Lite-Build nicht verfügbar",
|
||||
"appx_installing_bundled_daemon": "Mitgelieferter Daemon wird installiert — der Node stoppt, aktualisiert und startet neu...",
|
||||
"appx_invalid_payment_uri_prefix": "Ungültige Zahlungs-URI: ",
|
||||
"appx_ive_written_it_down": "Ich habe sie notiert",
|
||||
"appx_keep_node_running_and_quit": "Node weiterlaufen lassen & beenden",
|
||||
"appx_last_block_n": "Letzter Block: %d",
|
||||
"appx_last_used_wallet_not_found_prefix": "Ihre zuletzt verwendete Wallet-Datei (",
|
||||
"appx_last_used_wallet_not_found_suffix": ") wurde nicht gefunden — stattdessen wurde das Standard-Wallet geöffnet. Wenn Sie sie verschoben haben, stellen Sie sie wieder her und wechseln Sie über die Wallet-Liste zurück.",
|
||||
"appx_low_spec_mode_disabled": "Low-Spec-Modus deaktiviert",
|
||||
"appx_low_spec_mode_enabled": "Low-Spec-Modus aktiviert",
|
||||
"appx_miner_stopped_prefix": "Miner gestoppt: ",
|
||||
"appx_miner_stopped_unexpectedly": "Miner wurde unerwartet gestoppt.",
|
||||
"appx_n_min_n_sec": "%d Min %d Sek",
|
||||
"appx_n_seconds": "%d Sekunden",
|
||||
"appx_no_bundled_daemon_to_install": "Dieser Build enthält keinen mitgelieferten Daemon zur Installation",
|
||||
"appx_no_embedded_daemon_to_install": "Dieser Build enthält keinen eingebetteten Daemon zur Installation",
|
||||
"appx_node_busy_restarting": "Der Node ist mit dem Neustart beschäftigt — versuchen Sie es gleich erneut.",
|
||||
"appx_node_rebuilding_witness_cache": "Node baut seinen Witness-Cache neu auf",
|
||||
"appx_not_next_word": " — das ist nicht das nächste Wort",
|
||||
"appx_payment_request_loaded": "Zahlungsanforderung geladen",
|
||||
"appx_pool_miner_connected_and_hashing": "Pool-Miner verbunden und aktiv.",
|
||||
"appx_progress_n_of_n": "Fortschritt: %d / %d",
|
||||
"appx_rebuilding_sapling_note_witnesses": "Sapling-Note-Witnesses werden neu aufgebaut…",
|
||||
"appx_rebuilding_witness_cache_blocks_left": "Witness-Cache wird neu aufgebaut %.0f%% — %d Blöcke verbleiben",
|
||||
"appx_rebuilding_witness_cache_pct": "Witness-Cache wird neu aufgebaut %.0f%%",
|
||||
"appx_recovery_phrase_word_count": "Die Wiederherstellungsphrase sollte 24 Wörter haben — Sie haben %d.",
|
||||
"appx_restarting_daemon_rescan_flag": "Daemon wird mit -rescan-Flag neu gestartet...",
|
||||
"appx_restarting_daemon_zapwallettxes": "Daemon wird mit -zapwallettxes=2 neu gestartet (Wallet-Reparatur)...",
|
||||
"appx_restoring_your_wallet": "Ihr Wallet wird wiederhergestellt…",
|
||||
"appx_seed_backup_warning": "Diese 24 Wörter sind die EINZIGE Möglichkeit, Ihr Wallet wiederherzustellen. Notieren Sie sie in der richtigen Reihenfolge, bewahren Sie sie offline auf und geben Sie sie niemals weiter. Wenn Sie sie verlieren, sind Ihre Guthaben für immer verloren.",
|
||||
"appx_seed_not_backed_up_warning": "Sie haben Ihren Seed nicht gesichert — Guthaben könnten verloren gehen. Trotzdem überspringen?",
|
||||
"appx_sending_stop_command_to_daemon": "Stopp-Befehl wird an Daemon gesendet...",
|
||||
"appx_setting_initial_sapling_witnesses": "Initiale Sapling-Witnesses werden gesetzt %.0f%%",
|
||||
"appx_shutdown_complete": "Herunterfahren abgeschlossen",
|
||||
"appx_simple_background_disabled": "Einfacher Hintergrund deaktiviert",
|
||||
"appx_simple_background_enabled": "Einfacher Hintergrund aktiviert",
|
||||
"appx_skip": "Überspringen",
|
||||
"appx_skip_anyway": "Trotzdem überspringen",
|
||||
"appx_still_status_prefix": "Weiterhin \"",
|
||||
"appx_still_status_suffix": "\" — ein erzwungenes Beenden jetzt kann die Chain-Daten beschädigen.",
|
||||
"appx_stop_anyway_and_quit": "Trotzdem stoppen & beenden",
|
||||
"appx_stopping_daemon_deleting_blockchain": "Daemon wird gestoppt und Blockchain-Daten werden gelöscht...",
|
||||
"appx_stopping_node_discards_rebuild": "Wenn Sie den Node jetzt stoppen, wird der laufende Neuaufbau verworfen und beim nächsten Öffnen des Wallets neu gestartet (mehrere Minuten). Sie können den Node stattdessen weiterlaufen lassen.",
|
||||
"appx_stopping_pool_miner": "Pool-Miner wird gestoppt...",
|
||||
"appx_syncing_pct_block_n_of_n": "Synchronisierung %.1f%% — Block %d / %d",
|
||||
"appx_tap_words_in_order": "Tippen Sie die Wörter in der richtigen Reihenfolge an, um zu bestätigen, dass Sie sie gespeichert haben.",
|
||||
"appx_theme_effects_disabled": "Design-Effekte deaktiviert",
|
||||
"appx_theme_effects_enabled": "Design-Effekte aktiviert",
|
||||
"appx_theme_prefix": "Design: ",
|
||||
"appx_use_settings_restart_daemon_hint": "Verwenden Sie Einstellungen > Daemon neu starten, um es erneut zu versuchen",
|
||||
"appx_waiting_for_daemon_to_encrypt_wallet": "Warten, bis der Daemon das Wallet verschlüsselt...",
|
||||
"appx_wallet_created_and_backed_up": "Wallet erstellt und gesichert.",
|
||||
"appx_wallet_open_failed_prefix": "Öffnen des Wallets fehlgeschlagen: ",
|
||||
"auto_shield": "Mining automatisch abschirmen",
|
||||
"autoshield_by_node": "Ihr Node schützt für Sie gemäß Coinbase automatisch.",
|
||||
"autoshield_off_backup_seed": "Geschürfte Coinbase-Coins werden noch nicht automatisch geschützt. Sichern Sie Ihre Wiederherstellungsphrase (Einstellungen → Sicherung & Daten), damit Ihr Node dies sicher aktivieren kann.",
|
||||
"autoshield_off_generic": "Ihr Node schützt geschürfte Coinbase-Coins derzeit nicht automatisch.",
|
||||
"av_intro": "Mining-Software wird oft als potenziell unerwünscht eingestuft. Führen Sie diese Schritte aus, um das Pool-Mining zu aktivieren:",
|
||||
"av_open_security": "Windows-Sicherheit öffnen",
|
||||
"av_step1": "Schritt 1: Ausnahme hinzufügen",
|
||||
@@ -100,6 +178,17 @@
|
||||
"balance_shielded_fmt": "Abgeschirmt: %.8f",
|
||||
"balance_syncing_pct": "Synchronisiere %.1f%%",
|
||||
"balance_transparent_fmt": "Transparent: %.8f",
|
||||
"baltab_market": "Markt",
|
||||
"baltab_market_price_4dp": "Markt: $%.4f",
|
||||
"baltab_market_price_8dp": "Markt: $%.8f",
|
||||
"baltab_pct_change_24h": "%s%.1f%% 24h",
|
||||
"baltab_pct_of_total_zaddr": "%.0f%% des Gesamtbetrags · %d Z-addr",
|
||||
"baltab_shielded": "Geschützt",
|
||||
"baltab_shielded_amount": "Geschützt %.8f",
|
||||
"baltab_t_addresses_count": "%d T-Adressen",
|
||||
"baltab_total_balance": "Gesamtguthaben",
|
||||
"baltab_transparent": "Transparent",
|
||||
"baltab_transparent_amount": "Transparent %.8f",
|
||||
"ban": "Sperren",
|
||||
"banned_peers": "Gesperrte Peers",
|
||||
"block": "Block",
|
||||
@@ -107,6 +196,12 @@
|
||||
"block_click_copy": "Klicken zum Kopieren",
|
||||
"block_click_next": "Klicken für nächsten Block",
|
||||
"block_click_prev": "Klicken für vorherigen Block",
|
||||
"block_db_reindex_body": "Dies passiert meist nach einem Daemon-Update, das das Format auf der Festplatte ändert, oder wenn der Block-Index beschädigt ist. Ihre Wallet und Ihre Coins sind sicher — der Node kann die Chain nur nicht laden, daher wird das Guthaben mit null angezeigt.\n\nDas Neuaufbauen liest Ihre vorhandenen Block-Dateien erneut ein und kann eine Weile dauern (dabei wird auch Ihre Wallet erneut gescannt). Es wird nichts heruntergeladen.",
|
||||
"block_db_reindex_confirm": "Block-Datenbank neu aufbauen",
|
||||
"block_db_reindex_notify": "Der Node kann seine Block-Datenbank nicht lesen (oft nach einem Daemon-Update). Bauen Sie sie neu auf, um Ihr Guthaben wiederherzustellen — siehe die Aufforderung oder Einstellungen › Node.",
|
||||
"block_db_reindex_started": "Die Block-Datenbank wird aus Ihren Blöcken neu aufgebaut — dies kann eine Weile dauern.",
|
||||
"block_db_reindex_title": "Block-Datenbank neu aufbauen?",
|
||||
"block_db_reindex_warn": "Der Node kann seine Block-Datenbank nicht lesen.",
|
||||
"block_explorer": "Block-Explorer",
|
||||
"block_get_info": "Block-Info abrufen",
|
||||
"block_hash": "Block-Hash:",
|
||||
@@ -154,6 +249,10 @@
|
||||
"chat_accent_theme": "Design",
|
||||
"chat_add_contact": "Kontakt hinzufügen",
|
||||
"chat_awaiting_key": "Warten auf Antwort",
|
||||
"chat_blocked_desc": "Blockierte Unterhaltungen werden entfernt und ihre Nachrichten verworfen — alte wie neue — bis Sie die Blockierung aufheben. Beim Aufheben wird die Unterhaltung erneut aus der Chain importiert.",
|
||||
"chat_blocked_manage": "Blockiert",
|
||||
"chat_blocked_title": "Blockierte Unterhaltungen",
|
||||
"chat_blocked_toast": "Unterhaltung gelöscht & blockiert",
|
||||
"chat_bubble_minimal": "Minimal",
|
||||
"chat_bubble_rounded": "Abgerundet",
|
||||
"chat_bubble_square": "Eckig",
|
||||
@@ -166,6 +265,16 @@
|
||||
"chat_contact_added": "Kontakt hinzugefügt – benenne ihn in Kontakte um",
|
||||
"chat_contact_request": "kontaktanfrage",
|
||||
"chat_copy_address_tip": "Zum Kopieren der Adresse klicken",
|
||||
"chat_delete": "Unterhaltung löschen",
|
||||
"chat_delete_block": "Löschen & blockieren",
|
||||
"chat_delete_body_prefix": "Ihre lokale Kopie der Unterhaltung mit ",
|
||||
"chat_delete_body_suffix": " löschen?",
|
||||
"chat_delete_confirm": "Löschen",
|
||||
"chat_delete_failed": "Die Unterhaltung konnte nicht gelöscht werden — es wurde nichts geändert.",
|
||||
"chat_delete_local_note": "Dies betrifft nur dieses Gerät — die Nachrichten bleiben auf der Blockchain, und die andere Person behält ihre Kopie.",
|
||||
"chat_delete_revive_note": "„Löschen“ entfernt den Verlauf auf diesem Gerät. Wenn Ihnen die Person erneut schreibt, kommt die Unterhaltung zurück.",
|
||||
"chat_delete_title": "Unterhaltung löschen?",
|
||||
"chat_deleted_toast": "Unterhaltung gelöscht",
|
||||
"chat_density_comfortable": "Komfortabel",
|
||||
"chat_density_compact": "Kompakt",
|
||||
"chat_emoji_color": "Farbig",
|
||||
@@ -189,6 +298,7 @@
|
||||
"chat_new_button": "Neuer Chat",
|
||||
"chat_new_message": "Nachricht",
|
||||
"chat_new_message_toast": "Neue verschlüsselte Chat-Nachricht",
|
||||
"chat_new_needs_zaddr": "Chat benötigt eine geschützte (z-)Adresse — transparente (t-)Adressen können keine verschlüsselten Nachrichten empfangen.",
|
||||
"chat_new_send": "Anfrage senden",
|
||||
"chat_new_title": "Neuer Chat",
|
||||
"chat_new_zaddr": "z-Adresse des Empfängers",
|
||||
@@ -234,6 +344,8 @@
|
||||
"chat_ts_24h": "24-Stunden",
|
||||
"chat_ts_global": "Global folgen",
|
||||
"chat_ts_global_short": "Global",
|
||||
"chat_unblock": "Blockierung aufheben",
|
||||
"chat_unblocked_toast": "Blockierung der Unterhaltung aufgehoben",
|
||||
"chat_unhide": "Einblenden",
|
||||
"chat_unmute": "Stummschaltung aufheben",
|
||||
"chat_verify_key": "Identitätsschlüssel – zum Verifizieren vergleichen",
|
||||
@@ -286,6 +398,7 @@
|
||||
"console": "Konsole",
|
||||
"console_accents": "Farbakzente",
|
||||
"console_app": "App",
|
||||
"console_auto_focus": "Eingabe beim Öffnen fokussieren",
|
||||
"console_auto_scroll": "Automatisch scrollen",
|
||||
"console_available_commands": "Verfügbare Befehle:",
|
||||
"console_backend_reference": "Backend-Befehlsreferenz",
|
||||
@@ -380,11 +493,15 @@
|
||||
"console_tab_completion": "Tab zur Vervollständigung",
|
||||
"console_text_colors": "Textfarben",
|
||||
"console_toggle_accents": "Farbakzente der Zeilen umschalten",
|
||||
"console_toggle_auto_focus": "Den Cursor beim Öffnen des Konsolen-Tabs im Befehlsfeld platzieren",
|
||||
"console_toggle_text_color": "Textfarben der Zeilen umschalten",
|
||||
"console_type_help": "Geben Sie 'help' ein für verfügbare Befehle",
|
||||
"console_welcome": "Willkommen bei ObsidianDragon Konsole",
|
||||
"console_zoom_in": "Vergrößern",
|
||||
"console_zoom_out": "Verkleinern",
|
||||
"consolidate_desc": "Fassen Sie viele kleine Eingaben zu einer einzigen geschützten Note zusammen. Weniger Notes bedeuten eine kleinere Wallet-Datei und mehr Privatsphäre.",
|
||||
"consolidate_funds_btn": "Konsolidieren",
|
||||
"consolidate_title": "Guthaben konsolidieren",
|
||||
"contact_avatar": "AVATAR",
|
||||
"contact_avatar_bad_image": "Dieses Bild konnte nicht geladen werden.",
|
||||
"contact_avatar_badge": "Abzeichen",
|
||||
@@ -414,6 +531,7 @@
|
||||
"contacts_shape_square": "Quadrat",
|
||||
"contacts_shape_tab": "Reiter",
|
||||
"copied": "Kopiert!",
|
||||
"copied_to_clipboard": "In die Zwischenablage kopiert",
|
||||
"copy": "Kopieren",
|
||||
"copy_address": "Vollständige Adresse kopieren",
|
||||
"copy_error": "Fehler kopieren",
|
||||
@@ -429,9 +547,13 @@
|
||||
"daemon_maintenance_label": "WARTUNG",
|
||||
"daemon_none_bundled": "keiner in diesem Build",
|
||||
"daemon_not_installed": "nicht installiert",
|
||||
"daemon_status_diff": "Version weicht ab",
|
||||
"daemon_status_differ": "Installierte Binärdatei unterscheidet sich von der gebündelten Version.",
|
||||
"daemon_status_match": "Installierte Binärdatei entspricht der gebündelten Version.",
|
||||
"daemon_status_missing": "Kein Daemon installiert — installieren Sie die mitgelieferte Version.",
|
||||
"daemon_status_none": "Nicht installiert",
|
||||
"daemon_status_ok": "Aktuell",
|
||||
"daemon_update_active_tt": "Der derzeit installierte (aktive) Node",
|
||||
"daemon_update_available": "Eine neue Node-Version ist verfügbar",
|
||||
"daemon_update_body": "Dieser Wallet-Build enthält einen neueren DragonX-Node als den derzeit installierten. Beim Aktualisieren wird der installierte dragonxd (sowie dragonx-cli/dragonx-tx) ersetzt und der Node anschließend gestoppt und neu gestartet, damit die neue Version wirksam wird. Empfohlen — ein neuerer Node kann Funktionen (z. B. Unterstützung für Wiederherstellungsphrasen) bieten, die der alte nicht hat.",
|
||||
"daemon_update_browse": "Alle Releases durchsuchen…",
|
||||
@@ -441,19 +563,28 @@
|
||||
"daemon_update_download_install": "Herunterladen & installieren",
|
||||
"daemon_update_downloading": "Wird heruntergeladen…",
|
||||
"daemon_update_failed": "Update fehlgeschlagen",
|
||||
"daemon_update_install_this": "Diese Version installieren",
|
||||
"daemon_update_installed": "Installiert:",
|
||||
"daemon_update_installed_ok": "Node installiert",
|
||||
"daemon_update_installing": "Wird installiert…",
|
||||
"daemon_update_keep": "Aktuellen behalten",
|
||||
"daemon_update_later": "Später",
|
||||
"daemon_update_latest": "Neueste:",
|
||||
"daemon_update_latest_badge": "neueste",
|
||||
"daemon_update_latest_tt": "Neueste verfügbare Version",
|
||||
"daemon_update_loading": "Releases werden geladen…",
|
||||
"daemon_update_no_notes": "Keine Versionshinweise für diese Version.",
|
||||
"daemon_update_now": "Jetzt aktualisieren",
|
||||
"daemon_update_prerelease_tt": "Vorabversion / Testbuild",
|
||||
"daemon_update_prompt_title": "Node-Daemon aktualisieren?",
|
||||
"daemon_update_reinstall": "Neu installieren",
|
||||
"daemon_update_released": "Veröffentlicht:",
|
||||
"daemon_update_restart_note": "Starten Sie den Daemon neu, um die neue Version auszuführen.",
|
||||
"daemon_update_restart_now": "Daemon jetzt neu starten",
|
||||
"daemon_update_safe": "Ihre Wallet, Ihre Schlüssel und Ihre Blockchain-Daten bleiben unberührt — nur die Programmdateien des Daemons werden ersetzt. Wenn Sie bewusst einen eigenen Node betreiben, wählen Sie „Aktuellen behalten“.",
|
||||
"daemon_update_status_current": "Diese Version ist installiert",
|
||||
"daemon_update_status_latest": "Neueste Version",
|
||||
"daemon_update_status_older": "Älter als Ihre installierte Version",
|
||||
"daemon_update_title": "Node-Daemon aktualisieren?",
|
||||
"daemon_update_unavailable_body": "Für diese Plattform ist kein Node-Build verfügbar.",
|
||||
"daemon_update_unavailable_title": "Node-Updates nicht verfügbar",
|
||||
@@ -469,6 +600,13 @@
|
||||
"data_stale_tooltip": "Der Kontostand ist möglicherweise veraltet – die Wallet hat kürzlich keine Aktualisierung erhalten. Überprüfe deine Node-Verbindung.",
|
||||
"date": "Datum",
|
||||
"date_label": "Datum:",
|
||||
"debug_gate_confirm": "Debug-Optionen anzeigen",
|
||||
"debug_gate_incorrect": "Falsche PIN oder Passphrase.",
|
||||
"debug_gate_pass_prompt": "Geben Sie Ihre Wallet-Passphrase ein, um fortzufahren:",
|
||||
"debug_gate_pin_prompt": "Geben Sie Ihre Entsperr-PIN ein, um fortzufahren:",
|
||||
"debug_gate_title": "Debug-Optionen anzeigen?",
|
||||
"debug_gate_verifying": "Wird überprüft…",
|
||||
"debug_gate_warning": "Dies sind erweiterte Diagnose- und Node-Feinabstimmungsoptionen. Sie können vertrauliche Informationen offenlegen und die Funktionsweise Ihres Nodes verändern. Fahren Sie nur fort, wenn Sie wissen, was Sie tun.",
|
||||
"debug_logging": "FEHLERPROTOKOLLIERUNG",
|
||||
"decrypt_desc": "Die Wallet wird exportiert, der Daemon mit einer frischen, unverschlüsselten Wallet neu gestartet und alle Schlüssel werden erneut importiert. Je nach Wallet-Größe kann dies einige Minuten dauern.",
|
||||
"decrypt_error_title": "Entschlüsselung fehlgeschlagen",
|
||||
@@ -491,6 +629,7 @@
|
||||
"disconnected": "Getrennt",
|
||||
"dismiss": "Verwerfen",
|
||||
"display": "Anzeige",
|
||||
"done": "Fertig",
|
||||
"download": "Herunterladen",
|
||||
"download_bootstrap": "Bootstrap herunterladen",
|
||||
"dragonx_green": "DragonX (Grün)",
|
||||
@@ -544,6 +683,7 @@
|
||||
"explorer_tx_outputs": "Ausgaben",
|
||||
"explorer_tx_size": "Größe",
|
||||
"explorer_txid": "TxID:",
|
||||
"explorer_urls_hdr": "URLS",
|
||||
"export": "Exportieren",
|
||||
"export_csv": "CSV exportieren",
|
||||
"export_keys_btn": "Schlüssel exportieren",
|
||||
@@ -571,6 +711,151 @@
|
||||
"export_viewing_key": "Betrachtungsschlüssel exportieren",
|
||||
"failed_create_shielded": "Abgeschirmte Adresse konnte nicht erstellt werden",
|
||||
"failed_create_transparent": "Transparente Adresse konnte nicht erstellt werden",
|
||||
"faq": "FAQ",
|
||||
"faq_d_mgmt_1_a": "Der eingebettete Node startet automatisch mit der Wallet und stoppt bei Bedarf, sodass Sie ihn normalerweise nicht manuell verwalten müssen. Erweiterte Steuerelemente (einschließlich des Neustarts des Daemons) finden Sie unter Einstellungen > Node & Sicherheit.",
|
||||
"faq_d_mgmt_1_q": "Wie starte oder stoppe ich den Node?",
|
||||
"faq_d_mgmt_2_a": "Wenn Sie die Wallet mit einem selbst gestarteten Node verbinden, entscheidet diese Option, ob das Schließen der Wallet auch diesen Node beendet. Lassen Sie sie deaktiviert, wenn Ihr Node nach dem Schließen der Wallet weiterlaufen soll.",
|
||||
"faq_d_mgmt_2_q": "Was bewirkt „externen Daemon beim Beenden schließen“?",
|
||||
"faq_d_mgmt_3_a": "Node- und Wallet-Daten befinden sich im DragonX-Datenverzeichnis unter Ihrem Benutzerprofil (zum Beispiel in AppData unter Windows oder Ihrem Home-Ordner unter Linux/macOS). Die Blockchain ist der große Teil davon; halten Sie genug freien Speicherplatz für ihr Wachstum bereit.",
|
||||
"faq_d_mgmt_3_q": "Wo werden die Blockchain- und Wallet-Daten gespeichert?",
|
||||
"faq_d_mgmt_title": "Node-Verwaltung",
|
||||
"faq_d_mine_1_a": "Im Tab „Mining“ können Sie mit Ihrer CPU minen. DragonX verwendet den RandomX-Algorithmus, der für gewöhnliche Prozessoren ausgelegt ist. Stellen Sie die Anzahl der Threads ein und starten Sie — Belohnungen fließen in Ihre Wallet.",
|
||||
"faq_d_mine_1_q": "Wie mine ich DragonX?",
|
||||
"faq_d_mine_2_a": "Solo-Mining sendet Belohnungen direkt an Sie, zahlt aber nur, wenn Sie einen Block finden — das ist selten, sofern Sie nicht über viel Hashrate verfügen. Pool-Mining teilt die Arbeit mit anderen für kleinere, aber gleichmäßigere Auszahlungen. Der Mining-Tab unterstützt beides.",
|
||||
"faq_d_mine_2_q": "Solo- oder Pool-Mining — was soll ich wählen?",
|
||||
"faq_d_mine_3_a": "Im Pool-Bereich des Mining-Tabs gibt es die Schaltfläche „Miner aktualisieren…“, die den neuesten optimierten DragonX-Miner herunterlädt, verifiziert und installiert. Wie beim Node-Updater wird jeder Download vor der Installation per Prüfsumme und Signatur verifiziert.",
|
||||
"faq_d_mine_3_q": "Wie aktualisiere ich den Miner?",
|
||||
"faq_d_mine_4_a": "Der aktuelle Node kann einen integrierten Stratum-Server betreiben, sodass andere Miner sich mit Ihrem Rechner verbinden können. Dafür gibt es einen Schalter unter Einstellungen > Node & Sicherheit. Standardmäßig lauscht er nur lokal; ihn für andere Rechner zu öffnen, erfordert bewusstes Freischalten.",
|
||||
"faq_d_mine_4_q": "Kann ich einen Mining-Pool hosten?",
|
||||
"faq_d_mine_title": "Mining & Pools",
|
||||
"faq_d_net_1_a": "Direkt nach dem Start braucht der Node einen Moment, um Peers zu finden — ein kurzzeitiges „0 Peers“ ist also normal. Hält es an, prüfen Sie Ihre Internetverbindung und ob eine Firewall den Node blockiert. Der Node findet Peers automatisch über eingebaute Seeds.",
|
||||
"faq_d_net_1_q": "Die Wallet zeigt keine Peers oder Verbindungen an — was ist los?",
|
||||
"faq_d_net_2_a": "Ja — DragonX-Nodes kommunizieren über TLS miteinander, Peer-Verbindungen sind also verschlüsselt. Auch die Wallet spricht mit ihrem eigenen Node über einen sicheren lokalen Kanal.",
|
||||
"faq_d_net_2_q": "Ist meine Verbindung zum Netzwerk verschlüsselt?",
|
||||
"faq_d_net_title": "Peers & Netzwerk",
|
||||
"faq_d_node_1_a": "Der Daemon, dragonxd, ist der DragonX Full Node. Er lädt und verifiziert die gesamte Blockchain, leitet Transaktionen an das Netzwerk weiter und beantwortet die Anfragen der Wallet. Einen eigenen Node zu betreiben bedeutet, dass Sie keinem fremden Server vertrauen müssen.",
|
||||
"faq_d_node_1_q": "Was ist der Daemon (dragonxd)?",
|
||||
"faq_d_node_2_a": "Standardmäßig startet und verwaltet die Wallet ihren eigenen mitgelieferten Node (eingebettet) — Sie müssen nichts tun. Wenn Sie dragonxd bereits selbst betreiben, kann die Wallet sich stattdessen mit diesem externen Node verbinden. Die Wallet erkennt automatisch, welche Situation vorliegt, und verhält sich entsprechend.",
|
||||
"faq_d_node_2_q": "Eingebetteter vs. externer Node — was ist der Unterschied?",
|
||||
"faq_d_node_3_a": "Ein Full Node prüft jede Regel der Blockchain eigenständig, sodass Ihre Wallet der Mathematik vertraut statt einem Dritten. Das stärkt außerdem das Netzwerk. Der Preis dafür sind Speicherplatz und eine anfängliche Synchronisierung.",
|
||||
"faq_d_node_3_q": "Warum überhaupt einen Full Node betreiben?",
|
||||
"faq_d_node_title": "Der Full Node",
|
||||
"faq_d_perf_1_a": "Die Blockchain ist der größte Posten und wächst mit der Zeit, halten Sie also mehrere Gigabyte frei. Der Node nutzt für mehr Geschwindigkeit einen Datenbank-Cache; die mitgelieferten Standardwerte sind auf typische Rechner abgestimmt. Fortgeschrittene Nutzer können den Datenbank-Cache des Nodes anpassen, indem sie dbcache in der Konfigurationsdatei (DRAGONX.conf) bearbeiten.",
|
||||
"faq_d_perf_1_q": "Wie viel Speicherplatz nutzt der Node, und kann ich den Speicherverbrauch anpassen?",
|
||||
"faq_d_perf_2_a": "Aktivieren Sie den Bootstrap-Download (im Einrichtungsassistenten angeboten), um eine aktuelle, verifizierte Kopie der Chain zu laden, statt jeden Block von Grund auf zu prüfen. Auch eine schnelle Festplatte (SSD) hilft erheblich.",
|
||||
"faq_d_perf_2_q": "Wie beschleunige ich die anfängliche Synchronisierung?",
|
||||
"faq_d_perf_title": "Speicher & Leistung",
|
||||
"faq_d_sync_1_a": "Beim Start lädt und prüft der Node Blöcke, bevor er die Wallet bedienen kann. Der Bildschirm „Blöcke werden verarbeitet / Blöcke werden angewendet, um den aktuellen Chain-Status aufzubauen“ ist dieses Aufwärmen. Das ist normal; die Wallet wird nutzbar, sobald der Node fertig ist und seine Höhe meldet.",
|
||||
"faq_d_sync_1_q": "Was bedeutet „Blöcke werden verarbeitet“ beim Start?",
|
||||
"faq_d_sync_2_a": "Für Ihre geschützten Guthaben führt der Node kryptografische „Witnesses“, die private Ausgaben ermöglichen. Nach bestimmten Neustarts oder Updates baut er diesen Cache neu auf, indem er aktuelle Blöcke erneut durchsucht. Bei einer Wallet mit vielen Transaktionen kann das mehrere Minuten dauern und zusätzlichen Speicher benötigen.\n\nSie hängt dabei nicht fest — lassen Sie sie fertig werden. Ein erzwungenes Beenden mittendrin führt nur dazu, dass der Neuaufbau beim nächsten Mal von vorn beginnt.",
|
||||
"faq_d_sync_2_q": "Was bedeutet „Witness-Cache wird neu aufgebaut“?",
|
||||
"faq_d_sync_3_a": "Nahe der Chain-Spitze konkurriert die Arbeit, die Ihre Wallet durchsucht (Guthaben und Notes prüfen), mit dem Node um dieselbe interne Sperre, was das Verbinden der letzten Blöcke bei einer großen Wallet verlangsamen kann. Neuere Versionen drosseln dieses Durchsuchen, damit der Node aufholen kann. Auf einem leichteren Tab zu bleiben (oder einfach zu warten) lässt es schneller fertig werden.",
|
||||
"faq_d_sync_3_q": "Warum ist die Synchronisierung langsam, obwohl ich fast aufgeholt habe?",
|
||||
"faq_d_sync_4_a": "Die erste Synchronisierung lädt und prüft die gesamte Chain und kann je nach Verbindung und Festplatte eine Weile dauern. Sie können sie deutlich beschleunigen, indem Sie im Einrichtungsassistenten den Bootstrap-Download aktivieren, der eine aktuelle, verifizierte Kopie der Chain-Daten lädt.",
|
||||
"faq_d_sync_4_q": "Wie lange dauert die anfängliche Synchronisierung?",
|
||||
"faq_d_sync_title": "Synchronisierung & Blockchain",
|
||||
"faq_d_trbl_1_a": "Normalerweise nicht. Bei einer großen Wallet kann der Node mehrere Minuten damit verbringen, geschützte Witnesses neu aufzubauen oder Blöcke anzuwenden — währenddessen wirkt er pausiert und reagiert möglicherweise langsam. Geben Sie ihm Zeit — das Debug-Log (Einstellungen) zeigt stetigen Fortschritt, wenn er arbeitet.\n\nVermeiden Sie ein erzwungenes Beenden während dieser Phase; es startet die Arbeit beim nächsten Start von vorn.",
|
||||
"faq_d_trbl_1_q": "Der Node scheint bei „Beste Chain wird aktiviert“ hängen zu bleiben — ist er eingefroren?",
|
||||
"faq_d_trbl_2_a": "Wenn eine Wallet-Datei wiederhergestellt wurde, aber ein Teil ihrer Schlüsseldaten fehlt, öffnet der Node sie möglicherweise in einem eingeschränkten „degraded“-Modus, in dem er keine neuen Adressen ableiten oder Guthaben schützen kann. Die sicherste Lösung ist, aus Ihrer Seed-Wiederherstellungsphrase eine frische Wallet wiederherzustellen.",
|
||||
"faq_d_trbl_2_q": "Was ist der „degraded“-Modus?",
|
||||
"faq_d_trbl_3_a": "Ein Re-Scan lässt die Wallet die Chain erneut lesen, um Ihre Transaktionen wiederzufinden (nützlich nach dem Import von Schlüsseln). Ein Re-Index baut die Blockchain-Datenbank des Nodes neu auf und ist nur nötig, wenn diese Datenbank nach einem Versionswechsel beschädigt oder inkompatibel ist. Beides kann eine Weile dauern; starten Sie es unter Einstellungen > Node & Sicherheit.",
|
||||
"faq_d_trbl_3_q": "Wann sollte ich re-indizieren oder re-scannen?",
|
||||
"faq_d_trbl_4_a": "Der Speicherverbrauch steigt bei intensiver Arbeit wie einem Witness-Neuaufbau oder einer anfänglichen Synchronisierung stark an und fällt danach wieder ab. Anhaltend hoher Speicherverbrauch im Leerlauf ist ungewöhnlich — ein Neustart der Wallet behebt das. Stellen Sie auf einem Rechner mit wenig RAM sicher, dass keine anderen speicherhungrigen Apps konkurrieren.",
|
||||
"faq_d_trbl_4_q": "Der Node nutzt viel Speicher — ist das normal?",
|
||||
"faq_d_trbl_title": "Problembehandlung",
|
||||
"faq_d_upd_1_a": "Öffnen Sie Einstellungen > Node & Sicherheit > Daemon-Binärdatei und nutzen Sie „Nach Updates suchen…“. Die Wallet lädt den neuesten verifizierten Node-Build herunter und installiert ihn; die neue Version wird beim nächsten Start des Daemons wirksam.",
|
||||
"faq_d_upd_1_q": "Wie aktualisiere ich den Node?",
|
||||
"faq_d_upd_2_a": "Ja — der Update-Dialog listet jede Version auf, sodass Sie einen bestimmten oder älteren Build wählen können. Seien Sie beim Downgrade vorsichtig: Ein älterer Node akzeptiert möglicherweise keine von einem neueren geschriebenen Blockchain-Daten und benötigt eventuell einen Re-Index.",
|
||||
"faq_d_upd_2_q": "Kann ich eine bestimmte Node-Version installieren?",
|
||||
"faq_d_upd_3_a": "Jedes heruntergeladene Node-Archiv wird vor der Installation gegen eine Prüfsumme und eine kryptografische Signatur verifiziert. Schlägt die Verifizierung fehl, verweigert die Wallet das Update.",
|
||||
"faq_d_upd_3_q": "Ist das Update sicher?",
|
||||
"faq_d_upd_title": "Node aktualisieren",
|
||||
"faq_group_daemon": "Daemon",
|
||||
"faq_group_wallet": "Wallet",
|
||||
"faq_intro": "Antworten zu Ihrer Wallet und dem DragonX-Node. Suchen Sie oder blättern Sie unten nach Thema.",
|
||||
"faq_intro_lite": "Antworten zu Ihrer Lite-Wallet. Suchen Sie oder blättern Sie unten nach Thema.",
|
||||
"faq_l_gs_1_a": "ObsidianDragonLite ist die schlanke DragonX-Wallet. Statt einen Full Node zu betreiben, verbindet sie sich mit einem DragonX-Lite-Server, startet also schnell und lädt nicht die gesamte Blockchain herunter.\n\nSie verwaltet Ihre Schlüssel, Guthaben und Sendevorgänge genau wie die vollständige Wallet — siehe den Abschnitt „Lite-Wallet“ unten für die Funktionsweise der Serververbindung und den Kompromiss bei der Privatsphäre.",
|
||||
"faq_l_gs_1_q": "Was ist ObsidianDragonLite?",
|
||||
"faq_l_lite_1_a": "Die Lite-Wallet spricht mit einem DragonX-Lite-Server, statt einen eigenen Full Node zu betreiben. Das macht sie schnell startbereit und schont Speicherplatz und Bandbreite, da sie die gesamte Blockchain nicht selbst herunterlädt oder prüft. Ihre privaten Schlüssel verbleiben stets auf Ihrem Gerät — sie werden nie an den Server gesendet.",
|
||||
"faq_l_lite_1_q": "Wie unterscheidet sich die Lite-Wallet von der vollständigen Wallet?",
|
||||
"faq_l_lite_2_a": "Öffnen Sie Einstellungen und wählen Sie einen Server. Sie können jederzeit den Server wechseln, die Wallet synchronisiert sich dann neu mit dem neuen Server. Ist ein Server langsam oder nicht erreichbar, versuchen Sie einen anderen.",
|
||||
"faq_l_lite_2_q": "Wie wähle oder ändere ich den Server?",
|
||||
"faq_l_lite_3_a": "Ihre Schlüssel verlassen niemals Ihr Gerät, und geschützte Beträge bleiben verschlüsselt. Der Lite-Server sieht jedoch, welche Adressen Ihre Wallet abfragt, sowie Ihre IP-Adresse, wodurch er Ihre Adressen miteinander verknüpfen kann — ein Kompromiss zwischen Komfort und Privatsphäre. Für maximale Privatsphäre nutzen Sie die Full-Node-Wallet, die die Kette selbst verifiziert und Ihre Adressen keinem Server preisgibt.",
|
||||
"faq_l_lite_3_q": "Ist die Lite-Wallet genauso privat wie der Full Node?",
|
||||
"faq_l_lite_title": "Lite-Wallet",
|
||||
"faq_l_sec_1_a": "Öffnen Sie Einstellungen > Wallet und legen Sie im Sicherheitsbereich eine Passphrase fest. Die Verschlüsselung schützt Ihre privaten Schlüssel auf der Festplatte, sodass jemand mit Zugriff auf die Dateien Ihre Coins trotzdem nicht ausgeben kann.\n\nWählen Sie eine starke Passphrase und verlieren Sie sie nicht — ohne sie gibt es keine Möglichkeit, eine verschlüsselte Wallet wiederherzustellen.",
|
||||
"faq_no_results": "Keine Ergebnisse. Versuchen Sie einen anderen Suchbegriff.",
|
||||
"faq_open_tooltip": "Hilfe & FAQ",
|
||||
"faq_search_hint": "FAQ durchsuchen…",
|
||||
"faq_title": "Hilfe & FAQ",
|
||||
"faq_w_addr_1_a": "Transparente Adressen (sie beginnen mit R oder t) funktionieren wie bei den meisten Coins: Beträge und Adressen sind öffentlich auf der Blockchain einsehbar.\n\nGeschützte Adressen (z-Adressen, beginnend mit zs) nutzen Zero-Knowledge-Kryptografie — Betrag, Absender und Empfänger sind on-chain verschlüsselt. Guthaben auf geschützten Adressen sind privat.",
|
||||
"faq_w_addr_1_q": "Was ist der Unterschied zwischen transparenten und geschützten Adressen?",
|
||||
"faq_w_addr_2_a": "Bevorzugen Sie wann immer möglich geschützte (z-)Adressen — sie halten Ihr Guthaben und Ihren Zahlungsverlauf privat. DragonX ist eine Privacy-Coin, und geschützt ist die Standardeinstellung für empfangene Gelder.\n\nVerwenden Sie eine transparente Adresse nur, wenn ein Dienst, mit dem Sie interagieren, keine geschützten Adressen verarbeiten kann.",
|
||||
"faq_w_addr_2_q": "Welchen Adresstyp sollte ich verwenden?",
|
||||
"faq_w_addr_3_a": "Bei geschützten Adressen ist die Wiederverwendung unbedenklich und gibt keinen Verlauf preis. Bei transparenten Adressen verbessert eine neue Adresse pro Zahlung die Privatsphäre. Neue Adressen erstellen Sie im Tab Empfangen.",
|
||||
"faq_w_addr_3_q": "Sollte ich eine Adresse wiederverwenden oder eine neue erstellen?",
|
||||
"faq_w_addr_4_a": "Beim Schützen werden Coins von einer transparenten Adresse in eine geschützte verschoben und dadurch privat gemacht. Neu geschürfte Coins kommen transparent an und werden automatisch für Sie geschützt.\n\nSie können auch manuell aus der Wallet heraus schützen; ein Schutzvorgang ist einfach eine spezielle Transaktion und benötigt daher die normale Bestätigungszeit.",
|
||||
"faq_w_addr_4_q": "Was bedeutet „Schützen“?",
|
||||
"faq_w_addr_title": "Adressen & Privatsphäre",
|
||||
"faq_w_bal_1_a": "Solange Ihre Wallet noch synchronisiert, ist Ihr Guthaben unvollständig und kann 0 anzeigen — es wurden noch nicht alle Transaktionen gescannt. Es füllt sich, sobald die Synchronisierung abgeschlossen ist.\n\nBeobachten Sie die Statusleiste unten: Sie zeigt den Verbindungsstatus und die Blockhöhe an. Sobald die Synchronisierung abgeschlossen ist, ist Ihr Guthaben korrekt.",
|
||||
"faq_w_bal_1_q": "Warum ist mein Guthaben 0 oder aktualisiert sich nicht?",
|
||||
"faq_w_bal_2_a": "Jeder neue Block, der auf den Block mit Ihrer Transaktion folgt, fügt eine Bestätigung hinzu. Mehr Bestätigungen bedeuten, dass die Zahlung fester verankert ist. Empfangene Gelder werden nach der ersten Bestätigung ausgabefähig.",
|
||||
"faq_w_bal_2_q": "Was sind Bestätigungen?",
|
||||
"faq_w_bal_3_a": "Das Gesamtguthaben umfasst Gelder, die noch bestätigt werden oder vorübergehend gesperrt sind (zum Beispiel Coins, die gerade geschützt werden). Verfügbar ist das, was Sie jetzt sofort senden können. Beide Werte gleichen sich an, sobald Transaktionen bestätigt werden.",
|
||||
"faq_w_bal_3_q": "Was ist der Unterschied zwischen Gesamt- und verfügbarem Guthaben?",
|
||||
"faq_w_bal_title": "Guthaben & Synchronisierung",
|
||||
"faq_w_chat_1_a": "Chat ist ein verschlüsselter, on-chain-basierter Messenger, der in die Wallet integriert ist. Nachrichten werden als private, geschützte Memos gesendet, sodass nur Sie und Ihr Kontakt sie lesen können.",
|
||||
"faq_w_chat_1_q": "Was ist die Chat-Funktion?",
|
||||
"faq_w_chat_2_a": "Ihre Chat-Identität wird aus der Wiederherstellungsphrase Ihrer Wallet abgeleitet, sie ist also an Ihre Wallet gebunden — stellen Sie die Wallet wieder her, und Ihre Identität kehrt zurück. Sie erstellen kein separates Konto oder Passwort.",
|
||||
"faq_w_chat_2_q": "Wie funktioniert meine Chat-Identität?",
|
||||
"faq_w_chat_3_a": "Fügen Sie einen Kontakt über dessen Adresse im Tab Kontakte hinzu, optional mit Namen und Avatar. Kontakte können privat für die aktuelle Wallet bleiben oder über alle Wallets hinweg geteilt werden, die Sie auf diesem Computer öffnen.",
|
||||
"faq_w_chat_3_q": "Wie funktionieren Kontakte?",
|
||||
"faq_w_chat_4_a": "Öffnen Sie eine Unterhaltung und verwenden Sie die Symbole in deren Kopfzeile. Ausblenden (das Auge) entfernt sie aus der Liste, behält aber alle Nachrichten — eine neue Nachricht blendet sie wieder ein. Löschen (der Papierkorb) öffnet einen Dialog: „Löschen“ entfernt den Verlauf auf diesem Gerät (schreibt Ihnen die Person erneut, kommt die Unterhaltung zurück), während „Löschen & blockieren“ zusätzlich zukünftige Nachrichten stoppt, bis Sie die Person aus der Liste „Blockiert“ oberhalb der Unterhaltungsliste entblocken. All dies gilt nur lokal für dieses Gerät — die Nachrichten bleiben auf der Blockchain, und die andere Person behält ihre eigene Kopie; es ist also kein „Zurückrufen“.",
|
||||
"faq_w_chat_4_q": "Wie blende ich eine Unterhaltung aus, lösche oder blockiere sie?",
|
||||
"faq_w_chat_title": "Chat & Kontakte",
|
||||
"faq_w_gs_1_a": "ObsidianDragon ist eine Full-Node-Wallet für DragonX (DRGX). Sie verwaltet Ihre Coins und betreibt im Desktop-Build im Hintergrund einen DragonX-Full-Node (den Daemon), sodass Ihre Wallet die Blockchain selbst verifiziert, statt einem Server eines Drittanbieters zu vertrauen.\n\nStellen Sie sich das als zwei zusammenarbeitende Teile vor: die Wallet (Schlüssel, Guthaben, Senden) und den Node (Blockchain und Netzwerk). Die Fragen hier sind entsprechend aufgeteilt — siehe die Tabs Wallet und Daemon oben.",
|
||||
"faq_w_gs_1_q": "Was ist ObsidianDragon?",
|
||||
"faq_w_gs_2_a": "Beim ersten Start erstellt der Einrichtungsassistent automatisch eine Wallet für Sie. Neue Wallets basieren auf einer geheimen Wiederherstellungsphrase (einer Liste von Wörtern). Notieren Sie diese Phrase und bewahren Sie sie offline auf — es ist die einzige Möglichkeit, Ihre Gelder wiederherzustellen, falls dieser Computer verloren geht.\n\nSie können die Phrase jederzeit unter Einstellungen > Sicherung & Daten > Seed phrase einsehen oder sichern.",
|
||||
"faq_w_gs_2_q": "Wie erstelle ich eine neue Wallet?",
|
||||
"faq_w_gs_3_a": "Verwenden Sie den Wiederherstellungs-Ablauf per Seed-Phrase, wenn Sie eine Wiederherstellungsphrase besitzen. (Im Full-Node-Build können Sie vor dem Start auch eine vorhandene wallet.dat-Datei in das Datenverzeichnis der Wallet kopieren.)\n\nNach der Wiederherstellung scannt die Wallet erneut, um Ihre vergangenen Transaktionen zu finden — Guthaben und Verlauf können daher beim ersten Mal etwas dauern, bis sie erscheinen.",
|
||||
"faq_w_gs_3_q": "Wie stelle ich eine bereits vorhandene Wallet wieder her?",
|
||||
"faq_w_gs_4_a": "Der Assistent lässt Sie ein Erscheinungsbild/Theme wählen, Verschlüsselung und eine PIN einrichten und — im Full-Node-Build — optional einen Bootstrap herunterladen, um die erste Synchronisierung zu beschleunigen. Sie können jeden Schritt überspringen und all dies später in den Einstellungen ändern.",
|
||||
"faq_w_gs_4_q": "Was macht die Ersteinrichtung?",
|
||||
"faq_w_gs_title": "Erste Schritte",
|
||||
"faq_w_sec_1_a": "Öffnen Sie Einstellungen > Node & Sicherheit und legen Sie eine Passphrase fest (Bereich SICHERHEIT). Die Verschlüsselung schützt Ihre privaten Schlüssel auf der Festplatte, sodass jemand mit Zugriff auf die Dateien Ihre Coins trotzdem nicht ausgeben kann.\n\nWählen Sie eine starke Passphrase und verlieren Sie sie nicht — ohne sie gibt es keine Möglichkeit, eine verschlüsselte Wallet wiederherzustellen.",
|
||||
"faq_w_sec_1_q": "Wie verschlüssele ich meine Wallet?",
|
||||
"faq_w_sec_2_a": "Die PIN sperrt den Bildschirm der App, sodass Guthaben und Aktionen verborgen sind, wenn Sie kurz weg sind. Sie ist eine Komfortsperre zusätzlich zur Verschlüsselung; Sie richten sie im Assistenten oder in den Einstellungen ein, nachdem die Wallet verschlüsselt wurde.",
|
||||
"faq_w_sec_2_q": "Was ist die PIN / der Sperrbildschirm?",
|
||||
"faq_w_sec_3_a": "Nein. Die Wallet-Verschlüsselung kann nicht umgangen werden. Wenn Sie noch Ihre Wiederherstellungsphrase besitzen, können Sie die Wallet daraus in eine neue Wallet wiederherstellen und eine neue Passphrase festlegen. Ohne Passphrase oder Seed-Phrase können die Gelder nicht wiederhergestellt werden.",
|
||||
"faq_w_sec_3_q": "Ich habe meine Passphrase vergessen — kann sie wiederhergestellt werden?",
|
||||
"faq_w_sec_title": "Sicherheit & Verschlüsselung",
|
||||
"faq_w_seed_1_a": "Gehen Sie zu Einstellungen > Sicherung & Daten > Seed phrase. Notieren Sie die Wörter der Reihe nach auf Papier und bewahren Sie sie an einem sicheren, offline zugänglichen Ort auf. Jeder, der die Phrase kennt, kann Ihre Coins ausgeben — speichern Sie sie daher niemals als Foto, in einer E-Mail oder einer Cloud-Notiz.",
|
||||
"faq_w_seed_1_q": "Wie sichere ich meine Wiederherstellungsphrase?",
|
||||
"faq_w_seed_2_a": "Ältere (Legacy-)Wallets basierten nicht auf einer Seed-Phrase. Die Wallet kann eine Legacy-Wallet in eine moderne, seed-basierte Wallet migrieren: Dabei wird eine neue Seed-Wallet erstellt und Ihre Gelder werden dorthin überführt. Suchen Sie nach „Zu Seed-Wallet migrieren…“ unter Einstellungen > Sicherung & Daten. Diese Funktion benötigt die aktuelle Daemon-Version.",
|
||||
"faq_w_seed_2_q": "Meine Wallet hat keine Wiederherstellungsphrase — kann ich eine hinzufügen?",
|
||||
"faq_w_seed_3_a": "Verwenden Sie beim Einrichten einer Wallet den Seed-Wiederherstellungs-Ablauf und geben Sie Ihre Wörter der Reihe nach ein. Ihre Wallet scannt anschließend erneut, um Guthaben und Verlauf wiederherzustellen, was beim ersten Mal etwas dauern kann.",
|
||||
"faq_w_seed_3_q": "Wie stelle ich aus meiner Wiederherstellungsphrase wieder her?",
|
||||
"faq_w_seed_4_a": "Ihre Seed-Phrase ist die primäre Sicherung und reicht aus, um alles wiederherzustellen. Eine Kopie Ihrer Wallet-Datei ist eine praktische zusätzliche Sicherung, die außerdem Bezeichnungen und Einstellungen erhält. Bewahren Sie jede Sicherung offline und privat auf.",
|
||||
"faq_w_seed_4_q": "Sollte ich auch wallet.dat sichern?",
|
||||
"faq_w_seed_title": "Seed-Phrase & Sicherung",
|
||||
"faq_w_send_1_a": "Öffnen Sie den Tab Senden, fügen Sie die Adresse des Empfängers ein, geben Sie einen Betrag ein und bestätigen Sie. Wenn die Wallet verschlüsselt ist, werden Sie gebeten, sie für die Transaktion zu entsperren.\n\nSendungen an geschützte Adressen sind privat; Sendungen an transparente Adressen sind öffentlich.",
|
||||
"faq_w_send_1_q": "Wie sende ich Gelder?",
|
||||
"faq_w_send_2_a": "DragonX-Gebühren sind sehr niedrig und werden automatisch festgelegt. Die Gebühr wird angezeigt, bevor Sie bestätigen. Normalerweise müssen Sie sie nicht ändern.",
|
||||
"faq_w_send_2_q": "Welche Gebühr zahle ich?",
|
||||
"faq_w_send_3_a": "Ja — beim Senden an eine geschützte (z-)Adresse können Sie ein verschlüsseltes Memo anhängen. Nur der Empfänger kann es lesen. Transparente Adressen unterstützen keine Memos.",
|
||||
"faq_w_send_3_q": "Kann ich einer Zahlung eine Nachricht beifügen?",
|
||||
"faq_w_send_4_a": "Eine Transaktion ist „ausstehend“, bis sie in einen Block aufgenommen wird und Bestätigungen erhält. Das dauert normalerweise ein bis zwei Minuten. Geschützte Transaktionen erfordern außerdem, dass Ihre Wallet synchronisiert ist, um den Beweis zu erstellen.\n\nBleibt eine Sendung ungewöhnlich lange ausstehend, prüfen Sie, ob Ihre Wallet verbunden und synchronisiert ist (siehe die Statusleiste unten im Fenster).",
|
||||
"faq_w_send_4_q": "Ich habe eine Zahlung gesendet, aber sie zeigt „ausstehend“ — warum?",
|
||||
"faq_w_send_title": "Senden & Empfangen",
|
||||
"faq_w_set_1_a": "Öffnen Sie Einstellungen > Erscheinungsbild, um ein Design und eine Akzentfarbe zu wählen. Mehrere helle und dunkle Skins sind enthalten, und Effekte wie Weichzeichnen lassen sich für leistungsschwächere Rechner abschalten.",
|
||||
"faq_w_set_1_q": "Wie ändere ich das Design?",
|
||||
"faq_w_set_2_a": "Ja — Einstellungen > Erscheinungsbild enthält eine Sprachauswahl. Die Wallet wird mit mehreren Übersetzungen ausgeliefert; alles, was noch nicht übersetzt ist, greift auf Englisch zurück.",
|
||||
"faq_w_set_2_q": "Kann ich die Sprache ändern?",
|
||||
"faq_w_set_3_a": "Verwenden Sie die Schriftskalierung in Einstellungen > Erscheinungsbild. Sie skaliert die gesamte Oberfläche, was auf hochauflösenden (HiDPI-)Bildschirmen hilfreich ist, auf denen die App sonst klein dargestellt werden könnte.",
|
||||
"faq_w_set_3_q": "Der Text ist zu klein (oder zu groß) — kann ich ihn skalieren?",
|
||||
"faq_w_set_4_a": "Aktivieren Sie den Preisabruf in den Einstellungen. Die Wallet zeigt dann einen ungefähren Fiat-Wert neben den Guthaben an und kann ein kleines Marktdiagramm anzeigen. Preise dienen nur zur Information.",
|
||||
"faq_w_set_4_q": "Wie zeige ich Fiat-Preise an?",
|
||||
"faq_w_set_title": "Einstellungen & Erscheinungsbild",
|
||||
"favorite_address": "Als Favorit markieren",
|
||||
"fee": "Gebühr",
|
||||
"fee_high": "Hoch",
|
||||
@@ -593,6 +878,63 @@
|
||||
"general": "Allgemein",
|
||||
"generating": "Wird generiert",
|
||||
"go_to_receive": "Zum Empfangen",
|
||||
"grpa_current_block_paren": "(Aktuell: %d)",
|
||||
"grpa_days_ago": "vor %lld Tagen",
|
||||
"grpa_dbg_addrman": "Verfolgung und Verwaltung von Peer-Adressen",
|
||||
"grpa_dbg_alert": "Meldungen des Alarmsystems",
|
||||
"grpa_dbg_bench": "Benchmark-Zeiten für Operationen",
|
||||
"grpa_dbg_coindb": "Lese-/Schreiboperationen der Coin-Datenbank",
|
||||
"grpa_dbg_db": "Berkeley-DB-Operationen",
|
||||
"grpa_dbg_estimatefee": "Algorithmus zur Gebührenschätzung",
|
||||
"grpa_dbg_http": "Aktivität des HTTP-RPC-Servers",
|
||||
"grpa_dbg_libevent": "Libevent-Netzwerkbibliothek",
|
||||
"grpa_dbg_lock": "Debugging von Lock-Konflikten",
|
||||
"grpa_dbg_mempool": "Aktivität des Transaktions-Mempools",
|
||||
"grpa_dbg_net": "Netzwerkverbindungen und -nachrichten",
|
||||
"grpa_dbg_paymentdisclosure": "Zahlungsoffenlegungsprotokoll",
|
||||
"grpa_dbg_pow": "Proof-of-Work-Mining-Aktivität",
|
||||
"grpa_dbg_proxy": "SOCKS5-Proxy-Verbindungen",
|
||||
"grpa_dbg_prune": "Block-Pruning-Operationen",
|
||||
"grpa_dbg_rand": "Zufallszahlengenerierung",
|
||||
"grpa_dbg_reindex": "Fortschritt der Blockchain-Neuindizierung",
|
||||
"grpa_dbg_rpc": "Verarbeitung von RPC-Befehlen",
|
||||
"grpa_dbg_selectcoins": "Coin-Auswahl für Transaktionen",
|
||||
"grpa_dbg_tor": "Tor-Integration und Circuit-Informationen",
|
||||
"grpa_dbg_zmq": "ZeroMQ-Benachrichtigungssystem",
|
||||
"grpa_dbg_zrpc": "Geschützte (z-addr) RPC-Operationen",
|
||||
"grpa_enter_private_key_to_import": "Geben Sie einen privaten Schlüssel zum Importieren ein.",
|
||||
"grpa_error_prefix": "Fehler: ",
|
||||
"grpa_hr_ago": "vor %lld Std",
|
||||
"grpa_invalid_response_from_daemon": "Ungültige Antwort vom Daemon",
|
||||
"grpa_invalid_suffix": " (ungültig)",
|
||||
"grpa_min_ago": "vor %lld Min",
|
||||
"grpa_sec_ago": "vor %lld Sek",
|
||||
"grpa_seed_demo_chat": "Seed-Demo-Chat",
|
||||
"grpa_showing_first_100_of": "... die ersten 100 von %d werden angezeigt",
|
||||
"grpa_tab_about": "Über",
|
||||
"grpa_tab_appearance": "Darstellung",
|
||||
"grpa_tab_backup_data": "Backup & Daten",
|
||||
"grpa_tab_chat": "Chat",
|
||||
"grpa_tab_explorer": "Explorer",
|
||||
"grpa_tab_node_security": "Node & Sicherheit",
|
||||
"grpa_tab_wallet": "Wallet",
|
||||
"grpa_unexpected_getblockhash_result": "unerwartetes getblockhash-Ergebnis",
|
||||
"grpb_copy": "Kopieren",
|
||||
"grpb_max": "Max",
|
||||
"grpb_new_badge_suffix": " [NEU]",
|
||||
"grpb_preview_msg_payment_through": "Ist die Zahlung durchgegangen? 🙂",
|
||||
"grpb_preview_msg_sending_rest": "Sende jetzt den Rest 👍",
|
||||
"grpb_preview_msg_yep_confirmed": "Ja — gerade bestätigt ✅",
|
||||
"grpb_selected_suffix": "\n(ausgewählt)",
|
||||
"grpb_tooltip_address_balance": "%s\nGuthaben: %.8f %s%s",
|
||||
"grpb_undo_clear": "Löschen rückgängig",
|
||||
"grpc_benchmark_inconclusive": "Benchmark nicht aussagekräftig: es wurden keine Hashrate-Werte aufgezeichnet. Prüfen Sie die Pool-Verbindung und versuchen Sie es erneut.",
|
||||
"grpc_benchmark_takes_secs": "Der Benchmark dauert ~%ds und unterbricht das Mining. Erneut klicken zum Starten.",
|
||||
"grpc_bootstrap_failed": "Bootstrap fehlgeschlagen",
|
||||
"grpc_bootstrap_not_initialized": "Bootstrap nicht initialisiert",
|
||||
"grpc_hashrate_fee": "%s %s%% Gebühr",
|
||||
"grpc_key_not_available": "Schlüssel für diese Adresse nicht verfügbar",
|
||||
"grpc_na": "N/V",
|
||||
"height": "Höhe",
|
||||
"help": "Hilfe",
|
||||
"hidden_tag": " (versteckt)",
|
||||
@@ -817,9 +1159,27 @@
|
||||
"memo_optional": "MEMO (OPTIONAL)",
|
||||
"memo_upper": "MEMO",
|
||||
"memo_z_only": "Hinweis: Memos sind nur beim Senden an abgeschirmte (z) Adressen verfügbar",
|
||||
"merge_addr_created": "Geschützte Adresse erstellt.",
|
||||
"merge_advanced": "Erweitert",
|
||||
"merge_back": "Zurück",
|
||||
"merge_batch_fmt": "Führt bis zu %d Inputs pro Durchgang zusammen — wiederholen Sie den Vorgang, um den Rest abzuschließen.",
|
||||
"merge_confirm_btn": "Bestätigen",
|
||||
"merge_confirm_fmt": "~%s DRGX aus %d Input(s) in %s konsolidieren?",
|
||||
"merge_create_zaddr": "Geschützte Adresse erstellen",
|
||||
"merge_creating": "Adresse wird erstellt…",
|
||||
"merge_description": "Mehrere UTXOs zu einer einzelnen abgeschirmten Adresse zusammenführen. Dies kann die Wallet-Größe reduzieren und die Privatsphäre verbessern.",
|
||||
"merge_fee_hint": "Netzwerkgebühr für diese Transaktion.",
|
||||
"merge_funds": "Gelder zusammenführen",
|
||||
"merge_max_inputs": "Max. Inputs pro Batch",
|
||||
"merge_no_spendable": "Noch keine verfügbaren Inputs zum Konsolidieren.",
|
||||
"merge_progress": "Konsolidierung läuft… dies kann einige Minuten dauern. Sie können dieses Fenster schließen.",
|
||||
"merge_scope_fmt": "%d transparente + %d geschützte Inputs · ~%s DRGX verfügbar",
|
||||
"merge_scope_loading": "Ihre Inputs werden geprüft…",
|
||||
"merge_send_failed": "Zusammenführen fehlgeschlagen: ",
|
||||
"merge_source": "Konsolidieren",
|
||||
"merge_src_both": "Beide",
|
||||
"merge_src_shielded": "Geschützt",
|
||||
"merge_src_transparent": "Transparent",
|
||||
"merge_started": "Zusammenführung gestartet",
|
||||
"merge_title": "An Adresse zusammenführen",
|
||||
"mig_adopt": "Dies zu meiner Wallet machen",
|
||||
@@ -1059,6 +1419,13 @@
|
||||
"peers_upper": "PEERS",
|
||||
"peers_version": "Version",
|
||||
"pending": "Ausstehend",
|
||||
"picker_dat_count": "%d Wallet-Datei(en) in diesem Ordner",
|
||||
"picker_dat_none": "Keine Wallet-Dateien direkt in diesem Ordner",
|
||||
"picker_empty": "Dieser Ordner enthält keine Unterordner oder Wallet-Dateien.",
|
||||
"picker_home": "Home-Ordner",
|
||||
"picker_select": "Diesen Ordner durchsuchen",
|
||||
"picker_title": "Ordner zum Durchsuchen auswählen",
|
||||
"picker_up": "Eine Ebene nach oben",
|
||||
"pin_change_desc": "Ändern Sie Ihre Entsperr-PIN. Sie benötigen Ihre aktuelle PIN und eine neue PIN.",
|
||||
"pin_confirm_new_label": "Neue PIN bestätigen:",
|
||||
"pin_current_label": "Aktuelle PIN:",
|
||||
@@ -1195,6 +1562,7 @@
|
||||
"save_z_transactions": "Z-Tx in Tx-Liste speichern",
|
||||
"sb_auth_failed": "Authentifizierung fehlgeschlagen — rpcuser/rpcpassword prüfen",
|
||||
"sb_block": "Block: %d",
|
||||
"sb_block_db_unreadable": "Blockdatenbank unlesbar — Neuaufbau erforderlich",
|
||||
"sb_building_witnesses": "Zeugen werden erstellt",
|
||||
"sb_building_witnesses_pct": "Witnesses werden neu aufgebaut %.0f%%",
|
||||
"sb_connecting_daemon": "Verbindung zu dragonxd...",
|
||||
@@ -1210,6 +1578,7 @@
|
||||
"sb_dragonxd_stopped": "dragonxd gestoppt",
|
||||
"sb_dragonxd_stopping": "dragonxd wird gestoppt...",
|
||||
"sb_extracting_sapling": "Sapling-Parameter werden extrahiert...",
|
||||
"sb_finishing_repair": "Wallet-Reparatur wird abgeschlossen — erneutes Scannen…",
|
||||
"sb_importing_keys": "Schlüssel importieren",
|
||||
"sb_loading_config": "Konfiguration laden...",
|
||||
"sb_mining_hs": "%.1f H/s",
|
||||
@@ -1231,6 +1600,7 @@
|
||||
"sb_waiting_config": "Warten auf Daemon-Konfiguration...",
|
||||
"sb_waiting_daemon": "Warten auf dragonxd...",
|
||||
"sb_waiting_daemon_err": "Warten auf dragonxd — %s",
|
||||
"sb_wallet_needs_recovery": "Wallet-Reparatur verfügbar",
|
||||
"sb_warming_up": "Aufwärmen...",
|
||||
"sb_witness_cache": "Zeugen werden neu aufgebaut",
|
||||
"scale_effects": "SKALIERUNG & EFFEKTE",
|
||||
@@ -1240,6 +1610,45 @@
|
||||
"screenshot_sweep_full": "Vollständiger UI-Durchlauf",
|
||||
"search_icons": "Symbole suchen...",
|
||||
"search_placeholder": "Suchen...",
|
||||
"sec_changing_passphrase": "Passphrase wird geändert...",
|
||||
"sec_changing_pin": "PIN wird geändert...",
|
||||
"sec_couldnt_lock_wallet": "Wallet konnte nicht gesperrt werden — es ist weiterhin entsperrt. Prüfen Sie die Daemon-Verbindung.",
|
||||
"sec_encrypted_backup_suffix": "\nVerschlüsseltes Backup: wallet.dat.encrypted.bak",
|
||||
"sec_encrypting_wallet": "Wallet wird verschlüsselt...",
|
||||
"sec_encryption_did_not_complete": "Die Wallet-Verschlüsselung wurde nicht abgeschlossen — Ihr Wallet ist NICHT verschlüsselt. Öffnen Sie die Einstellungen, um die Verschlüsselung abzuschließen.",
|
||||
"sec_encryption_failed_prefix": "Verschlüsselung fehlgeschlagen: ",
|
||||
"sec_failed_prefix": "Fehlgeschlagen: ",
|
||||
"sec_failed_to_create_vault": "Tresor konnte nicht erstellt werden",
|
||||
"sec_importing_keys_rescanning": "Schlüssel werden importiert & Blockchain neu gescannt — Wallet ist währenddessen nutzbar",
|
||||
"sec_incorrect_current_pin": "Aktuelle PIN falsch",
|
||||
"sec_incorrect_passphrase_decrypt": "Falsche Passphrase",
|
||||
"sec_incorrect_passphrase_pin_setup": "Falsche Passphrase",
|
||||
"sec_incorrect_pin_remove": "Falsche PIN",
|
||||
"sec_internal_error_change_pin": "Interner Fehler",
|
||||
"sec_internal_error_remove_pin": "Interner Fehler",
|
||||
"sec_mode_passphrase": " Passphrase",
|
||||
"sec_not_connected_to_daemon": "Nicht mit Daemon verbunden",
|
||||
"sec_not_connected_to_daemon_pin": "Nicht mit Daemon verbunden",
|
||||
"sec_passphrase_changed_successfully": "Passphrase erfolgreich geändert",
|
||||
"sec_pin_changed_successfully": "PIN erfolgreich geändert",
|
||||
"sec_pin_removed": "PIN entfernt",
|
||||
"sec_pin_set_successfully": "PIN erfolgreich festgelegt",
|
||||
"sec_restart_daemon_for_encryption": "Bitte starten Sie Ihren Daemon neu, damit die Verschlüsselung wirksam wird.",
|
||||
"sec_too_many_attempts_wait": "Zu viele Versuche. Warten Sie %.0f Sekunden...",
|
||||
"sec_total_elapsed_fmt": "Gesamtdauer: %dm %02ds",
|
||||
"sec_unlock_button": "Entsperren",
|
||||
"sec_unlock_failed_prefix": "Entsperren fehlgeschlagen: ",
|
||||
"sec_unlocking_fmt": "Entsperren%s",
|
||||
"sec_use_passphrase_instead": "Stattdessen Passphrase verwenden",
|
||||
"sec_use_pin_instead": "Stattdessen PIN verwenden",
|
||||
"sec_verifying_passphrase": "Passphrase wird überprüft...",
|
||||
"sec_verifying_pin": "PIN wird überprüft...",
|
||||
"sec_wallet_decrypted_all_keys_imported": "Wallet erfolgreich entschlüsselt! Alle Schlüssel importiert.",
|
||||
"sec_wallet_encrypted_and_pin_set": "Wallet verschlüsselt & PIN festgelegt",
|
||||
"sec_wallet_encrypted_but_pin_vault_failed": "Wallet verschlüsselt, aber PIN-Tresor fehlgeschlagen",
|
||||
"sec_wallet_encrypted_restarting_daemon": "Wallet verschlüsselt. Daemon wird neu gestartet...",
|
||||
"sec_wallet_encrypted_successfully": "Wallet erfolgreich verschlüsselt",
|
||||
"sec_wallet_locked_title": "Wallet gesperrt",
|
||||
"security": "SICHERHEIT",
|
||||
"seed_backup_button": "Wiederherstellungsphrase",
|
||||
"seed_backup_close": "Schließen",
|
||||
@@ -1263,6 +1672,7 @@
|
||||
"send_amount": "Betrag",
|
||||
"send_amount_details": "BETRAGSDETAILS",
|
||||
"send_amount_upper": "BETRAG",
|
||||
"send_available_note": "verfügbar",
|
||||
"send_cannot_send_now": "Senden derzeit nicht möglich — prüfen Sie Verbindung, Synchronisierung und verfügbares Guthaben.",
|
||||
"send_clear_fields": "Alle Formularfelder leeren?",
|
||||
"send_contacts_button": "Aus Kontakten auswählen",
|
||||
@@ -1450,17 +1860,34 @@
|
||||
"status": "Status",
|
||||
"stop_external": "Externen Daemon stoppen",
|
||||
"stop_mining": "Mining stoppen",
|
||||
"stratum_allowip_hint": "Miner von IP oder CIDR zulassen (leer = nur dieser Computer)",
|
||||
"stratum_expose_warn": "Öffnet einen Mining-Port für das von Ihnen zugelassene Netzwerk — nur in einem vertrauenswürdigen LAN verwenden.",
|
||||
"stratum_host": "Mining-Pool hosten (Stratum)",
|
||||
"stratum_host_hint": "Miner verbinden sich mit diesem Computer über Port 22769 (RPC-Port + 1000) mit einem RandomX-Stratum-Miner. Starten Sie den Daemon neu, um die Änderung zu übernehmen.",
|
||||
"stratum_host_section": "MINING-POOL-HOSTING",
|
||||
"submitting_transaction": "Transaktion wird übermittelt...",
|
||||
"success": "Erfolg",
|
||||
"summary": "Zusammenfassung",
|
||||
"sweep_button": "Fegen",
|
||||
"sweep_caveat": "Importiert den Schlüssel, um eine Transaktion zu signieren, die alle Mittel an Ihre Adresse verschiebt. Der Schlüssel bleibt mit leerem Guthaben in Ihrer Wallet.",
|
||||
"sweep_current_theme_only": "Nur aktuelles Design",
|
||||
"sweep_dest_label": "Gefegte Mittel senden an",
|
||||
"sweep_dest_new": "Neue abgeschirmte Adresse (empfohlen)",
|
||||
"sweep_done": "Fertig — Mittel an Ihre Adresse gefegt.",
|
||||
"sweep_to": "Gefegt an:",
|
||||
"sweep_toggle": "In meine Wallet fegen (Schlüssel nicht behalten)",
|
||||
"sweep_tx": "Transaktion:",
|
||||
"swin_connection_failed": "Verbindung fehlgeschlagen: ",
|
||||
"swin_connection_successful": "Verbindung erfolgreich!\ndragonxd-Version: ",
|
||||
"swin_invalid_suffix": " (ungültig)",
|
||||
"swin_no_history_file_found": "Keine Verlaufsdatei gefunden",
|
||||
"swin_rescan_failed": "Neuscan fehlgeschlagen: ",
|
||||
"swin_rescan_started_from_block": "Neuscan gestartet ab Block ",
|
||||
"swin_rescan_to": " bis ",
|
||||
"swin_rpc_client_not_initialized": "RPC-Client nicht initialisiert",
|
||||
"swin_settings_saved": "Einstellungen gespeichert",
|
||||
"swin_theme_list_refreshed": "Design-Liste aktualisiert",
|
||||
"swin_ztx_history_cleared": "Z-Transaktionsverlauf gelöscht",
|
||||
"switch_corrupt_body": "Diese Wallet scheint beschädigt zu sein – der Knoten konnte sie nicht öffnen. Aus einem Backup wiederherstellen, neu erstellen oder eine Reparatur versuchen.",
|
||||
"switch_corrupt_repair": "Reparatur versuchen (Salvage)",
|
||||
"switch_progress_background": "Im Hintergrund fortsetzen",
|
||||
@@ -1621,6 +2048,8 @@
|
||||
"tt_simple_bg": "Einen einfachen Verlauf für den Hintergrund verwenden\\nHotkey: Ctrl+Up",
|
||||
"tt_simple_bg_alt": "Eine Verlaufsversion des Theme-Hintergrundbilds verwenden\\nHotkey: Ctrl+Up",
|
||||
"tt_stop_external": "Gilt bei Verbindung zu einem Daemon,\\nder außerhalb dieser Wallet gestartet wurde",
|
||||
"tt_stratum_host": "Betreibt einen RandomX-Stratum-Pool-Server auf diesem Node, sodass andere RandomX-Miner diesen Computer ansprechen können. Erfordert einen Node ab v1.3.0 und einen Daemon-Neustart, um wirksam zu werden.",
|
||||
"tt_sweep_current_theme_only": "Nur das aktive Design durchsuchen, statt jedes Design zu durchlaufen",
|
||||
"tt_test_conn": "Die RPC-Verbindung zum Daemon überprüfen",
|
||||
"tt_theme_effects": "Schimmer, Glühen, Farbton-Zyklen pro Theme",
|
||||
"tt_theme_hotkey": "Hotkey: Ctrl+Links/Rechts zum Wechseln der Themes",
|
||||
@@ -1629,6 +2058,7 @@
|
||||
"tt_ui_opacity": "Karten- und Seitenleisten-Deckkraft (100% = vollständig undurchsichtig, niedriger = durchsichtiger)",
|
||||
"tt_validate": "Prüfen, ob eine DragonX-Adresse gültig ist",
|
||||
"tt_verbose": "Detaillierte Verbindungsdiagnosen,\\nDaemon-Status und Port-Besitzer-Info\\nin der Konsolen-Registerkarte protokollieren",
|
||||
"tt_wallet_size_warn": "Geschützte Wallets wachsen mit den Witness-Daten jedes Notes — das Zusammenführen vieler Notes in eine Adresse verringert die Größe. Zuerst sichern.",
|
||||
"tt_wallets_button": "Ihre Wallet-Dateien auflisten und zwischen ihnen wechseln",
|
||||
"tt_website": "Die DragonX-Website öffnen",
|
||||
"tt_window_opacity": "Hintergrund-Deckkraft (niedriger = Desktop durch Fenster sichtbar)",
|
||||
@@ -1695,8 +2125,68 @@
|
||||
"view_on_explorer": "Im Explorer anzeigen",
|
||||
"waiting_for_daemon": "Warte auf Daemon-Verbindung...",
|
||||
"wallet": "WALLET",
|
||||
"wallet_degraded_notify": "Ihre Wallet wurde im eingeschränkten Modus geöffnet: Vorhandene Guthaben sind sicher und verfügbar, aber das Erstellen neuer Adressen und das Schützen von Guthaben sind deaktiviert. Sichern Sie Ihre Seed-Phrase und stellen Sie sie wieder her, um die Wallet vollständig zu reparieren.",
|
||||
"wallet_diagnostics_hdr": "DIAGNOSE",
|
||||
"wallet_empty": "Ihre Wallet ist leer",
|
||||
"wallet_empty_hint": "Wechseln Sie zu Empfangen, um Ihre Adresse zu erhalten und Gelder zu empfangen.",
|
||||
"wallet_options_hdr": "OPTIONEN",
|
||||
"wallet_rebuild_failed": "Der Neuaufbau hat keine gültige Wallet erzeugt, daher wurde nichts geändert. Ihre Wallet ist unangetastet.",
|
||||
"wallet_rebuild_install_failed": "Die neu aufgebaute Wallet konnte nicht installiert werden; Ihre aktuelle Wallet wurde beibehalten.",
|
||||
"wallet_rebuild_no_helper": "Der Wallet-Neuaufbau-Helfer ist in diesem Build nicht verfügbar. Verwenden Sie Wiederherstellen, oder bauen Sie manuell neu auf.",
|
||||
"wallet_rebuild_no_source": "Es konnte keine lesbare Wallet zum Neuaufbau gefunden werden. Nichts wurde geändert.",
|
||||
"wallet_rebuild_ok": "Wallet-Datenbank neu aufgebaut — der Node lädt sie und scannt erneut nach Ihrem Guthaben.",
|
||||
"wallet_rebuild_started": "Ihre Wallet-Datei wird repariert — bitte schließen Sie dieses Fenster nicht.",
|
||||
"wallet_recovered_body": "Es wurde nichts gelöscht. Ihre ursprüngliche Wallet ist weiterhin als datierte Sicherungsdatei auf Ihrem Computer gespeichert, und jede Option unten kopiert oder benennt Dateien nur um — gelöscht wird nie etwas. Ihre Schlüssel werden nie neu erzeugt, nur erneut eingelesen.\n\nDie jetzt geladene reparierte Kopie fehlen möglicherweise ein paar aktuelle Transaktionen, daher kann Ihr Guthaben etwas niedrig erscheinen, bis Sie unten abschließen und es neu gescannt wird.",
|
||||
"wallet_recovered_dismiss": "Nicht jetzt — reparierte Kopie behalten",
|
||||
"wallet_recovered_dismiss_sub": "Es werden keine Dateien geändert. Sie können dies jederzeit über die Statusleiste erneut öffnen; Ihr Original bleibt so oder so sicher gesichert.",
|
||||
"wallet_recovered_notify": "Ihre Wallet-Datei musste repariert werden — Ihr Original wurde sicher gesichert. Öffnen Sie die App, um Ihre Optionen zu prüfen.",
|
||||
"wallet_recovered_open_folder": "Dateien anzeigen",
|
||||
"wallet_recovered_open_folder_sub": "Öffnet den Wallet-Datenordner, damit Sie die Sicherungsdateien selbst prüfen können — es wird nichts geändert.",
|
||||
"wallet_recovered_rebuild": "Automatisch reparieren (empfohlen)",
|
||||
"wallet_recovered_rebuild_sub": "Liest jeden wiederherstellbaren Datensatz aus Ihrer vollständigsten Wallet-Datei erneut ein und schreibt eine saubere neue Datei, dann wird neu gestartet. Die gründlichste Option — Ihre aktuelle Datei wird so oder so als datierte Sicherung behalten.",
|
||||
"wallet_recovered_restore": "Stattdessen die Originaldatei wiederherstellen",
|
||||
"wallet_recovered_restore_sub": "Setzt Ihre größte unveränderte Sicherung unverändert zurück, dann wird neu gescannt — etwas schneller, aber nur so vollständig, wie diese eine Datei war. Ihre aktuelle Datei wird so oder so als datierte Sicherung behalten.",
|
||||
"wallet_recovered_safety": "Ihre Coins sind sicher.",
|
||||
"wallet_recovered_title": "Ihre Wallet-Datei benötigt eine kurze Reparatur",
|
||||
"wallet_recovered_warn": "Beim Start der App wurde festgestellt, dass Ihre Wallet-Datei die Konsistenzprüfung nicht bestanden hat — das passiert meist nach einem App-Update oder einem unsauberen Herunterfahren. Die App hat Ihre Daten bereits geschützt: Sie hat die alte Datei beiseitegelegt und eine reparierte Kopie geladen, damit Sie nicht feststecken.",
|
||||
"wallet_recovery_decide_later": "Später entscheiden",
|
||||
"wallet_recovery_decide_later_tip": "Schließt dies und behält die jetzt geladene Kopie. Es wird nichts geändert, und Sie können jederzeit reparieren — die Statusleiste behält einen Link „Wallet-Reparatur verfügbar“.",
|
||||
"wallet_recovery_details_label": "Technische Details anzeigen",
|
||||
"wallet_recovery_done": "Fertig",
|
||||
"wallet_recovery_failure_body": "Die Reparatur wurde ausgeführt, aber ihr Ergebnis hat die Überprüfung nicht bestanden (es konnte nicht gelesen werden oder hatte keine Adressen), daher wurde es automatisch verworfen, bevor es jemals etwas ersetzt hat. Ihre Wallet ist genau so, wie sie vor dem Klick war — auf der Festplatte hat sich nichts geändert.",
|
||||
"wallet_recovery_failure_title": "Die Reparatur wurde nicht abgeschlossen",
|
||||
"wallet_recovery_files_detail": "Ihre ursprüngliche Wallet ist noch vorhanden — die App hat sie in Ihrem Datenordner in eine datierte Sicherung (wallet.<Zahlen>.bak) umbenannt und nichts gelöscht. „Reparieren“ liest jeden Datensatz aus Ihrer vollständigsten Wallet in eine brandneue, saubere Datei ein. „Wiederherstellen“ kopiert Ihre größte Sicherung exakt so zurück, wie sie ist. Die gerade geladene Kopie wird ebenfalls als Sicherung behalten.",
|
||||
"wallet_recovery_files_label": "Was passiert mit meinen Dateien?",
|
||||
"wallet_recovery_notnow_short": "Nicht jetzt",
|
||||
"wallet_recovery_other_options": "Weitere Optionen",
|
||||
"wallet_recovery_rebuild_card": "Automatisch reparieren",
|
||||
"wallet_recovery_rebuild_card_desc": "Liest jeden wiederherstellbaren Datensatz in eine saubere Datei ein, dann wird neu gestartet. Die gründlichste Option.",
|
||||
"wallet_recovery_recommended": "EMPFOHLEN",
|
||||
"wallet_recovery_repair_go": "Reparieren",
|
||||
"wallet_recovery_rescan_body": "Die Blockchain wird neu gescannt, um Ihr Guthaben und Ihren Transaktionsverlauf wiederherzustellen. Dies kann mehrere Minuten dauern — Sie müssen nichts tun, und starten Sie den Node bitte nicht neu, während dies läuft.",
|
||||
"wallet_recovery_rescan_elapsed": "Arbeitet seit %s",
|
||||
"wallet_recovery_rescan_size": "Die reparierte Wallet ist jetzt %s groß und füllt sich weiter",
|
||||
"wallet_recovery_rescan_slow": "Dies dauert länger als gewöhnlich, was bei einer großen Wallet normal ist. Es ist sicher, es weiterlaufen zu lassen — Sie können den Fortschritt unter Erweitert ▸ Konsole beobachten.",
|
||||
"wallet_recovery_rescan_title": "Ihre Wallet-Reparatur wird abgeschlossen",
|
||||
"wallet_recovery_restore_card": "Original wiederherstellen",
|
||||
"wallet_recovery_restore_card_desc": "Setzt Ihre größte unveränderte Sicherung unverändert zurück — nur so vollständig, wie diese Datei war.",
|
||||
"wallet_recovery_restore_go": "Wiederherstellen",
|
||||
"wallet_recovery_success_body": "Die App hat Ihre reparierte Wallet geladen und scannt neu, um Ihr Guthaben zu summieren — dies kann einige Minuten dauern. Sie liest jeden Datensatz, den sie kann, aber bei seltenen beschädigten Dateien kann sie etwas weniger — oder gelegentlich mehr — Adressen wiederherstellen als zuvor. Prüfen Sie nach Abschluss des Neuscans, ob Guthaben und Verlauf korrekt aussehen.",
|
||||
"wallet_recovery_success_restore": "Ihre größte Sicherungsdatei ist wieder an ihrem Platz und wird jetzt geladen, und die App scannt neu. Sie wurde unverändert wiederhergestellt, ist also genau so vollständig, wie diese Datei war — prüfen Sie Ihr Guthaben, sobald der Neuscan abgeschlossen ist.",
|
||||
"wallet_recovery_try_other": "Die andere Option versuchen",
|
||||
"wallet_recovery_whats_happened": "Was ist passiert?",
|
||||
"wallet_recovery_working_label": "Wird ausgeführt",
|
||||
"wallet_restore_bad_backup": "Die gesicherte Wallet-Datei scheint unlesbar zu sein, daher wurde sie NICHT wiederhergestellt — Ihre aktuelle Wallet ist unverändert. Stellen Sie stattdessen von Ihrer eigenen Sicherung wieder her.",
|
||||
"wallet_restore_busy": "Der Node startet gerade neu — versuchen Sie es gleich noch einmal.",
|
||||
"wallet_restore_copy_failed": "Die gesicherte Wallet konnte nicht installiert werden; Ihre aktuelle Wallet wurde beibehalten.",
|
||||
"wallet_restore_move_failed": "Die aktuelle Wallet konnte nicht beiseitegelegt werden — es wurde nichts geändert.",
|
||||
"wallet_restore_no_backup": "Es konnte keine wallet.<Zeitstempel>.bak zum Wiederherstellen gefunden werden. Es wurde nichts geändert.",
|
||||
"wallet_restore_no_restart": "Ihre ursprüngliche Wallet wurde wiederhergestellt, aber der Node wurde nicht neu gestartet — starten Sie ihn über die Einstellungen.",
|
||||
"wallet_restore_ok": "Ursprüngliche Wallet wiederhergestellt. Der Node lädt sie jetzt.",
|
||||
"wallet_restore_started": "Ihre Originaldatei wird wiederhergestellt — bitte schließen Sie dieses Fenster nicht.",
|
||||
"wallet_restore_stop_failed": "Der Node hat nicht rechtzeitig gestoppt, daher wurde nichts geändert. Versuchen Sie es erneut.",
|
||||
"wallet_size_consolidate": "Notizen konsolidieren…",
|
||||
"wallet_size_warn": "Diese Wallet-Datei ist groß. Das Konsolidieren Ihrer Notizen kann weiteres Wachstum eindämmen.",
|
||||
"wallets_active": "Aktiv",
|
||||
"wallets_add": "Ordner hinzufügen",
|
||||
"wallets_add_folder": "Auch einen weiteren Ordner nach Wallet-Dateien durchsuchen:",
|
||||
@@ -1739,6 +2229,7 @@
|
||||
"wallets_open_inplace_tt": "Öffnet diese Wallet an ihrem Ort — im Datenverzeichnis verlinkt (keine Kopie)",
|
||||
"wallets_remove_folder": "Diesen Ordner nicht mehr durchsuchen",
|
||||
"wallets_reveal": "Ordner anzeigen",
|
||||
"wallets_scan_folder": "Einen weiteren Ordner nach Wallets durchsuchen…",
|
||||
"wallets_scanned_folders": "Durchsuchte Ordner:",
|
||||
"wallets_sort_addresses": "Adressen",
|
||||
"wallets_sort_asc": "Aufsteigend (ältestes / wenigste / kleinstes zuerst)",
|
||||
@@ -1808,19 +2299,30 @@
|
||||
"wiz_welcome_sub": "Ein paar schnelle Einstellungen und Ihr Full Node ist bereit.",
|
||||
"wiz_welcome_title": "Willkommen bei ObsidianDragon!",
|
||||
"wizard_daemon_start_failed": "Daemon-Start fehlgeschlagen — wird automatisch wiederholt",
|
||||
"xmrig_active_tt": "Der aktuell installierte (aktive) Miner",
|
||||
"xmrig_browse_releases": "Alle Releases durchsuchen…",
|
||||
"xmrig_checking": "Nach dem neuesten Miner wird gesucht…",
|
||||
"xmrig_current": "Aktuell:",
|
||||
"xmrig_downgrade_note": "Das Installieren einer anderen Miner-Version ersetzt die aktuelle; stoppen Sie zuerst das Mining, dann wird sie beim nächsten Start wirksam.",
|
||||
"xmrig_download_install": "Herunterladen & installieren",
|
||||
"xmrig_downloading": "Wird heruntergeladen…",
|
||||
"xmrig_install_this": "Diese Version installieren",
|
||||
"xmrig_installed": "Installiert:",
|
||||
"xmrig_installed_ok": "Miner installiert",
|
||||
"xmrig_installing": "Wird installiert…",
|
||||
"xmrig_latest": "Neueste:",
|
||||
"xmrig_latest_badge": "neueste",
|
||||
"xmrig_latest_tt": "Neueste verfügbare Version",
|
||||
"xmrig_loading_releases": "Releases werden geladen…",
|
||||
"xmrig_no_notes": "Keine Versionshinweise für diese Version.",
|
||||
"xmrig_none": "keiner",
|
||||
"xmrig_prerelease_tt": "Vorabversion / Testbuild",
|
||||
"xmrig_reinstall": "Neu installieren",
|
||||
"xmrig_released": "Veröffentlicht:",
|
||||
"xmrig_releases": "xmrig-Releases",
|
||||
"xmrig_status_current": "Diese Version ist installiert",
|
||||
"xmrig_status_latest": "Neueste Version",
|
||||
"xmrig_status_older": "Älter als Ihre installierte Version",
|
||||
"xmrig_stop_mining_first": "Stoppen Sie das Mining, bevor Sie den Miner aktualisieren.",
|
||||
"xmrig_unavailable_body": "Für diese Plattform ist kein Miner-Build verfügbar.",
|
||||
"xmrig_unavailable_title": "Miner-Updates nicht verfügbar",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"about_license_text": "Este software se distribuye bajo la Licencia Pública General de GNU v3 (GPLv3). Usted es libre de usar, modificar y distribuir este software bajo los términos de la licencia.",
|
||||
"about_peers_count": "%zu nodos",
|
||||
"about_release": "Producción",
|
||||
"about_source": "Código fuente",
|
||||
"about_title": "Acerca de ObsidianDragon",
|
||||
"about_version": "Versión:",
|
||||
"about_website": "Sitio Web",
|
||||
@@ -60,7 +61,84 @@
|
||||
"amount_label": "Cantidad:",
|
||||
"animate_avatars": "Animar avatares",
|
||||
"appearance": "APARIENCIA",
|
||||
"appx_back": "Atrás",
|
||||
"appx_back_up_seed_phrase_title": "Haz una copia de tu frase semilla",
|
||||
"appx_birthday_block_height": "Fecha de creación (altura de bloque): %llu — guárdala también.",
|
||||
"appx_blockchain_data_deleted": "Datos de la cadena de bloques eliminados (%d elementos). El daemon se está reiniciando para volver a sincronizar desde la red.",
|
||||
"appx_blockchain_maintenance_in_progress": "Ya hay una operación de mantenimiento de la cadena de bloques en curso.",
|
||||
"appx_blockchain_rescan_complete": "Reescaneo de la cadena de bloques completado",
|
||||
"appx_bootstrap_complete_reconciling": "Arranque inicial completado; reconciliando tu cartera con los nuevos datos de la cadena.",
|
||||
"appx_cancel": "Cancelar",
|
||||
"appx_cleaning_up": "Limpiando...",
|
||||
"appx_confirm_your_backup": "Confirma tu copia de seguridad",
|
||||
"appx_copied_clipboard_autoclears": "Copiado; el portapapeles se borra automáticamente en 45 s",
|
||||
"appx_copy": "Copiar",
|
||||
"appx_could_not_start_restore": "No se pudo iniciar la restauración",
|
||||
"appx_create_failed_prefix": "Error al crear: ",
|
||||
"appx_creating_your_wallet": "Creando tu cartera…",
|
||||
"appx_daemon_error": "Error del daemon",
|
||||
"appx_daemon_reinstall_in_progress": "La reinstalación del daemon ya está en curso.",
|
||||
"appx_disconnecting": "Desconectando...",
|
||||
"appx_done": "Listo",
|
||||
"appx_dragonxd_output": "Salida de dragonxd",
|
||||
"appx_encrypting_wallet": "Cifrando la cartera...",
|
||||
"appx_fullnode_lifecycle_unavailable_lite": "Las acciones de ciclo de vida del nodo completo no están disponibles en la versión lite",
|
||||
"appx_installing_bundled_daemon": "Instalando el daemon incluido; el nodo se detendrá, se actualizará y se reiniciará...",
|
||||
"appx_invalid_payment_uri_prefix": "URI de pago no válida: ",
|
||||
"appx_ive_written_it_down": "Ya la anoté",
|
||||
"appx_keep_node_running_and_quit": "Mantener el nodo y salir",
|
||||
"appx_last_block_n": "Último bloque: %d",
|
||||
"appx_last_used_wallet_not_found_prefix": "Tu último archivo de cartera usado (",
|
||||
"appx_last_used_wallet_not_found_suffix": ") no se encontró; se abrió la cartera predeterminada en su lugar. Si lo moviste, restáuralo y vuelve a él desde la lista de carteras.",
|
||||
"appx_low_spec_mode_disabled": "Modo de bajos recursos desactivado",
|
||||
"appx_low_spec_mode_enabled": "Modo de bajos recursos activado",
|
||||
"appx_miner_stopped_prefix": "Minero detenido: ",
|
||||
"appx_miner_stopped_unexpectedly": "El minero se detuvo inesperadamente.",
|
||||
"appx_n_min_n_sec": "%d min %d s",
|
||||
"appx_n_seconds": "%d segundos",
|
||||
"appx_no_bundled_daemon_to_install": "Esta versión no incluye un daemon para instalar",
|
||||
"appx_no_embedded_daemon_to_install": "Esta versión no incluye un daemon integrado para instalar",
|
||||
"appx_node_busy_restarting": "El nodo está ocupado reiniciándose; inténtalo de nuevo en un momento.",
|
||||
"appx_node_rebuilding_witness_cache": "El nodo está reconstruyendo su caché de testigos",
|
||||
"appx_not_next_word": " — esa no es la siguiente palabra",
|
||||
"appx_payment_request_loaded": "Solicitud de pago cargada",
|
||||
"appx_pool_miner_connected_and_hashing": "Minero de pool conectado y calculando hashes.",
|
||||
"appx_progress_n_of_n": "Progreso: %d / %d",
|
||||
"appx_rebuilding_sapling_note_witnesses": "Reconstruyendo testigos de notas Sapling…",
|
||||
"appx_rebuilding_witness_cache_blocks_left": "Reconstruyendo la caché de testigos %.0f%% — %d bloques restantes",
|
||||
"appx_rebuilding_witness_cache_pct": "Reconstruyendo la caché de testigos %.0f%%",
|
||||
"appx_recovery_phrase_word_count": "La frase de recuperación debe tener 24 palabras; tú tienes %d.",
|
||||
"appx_restarting_daemon_rescan_flag": "Reiniciando el daemon con la opción -rescan...",
|
||||
"appx_restarting_daemon_zapwallettxes": "Reiniciando el daemon con -zapwallettxes=2 (reparación de cartera)...",
|
||||
"appx_restoring_your_wallet": "Restaurando tu cartera…",
|
||||
"appx_seed_backup_warning": "Estas 24 palabras son la ÚNICA forma de restaurar tu cartera. Anótalas en orden, guárdalas sin conexión y nunca las compartas. Si las pierdes, tus fondos se perderán para siempre.",
|
||||
"appx_seed_not_backed_up_warning": "No has hecho una copia de tu semilla; podrías perder tus fondos. ¿Omitir de todos modos?",
|
||||
"appx_sending_stop_command_to_daemon": "Enviando el comando de parada al daemon...",
|
||||
"appx_setting_initial_sapling_witnesses": "Estableciendo testigos Sapling iniciales %.0f%%",
|
||||
"appx_shutdown_complete": "Apagado completado",
|
||||
"appx_simple_background_disabled": "Fondo simple desactivado",
|
||||
"appx_simple_background_enabled": "Fondo simple activado",
|
||||
"appx_skip": "Omitir",
|
||||
"appx_skip_anyway": "Omitir de todos modos",
|
||||
"appx_still_status_prefix": "Aún \"",
|
||||
"appx_still_status_suffix": "\" — forzar el cierre ahora podría dañar los datos de la cadena.",
|
||||
"appx_stop_anyway_and_quit": "Detener de todos modos y salir",
|
||||
"appx_stopping_daemon_deleting_blockchain": "Deteniendo el daemon y eliminando los datos de la cadena de bloques...",
|
||||
"appx_stopping_node_discards_rebuild": "Detener el nodo ahora descarta la reconstrucción en curso y la reinicia (varios minutos) la próxima vez que abras la cartera. También puedes dejar el nodo en ejecución.",
|
||||
"appx_stopping_pool_miner": "Deteniendo el minero de pool...",
|
||||
"appx_syncing_pct_block_n_of_n": "Sincronizando %.1f%% — Bloque %d / %d",
|
||||
"appx_tap_words_in_order": "Toca las palabras en el orden correcto para confirmar que las guardaste.",
|
||||
"appx_theme_effects_disabled": "Efectos de tema desactivados",
|
||||
"appx_theme_effects_enabled": "Efectos de tema activados",
|
||||
"appx_theme_prefix": "Tema: ",
|
||||
"appx_use_settings_restart_daemon_hint": "Usa Ajustes > Reiniciar daemon para intentarlo de nuevo",
|
||||
"appx_waiting_for_daemon_to_encrypt_wallet": "Esperando a que el daemon cifre la cartera...",
|
||||
"appx_wallet_created_and_backed_up": "Cartera creada y respaldada.",
|
||||
"appx_wallet_open_failed_prefix": "Error al abrir la cartera: ",
|
||||
"auto_shield": "Auto-proteger minería",
|
||||
"autoshield_by_node": "Tu nodo blinda automáticamente el coinbase minado por ti.",
|
||||
"autoshield_off_backup_seed": "El coinbase minado aún no se está blindando automáticamente. Respalda tu frase de recuperación (Ajustes → Copia de seguridad y datos) para que tu nodo pueda activarlo de forma segura.",
|
||||
"autoshield_off_generic": "Tu nodo no está blindando automáticamente el coinbase minado en este momento.",
|
||||
"av_intro": "El software de minería suele marcarse como potencialmente no deseado. Sigue estos pasos para habilitar la minería en pool:",
|
||||
"av_open_security": "Abrir Seguridad de Windows",
|
||||
"av_step1": "Paso 1: Agregar exclusión",
|
||||
@@ -100,6 +178,17 @@
|
||||
"balance_shielded_fmt": "Protegido: %.8f",
|
||||
"balance_syncing_pct": "Sincronizando %.1f%%",
|
||||
"balance_transparent_fmt": "Transparente: %.8f",
|
||||
"baltab_market": "Mercado",
|
||||
"baltab_market_price_4dp": "Mercado: $%.4f",
|
||||
"baltab_market_price_8dp": "Mercado: $%.8f",
|
||||
"baltab_pct_change_24h": "%s%.1f%% 24h",
|
||||
"baltab_pct_of_total_zaddr": "%.0f%% del total · %d Z-addr",
|
||||
"baltab_shielded": "Blindado",
|
||||
"baltab_shielded_amount": "Blindado %.8f",
|
||||
"baltab_t_addresses_count": "%d T-addresses",
|
||||
"baltab_total_balance": "Saldo total",
|
||||
"baltab_transparent": "Transparente",
|
||||
"baltab_transparent_amount": "Transparente %.8f",
|
||||
"ban": "Bloquear",
|
||||
"banned_peers": "Nodos Bloqueados",
|
||||
"block": "Bloque",
|
||||
@@ -107,6 +196,12 @@
|
||||
"block_click_copy": "Clic para copiar",
|
||||
"block_click_next": "Clic para ver bloque siguiente",
|
||||
"block_click_prev": "Clic para ver bloque anterior",
|
||||
"block_db_reindex_body": "Esto suele ocurrir después de que una actualización del daemon cambia el formato en disco, o si el índice de bloques está dañado. Tu cartera y tus monedas están a salvo — el nodo simplemente no puede cargar la cadena, así que los saldos aparecen en cero.\n\nReconstruir vuelve a leer tus archivos de bloques existentes y puede tardar un rato (también reescanea tu cartera). No se descarga nada.",
|
||||
"block_db_reindex_confirm": "Reconstruir base de datos de bloques",
|
||||
"block_db_reindex_notify": "El nodo no puede leer su base de datos de bloques (suele pasar tras una actualización del daemon). Reconstrúyela para restaurar tu saldo — consulta el aviso, o Ajustes › Nodo.",
|
||||
"block_db_reindex_started": "Reconstruyendo la base de datos de bloques a partir de tus bloques — esto puede tardar un rato.",
|
||||
"block_db_reindex_title": "¿Reconstruir base de datos de bloques?",
|
||||
"block_db_reindex_warn": "El nodo no puede leer su base de datos de bloques.",
|
||||
"block_explorer": "Explorador de Bloques",
|
||||
"block_get_info": "Obtener Info del Bloque",
|
||||
"block_hash": "Hash del Bloque:",
|
||||
@@ -154,6 +249,10 @@
|
||||
"chat_accent_theme": "Tema",
|
||||
"chat_add_contact": "Añadir contacto",
|
||||
"chat_awaiting_key": "Esperando respuesta",
|
||||
"chat_blocked_desc": "Las conversaciones bloqueadas se eliminan y sus mensajes se descartan — antiguos y nuevos — hasta que las desbloquees. Desbloquear reimporta la conversación desde la cadena.",
|
||||
"chat_blocked_manage": "Bloqueadas",
|
||||
"chat_blocked_title": "Conversaciones bloqueadas",
|
||||
"chat_blocked_toast": "Conversación eliminada y bloqueada",
|
||||
"chat_bubble_minimal": "Mínima",
|
||||
"chat_bubble_rounded": "Redondeada",
|
||||
"chat_bubble_square": "Cuadrada",
|
||||
@@ -166,6 +265,16 @@
|
||||
"chat_contact_added": "Contacto añadido: renómbralo en Contactos",
|
||||
"chat_contact_request": "solicitud de contacto",
|
||||
"chat_copy_address_tip": "Clic para copiar la dirección",
|
||||
"chat_delete": "Eliminar conversación",
|
||||
"chat_delete_block": "Eliminar y bloquear",
|
||||
"chat_delete_body_prefix": "¿Eliminar tu copia local de la conversación con ",
|
||||
"chat_delete_body_suffix": "?",
|
||||
"chat_delete_confirm": "Eliminar",
|
||||
"chat_delete_failed": "No se pudo eliminar la conversación — no se cambió nada.",
|
||||
"chat_delete_local_note": "Esto solo afecta a este dispositivo — los mensajes permanecen en la cadena de bloques y la otra persona conserva su copia.",
|
||||
"chat_delete_revive_note": "«Eliminar» borra el historial en este dispositivo. Si esa persona vuelve a escribirte, la conversación reaparece.",
|
||||
"chat_delete_title": "¿Eliminar conversación?",
|
||||
"chat_deleted_toast": "Conversación eliminada",
|
||||
"chat_density_comfortable": "Cómoda",
|
||||
"chat_density_compact": "Compacta",
|
||||
"chat_emoji_color": "Color",
|
||||
@@ -189,6 +298,7 @@
|
||||
"chat_new_button": "Nuevo chat",
|
||||
"chat_new_message": "Mensaje",
|
||||
"chat_new_message_toast": "Nuevo mensaje de chat cifrado",
|
||||
"chat_new_needs_zaddr": "El chat necesita una dirección blindada (z) — las direcciones transparentes (t) no pueden recibir mensajes cifrados.",
|
||||
"chat_new_send": "Enviar solicitud",
|
||||
"chat_new_title": "Nuevo chat",
|
||||
"chat_new_zaddr": "Dirección z del destinatario",
|
||||
@@ -234,6 +344,8 @@
|
||||
"chat_ts_24h": "24 horas",
|
||||
"chat_ts_global": "Seguir global",
|
||||
"chat_ts_global_short": "Global",
|
||||
"chat_unblock": "Desbloquear",
|
||||
"chat_unblocked_toast": "Conversación desbloqueada",
|
||||
"chat_unhide": "Mostrar",
|
||||
"chat_unmute": "Reactivar",
|
||||
"chat_verify_key": "Clave de identidad: compárala para verificar",
|
||||
@@ -286,6 +398,7 @@
|
||||
"console": "Consola",
|
||||
"console_accents": "Acentos de color",
|
||||
"console_app": "App",
|
||||
"console_auto_focus": "Enfocar la entrada al abrir",
|
||||
"console_auto_scroll": "Auto-desplazamiento",
|
||||
"console_available_commands": "Comandos disponibles:",
|
||||
"console_backend_reference": "Referencia de Comandos del Backend",
|
||||
@@ -380,11 +493,15 @@
|
||||
"console_tab_completion": "Tab para completar",
|
||||
"console_text_colors": "Colores de texto",
|
||||
"console_toggle_accents": "Alternar acentos de color de línea",
|
||||
"console_toggle_auto_focus": "Coloca el cursor en el cuadro de comandos al abrir la pestaña Consola",
|
||||
"console_toggle_text_color": "Alternar colores de texto de línea",
|
||||
"console_type_help": "Escribe 'help' para ver los comandos disponibles",
|
||||
"console_welcome": "Bienvenido a la Consola de ObsidianDragon",
|
||||
"console_zoom_in": "Acercar",
|
||||
"console_zoom_out": "Alejar",
|
||||
"consolidate_desc": "Combina muchas entradas pequeñas en una sola nota blindada. Menos notas significa un archivo de cartera más pequeño y mejor privacidad.",
|
||||
"consolidate_funds_btn": "Consolidar",
|
||||
"consolidate_title": "Consolidar fondos",
|
||||
"contact_avatar": "AVATAR",
|
||||
"contact_avatar_bad_image": "No se pudo cargar esa imagen.",
|
||||
"contact_avatar_badge": "Insignia",
|
||||
@@ -414,6 +531,7 @@
|
||||
"contacts_shape_square": "Cuadrado",
|
||||
"contacts_shape_tab": "Pestaña",
|
||||
"copied": "¡Copiado!",
|
||||
"copied_to_clipboard": "Copiado al portapapeles",
|
||||
"copy": "Copiar",
|
||||
"copy_address": "Copiar Dirección Completa",
|
||||
"copy_error": "Copiar Error",
|
||||
@@ -429,9 +547,13 @@
|
||||
"daemon_maintenance_label": "MANTENIMIENTO",
|
||||
"daemon_none_bundled": "ninguno en esta compilación",
|
||||
"daemon_not_installed": "no instalado",
|
||||
"daemon_status_diff": "La versión difiere",
|
||||
"daemon_status_differ": "El binario instalado difiere de la versión incluida.",
|
||||
"daemon_status_match": "El binario instalado coincide con la versión integrada.",
|
||||
"daemon_status_missing": "No hay ningún daemon instalado — instala la versión incluida.",
|
||||
"daemon_status_none": "No instalado",
|
||||
"daemon_status_ok": "Actualizado",
|
||||
"daemon_update_active_tt": "El nodo actualmente instalado (activo)",
|
||||
"daemon_update_available": "Hay una nueva versión del nodo disponible",
|
||||
"daemon_update_body": "Esta versión de la cartera incluye un nodo DragonX más reciente que el que está instalado actualmente. Al actualizar se reemplaza el dragonxd instalado (y dragonx-cli/dragonx-tx), y luego se detiene y reinicia el nodo para que la nueva versión surta efecto. Recomendado: un nodo más reciente puede añadir funciones (p. ej. compatibilidad con frase de recuperación) que el antiguo no tiene.",
|
||||
"daemon_update_browse": "Explorar todas las versiones…",
|
||||
@@ -441,19 +563,28 @@
|
||||
"daemon_update_download_install": "Descargar e instalar",
|
||||
"daemon_update_downloading": "Descargando…",
|
||||
"daemon_update_failed": "Error en la actualización",
|
||||
"daemon_update_install_this": "Instalar esta versión",
|
||||
"daemon_update_installed": "Instalado:",
|
||||
"daemon_update_installed_ok": "Nodo instalado",
|
||||
"daemon_update_installing": "Instalando…",
|
||||
"daemon_update_keep": "Mantener actual",
|
||||
"daemon_update_later": "Más tarde",
|
||||
"daemon_update_latest": "Más reciente:",
|
||||
"daemon_update_latest_badge": "más reciente",
|
||||
"daemon_update_latest_tt": "La versión más reciente disponible",
|
||||
"daemon_update_loading": "Cargando versiones…",
|
||||
"daemon_update_no_notes": "No hay notas de la versión para esta versión.",
|
||||
"daemon_update_now": "Actualizar ahora",
|
||||
"daemon_update_prerelease_tt": "Versión preliminar / de prueba",
|
||||
"daemon_update_prompt_title": "¿Actualizar el daemon del nodo?",
|
||||
"daemon_update_reinstall": "Reinstalar",
|
||||
"daemon_update_released": "Publicada:",
|
||||
"daemon_update_restart_note": "Reinicia el daemon para empezar a ejecutar la nueva versión.",
|
||||
"daemon_update_restart_now": "Reiniciar el daemon ahora",
|
||||
"daemon_update_safe": "Tu cartera, tus claves y los datos de la blockchain no se ven afectados: solo se reemplazan los archivos del programa daemon. Si ejecutas deliberadamente un nodo personalizado, elige Mantener actual.",
|
||||
"daemon_update_status_current": "Esta versión está instalada",
|
||||
"daemon_update_status_latest": "Última versión",
|
||||
"daemon_update_status_older": "Anterior a tu versión instalada",
|
||||
"daemon_update_title": "¿Actualizar el daemon del nodo?",
|
||||
"daemon_update_unavailable_body": "No hay una compilación del nodo disponible para esta plataforma.",
|
||||
"daemon_update_unavailable_title": "Actualizaciones del nodo no disponibles",
|
||||
@@ -469,6 +600,13 @@
|
||||
"data_stale_tooltip": "El saldo puede estar desactualizado: la cartera no ha recibido una actualización reciente. Comprueba la conexión con tu nodo.",
|
||||
"date": "Fecha",
|
||||
"date_label": "Fecha:",
|
||||
"debug_gate_confirm": "Mostrar opciones de depuración",
|
||||
"debug_gate_incorrect": "PIN o contraseña incorrectos.",
|
||||
"debug_gate_pass_prompt": "Ingresa la contraseña de tu cartera para continuar:",
|
||||
"debug_gate_pin_prompt": "Ingresa tu PIN de desbloqueo para continuar:",
|
||||
"debug_gate_title": "¿Mostrar opciones de depuración?",
|
||||
"debug_gate_verifying": "Verificando…",
|
||||
"debug_gate_warning": "Estas son opciones avanzadas de diagnóstico y ajuste del nodo. Pueden exponer información sensible y cambiar el funcionamiento de tu nodo. Continúa solo si sabes lo que haces.",
|
||||
"debug_logging": "REGISTRO DE DEPURACIÓN",
|
||||
"decrypt_desc": "Se exportará el monedero, se reiniciará el daemon con un monedero nuevo sin cifrar y se reimportarán todas las claves. Esto puede tardar varios minutos según el tamaño del monedero.",
|
||||
"decrypt_error_title": "Error al descifrar",
|
||||
@@ -491,6 +629,7 @@
|
||||
"disconnected": "Desconectado",
|
||||
"dismiss": "Descartar",
|
||||
"display": "Pantalla",
|
||||
"done": "Listo",
|
||||
"download": "Descargar",
|
||||
"download_bootstrap": "Descargar Bootstrap",
|
||||
"dragonx_green": "DragonX (Verde)",
|
||||
@@ -544,6 +683,7 @@
|
||||
"explorer_tx_outputs": "Salidas",
|
||||
"explorer_tx_size": "Tamaño",
|
||||
"explorer_txid": "TxID:",
|
||||
"explorer_urls_hdr": "URLS",
|
||||
"export": "Exportar",
|
||||
"export_csv": "Exportar CSV",
|
||||
"export_keys_btn": "Exportar Claves",
|
||||
@@ -571,6 +711,151 @@
|
||||
"export_viewing_key": "Exportar Clave de Vista",
|
||||
"failed_create_shielded": "Error al crear dirección protegida",
|
||||
"failed_create_transparent": "Error al crear dirección transparente",
|
||||
"faq": "Preguntas frecuentes",
|
||||
"faq_d_mgmt_1_a": "El nodo integrado se inicia automáticamente con la cartera y se detiene cuando corresponde, así que normalmente no necesitas administrarlo a mano. Los controles avanzados (incluido reiniciar el daemon) están en Ajustes > Nodo y seguridad.",
|
||||
"faq_d_mgmt_1_q": "¿Cómo inicio o detengo el nodo?",
|
||||
"faq_d_mgmt_2_a": "Si conectas la cartera a un nodo que iniciaste tú mismo, esta opción decide si cerrar la cartera también apaga ese nodo. Déjala desactivada si quieres que tu nodo siga funcionando después de cerrar la cartera.",
|
||||
"faq_d_mgmt_2_q": "¿Qué hace «cerrar el daemon externo al salir»?",
|
||||
"faq_d_mgmt_3_a": "Los datos del nodo y la cartera están en el directorio de datos de DragonX, dentro de tu perfil de usuario (por ejemplo, en AppData en Windows o en tu carpeta personal en Linux/macOS). La cadena de bloques es la parte más grande; conserva suficiente espacio libre en disco para que pueda crecer.",
|
||||
"faq_d_mgmt_3_q": "¿Dónde se almacenan la cadena de bloques y los datos de la cartera?",
|
||||
"faq_d_mgmt_title": "Gestión del nodo",
|
||||
"faq_d_mine_1_a": "La pestaña Minería te permite minar con tu CPU. DragonX usa el algoritmo RandomX, diseñado para procesadores normales. Define el número de hilos e inicia — las recompensas se acreditan en tu cartera.",
|
||||
"faq_d_mine_1_q": "¿Cómo mino DragonX?",
|
||||
"faq_d_mine_2_a": "La minería solo envía las recompensas directamente a ti, pero solo paga cuando encuentras un bloque, lo cual es poco frecuente a menos que tengas mucho hashrate. La minería en pool comparte el trabajo con otros para pagos más pequeños y constantes. La pestaña Minería admite ambas.",
|
||||
"faq_d_mine_2_q": "Minería solo vs. en pool — ¿cuál elijo?",
|
||||
"faq_d_mine_3_a": "La sección de pool en la pestaña Minería tiene un botón «Actualizar minero…» que descarga, verifica e instala la última versión optimizada del minero de DragonX. Igual que el actualizador del nodo, cada descarga se verifica con checksum y firma antes de instalarse.",
|
||||
"faq_d_mine_3_q": "¿Cómo actualizo el minero?",
|
||||
"faq_d_mine_4_a": "El nodo actual puede ejecutar un servidor stratum integrado para que otros mineros apunten a tu equipo. Hay un interruptor para esto en Ajustes > Nodo y seguridad. Por defecto solo escucha localmente; exponerlo a otros equipos requiere abrirlo deliberadamente.",
|
||||
"faq_d_mine_4_q": "¿Puedo alojar un pool de minería?",
|
||||
"faq_d_mine_title": "Minería y pools",
|
||||
"faq_d_net_1_a": "Justo después de iniciar, el nodo necesita un momento para encontrar pares, así que un «0 pares» breve es normal. Si persiste, comprueba tu conexión a internet y que un firewall no esté bloqueando el nodo. El nodo encuentra pares automáticamente mediante semillas integradas.",
|
||||
"faq_d_net_1_q": "La cartera no muestra pares ni conexiones — ¿qué ocurre?",
|
||||
"faq_d_net_2_a": "Sí — los nodos de DragonX se comunican entre sí mediante TLS, por lo que las conexiones entre pares están cifradas. La cartera también se comunica con su propio nodo por un canal local seguro.",
|
||||
"faq_d_net_2_q": "¿Está cifrada mi conexión a la red?",
|
||||
"faq_d_net_title": "Pares y red",
|
||||
"faq_d_node_1_a": "El demonio, dragonxd, es el nodo completo de DragonX. Descarga y verifica toda la cadena de bloques, retransmite transacciones a la red y responde a las consultas de la cartera. Ejecutar tu propio nodo significa que no tienes que confiar en el servidor de nadie más.",
|
||||
"faq_d_node_1_q": "¿Qué es el demonio (dragonxd)?",
|
||||
"faq_d_node_2_a": "Por defecto, la cartera inicia y gestiona su propio nodo integrado (embebido) — no tienes que hacer nada. Si ya ejecutas dragonxd por tu cuenta, la cartera puede conectarse a ese nodo externo en su lugar. La cartera detecta en qué situación está y actúa en consecuencia.",
|
||||
"faq_d_node_2_q": "Nodo integrado vs. externo — ¿cuál es la diferencia?",
|
||||
"faq_d_node_3_a": "Un nodo completo valida cada regla de la cadena de bloques de forma independiente, así que tu cartera confía en las matemáticas en lugar de en un tercero. También refuerza la red. La contrapartida es el espacio en disco y una sincronización inicial.",
|
||||
"faq_d_node_3_q": "¿Por qué ejecutar un nodo completo?",
|
||||
"faq_d_node_title": "El nodo completo",
|
||||
"faq_d_perf_1_a": "La cadena de bloques es el elemento más grande y crece con el tiempo, así que conserva varios gigabytes libres. El nodo usa una caché de base de datos para mayor velocidad; los valores predeterminados incluidos están ajustados para equipos típicos. Los usuarios avanzados pueden ajustar la caché de base de datos del nodo editando dbcache en su archivo de configuración (DRAGONX.conf).",
|
||||
"faq_d_perf_1_q": "¿Cuánto disco usa el nodo y puedo ajustar la memoria?",
|
||||
"faq_d_perf_2_a": "Activa la descarga de arranque (bootstrap) (ofrecida en el asistente de configuración) para obtener una copia reciente y verificada de la cadena en lugar de validar cada bloque desde cero. Un disco rápido (SSD) también ayuda mucho.",
|
||||
"faq_d_perf_2_q": "¿Cómo hago más rápida la sincronización inicial?",
|
||||
"faq_d_perf_title": "Almacenamiento y rendimiento",
|
||||
"faq_d_sync_1_a": "Al iniciar, el nodo carga y valida bloques antes de poder atender a la cartera. La pantalla «Procesando bloques / Aplicando bloques para construir el estado actual de la cadena» es ese calentamiento. Es normal; la cartera se vuelve utilizable cuando el nodo termina e informa su altura.",
|
||||
"faq_d_sync_1_q": "¿Qué significa «Procesando bloques» al iniciar?",
|
||||
"faq_d_sync_2_a": "Para tus fondos blindados, el nodo mantiene «testigos» criptográficos que te permiten gastar de forma privada. Tras ciertos reinicios o actualizaciones, reconstruye esta caché reescaneando bloques recientes. En una cartera con muchas transacciones, esto puede tardar varios minutos y usar memoria adicional.\n\nNo está atascado — deja que termine. Forzar el cierre a la mitad solo hace que la reconstrucción se reinicie la próxima vez.",
|
||||
"faq_d_sync_2_q": "¿Qué es «Reconstruyendo caché de testigos»?",
|
||||
"faq_d_sync_3_a": "Cerca de la punta de la cadena, el trabajo que escanea tu cartera (comprobando saldos y notas) compite con el nodo por el mismo bloqueo interno, lo que puede ralentizar la conexión de los últimos bloques en una cartera grande. Las versiones recientes limitan ese escaneo para que el nodo pueda ponerse al día. Quedarte en una pestaña más ligera (o simplemente esperar) permite que termine más rápido.",
|
||||
"faq_d_sync_3_q": "¿Por qué la sincronización es lenta cuando ya casi estoy al día?",
|
||||
"faq_d_sync_4_a": "La primera sincronización descarga y verifica toda la cadena y puede tardar según tu conexión y disco. Puedes acelerarla drásticamente activando la descarga de arranque (bootstrap) en el asistente de configuración, que obtiene una copia reciente y verificada de los datos de la cadena.",
|
||||
"faq_d_sync_4_q": "¿Cuánto dura la sincronización inicial?",
|
||||
"faq_d_sync_title": "Sincronización y cadena de bloques",
|
||||
"faq_d_trbl_1_a": "Normalmente no. En una cartera grande, el nodo puede pasar varios minutos reconstruyendo testigos blindados o aplicando bloques, durante lo cual parece pausado y puede tardar en responder. Dale tiempo — el registro de depuración (Ajustes) muestra progreso constante si está funcionando.\n\nEvita forzar el cierre durante esta fase; reinicia el trabajo desde cero en el próximo arranque.",
|
||||
"faq_d_trbl_1_q": "El nodo parece atascado en «Activando la mejor cadena» — ¿está congelado?",
|
||||
"faq_d_trbl_2_a": "Si un archivo de cartera se recupera pero falta parte de sus datos de claves, el nodo puede abrirlo en un modo «degradado» limitado, donde no puede derivar nuevas direcciones ni blindar fondos. La solución más segura es restaurar desde tu frase de recuperación en una cartera nueva.",
|
||||
"faq_d_trbl_2_q": "¿Qué es el «modo degradado»?",
|
||||
"faq_d_trbl_3_a": "Un reescaneo hace que la cartera vuelva a leer la cadena para redescubrir tus transacciones (útil tras importar claves). Una reindexación reconstruye la base de datos de la cadena de bloques del nodo y solo es necesaria si esa base de datos está dañada o es incompatible tras un cambio de versión. Ambas pueden tardar un rato; inícialas desde Ajustes > Nodo y seguridad.",
|
||||
"faq_d_trbl_3_q": "¿Cuándo debo reindexar o reescanear?",
|
||||
"faq_d_trbl_4_a": "El uso de memoria aumenta durante trabajo intenso, como una reconstrucción de testigos o una sincronización inicial, y vuelve a bajar al terminar. Un uso de memoria alto y sostenido en reposo es inusual — reiniciar la cartera lo libera. En un equipo con poca RAM, asegúrate de que otras aplicaciones que consumen mucha memoria no compitan por ella.",
|
||||
"faq_d_trbl_4_q": "El nodo usa mucha memoria — ¿es normal?",
|
||||
"faq_d_trbl_title": "Solución de problemas",
|
||||
"faq_d_upd_1_a": "Abre Ajustes > Nodo y seguridad > Binario del demonio y usa «Buscar actualizaciones…». La cartera descarga la última versión verificada del nodo y la instala; la nueva versión se aplica la próxima vez que se inicie el demonio.",
|
||||
"faq_d_upd_1_q": "¿Cómo actualizo el nodo?",
|
||||
"faq_d_upd_2_a": "Sí — el diálogo de actualización enumera todas las versiones para que puedas elegir una compilación específica o anterior. Ten cuidado al degradar: un nodo más antiguo puede no aceptar datos de la cadena de bloques escritos por uno más reciente y podría necesitar una reindexación.",
|
||||
"faq_d_upd_2_q": "¿Puedo instalar una versión específica del nodo?",
|
||||
"faq_d_upd_3_a": "Cada archivo de nodo descargado se verifica con un checksum y una firma criptográfica antes de instalarse. Si la verificación falla, la cartera rechaza la actualización.",
|
||||
"faq_d_upd_3_q": "¿Es segura la actualización?",
|
||||
"faq_d_upd_title": "Actualizar el nodo",
|
||||
"faq_group_daemon": "Demonio",
|
||||
"faq_group_wallet": "Cartera",
|
||||
"faq_intro": "Respuestas sobre tu cartera y el nodo de DragonX. Busca, o explora por tema abajo.",
|
||||
"faq_intro_lite": "Respuestas sobre tu cartera Lite. Busca, o explora por tema abajo.",
|
||||
"faq_l_gs_1_a": "ObsidianDragonLite es la cartera ligera de DragonX. En lugar de ejecutar un nodo completo, se conecta a un servidor lite de DragonX, así que inicia rápido y no descarga toda la cadena de bloques.\n\nGestiona tus claves, saldos y envíos igual que la cartera completa — consulta la sección «Cartera Lite» más abajo para ver cómo funciona la conexión al servidor y la contrapartida de privacidad.",
|
||||
"faq_l_gs_1_q": "¿Qué es ObsidianDragonLite?",
|
||||
"faq_l_lite_1_a": "La cartera Lite se comunica con un servidor lite de DragonX en lugar de ejecutar su propio nodo completo. Eso la hace rápida de iniciar y ligera en disco y ancho de banda, porque no descarga ni verifica toda la cadena de bloques por sí misma. Tus claves privadas siempre permanecen en tu dispositivo — nunca se envían al servidor.",
|
||||
"faq_l_lite_1_q": "¿En qué se diferencia la cartera Lite de la cartera completa?",
|
||||
"faq_l_lite_2_a": "Abre Ajustes y elige un servidor. Puedes cambiar de servidor en cualquier momento y la cartera se resincroniza desde el nuevo. Si un servidor va lento o no responde, prueba con otro.",
|
||||
"faq_l_lite_2_q": "¿Cómo elijo o cambio el servidor?",
|
||||
"faq_l_lite_3_a": "Tus claves nunca salen de tu dispositivo, y los importes protegidos permanecen cifrados. Pero el servidor lite sí ve qué direcciones consulta tu cartera y tu dirección IP, por lo que puede vincular tus direcciones entre sí — un compromiso entre comodidad y privacidad. Para la máxima privacidad, usa la cartera de full node, que verifica la cadena por sí misma y no revela tus direcciones a un servidor.",
|
||||
"faq_l_lite_3_q": "¿Es la cartera Lite tan privada como el full node?",
|
||||
"faq_l_lite_title": "Cartera Lite",
|
||||
"faq_l_sec_1_a": "Ve a Configuración > Cartera y define una contraseña en la sección de seguridad. El cifrado protege tus claves privadas en el disco, de modo que alguien con acceso a los archivos aún no podría gastar tus monedas.\n\nElige una contraseña segura y no la pierdas — no hay forma de recuperar una cartera cifrada sin ella.",
|
||||
"faq_no_results": "Sin resultados. Prueba con otro término de búsqueda.",
|
||||
"faq_open_tooltip": "Ayuda y preguntas frecuentes",
|
||||
"faq_search_hint": "Buscar en las preguntas frecuentes…",
|
||||
"faq_title": "Ayuda y preguntas frecuentes",
|
||||
"faq_w_addr_1_a": "Las direcciones transparentes (empiezan con R o t) funcionan como la mayoría de las monedas: los montos y las direcciones son públicos en la cadena de bloques.\n\nLas direcciones blindadas (direcciones z, empiezan con zs) usan criptografía de conocimiento cero: el monto, el remitente y el destinatario quedan cifrados en la cadena. Los saldos en direcciones blindadas son privados.",
|
||||
"faq_w_addr_1_q": "¿Cuál es la diferencia entre direcciones transparentes y blindadas?",
|
||||
"faq_w_addr_2_a": "Prefiere las direcciones blindadas (z) siempre que sea posible: mantienen privados tu saldo y tu historial de pagos. DragonX es una moneda de privacidad y lo blindado es la opción predeterminada para los fondos recibidos.\n\nUsa una dirección transparente solo cuando un servicio con el que interactúas no admita direcciones blindadas.",
|
||||
"faq_w_addr_2_q": "¿Qué tipo de dirección debería usar?",
|
||||
"faq_w_addr_3_a": "Para las direcciones blindadas, reutilizarlas está bien y no filtra el historial. Para las direcciones transparentes, usar una dirección nueva por cada pago mejora la privacidad. Crea nuevas direcciones desde la pestaña Recibir.",
|
||||
"faq_w_addr_3_q": "¿Debería reutilizar una dirección o crear una nueva?",
|
||||
"faq_w_addr_4_a": "Blindar mueve monedas de una dirección transparente a una blindada, haciéndolas privadas. Las monedas recién minadas llegan transparentes y se blindan automáticamente por ti.\n\nTambién puedes blindar manualmente desde la cartera; blindar es solo una transacción especial, así que tarda el tiempo de confirmación normal en completarse.",
|
||||
"faq_w_addr_4_q": "¿Qué es \"blindar\"?",
|
||||
"faq_w_addr_title": "Direcciones y privacidad",
|
||||
"faq_w_bal_1_a": "Mientras tu cartera aún se está sincronizando, tu saldo está incompleto y puede mostrar 0 — todavía no ha escaneado todas tus transacciones. Se completa cuando termina la sincronización.\n\nObserva la barra de estado en la parte inferior: muestra el estado de la conexión y la altura de bloque. Una vez que la sincronización termina, tu saldo es exacto.",
|
||||
"faq_w_bal_1_q": "¿Por qué mi saldo está en 0 o no se actualiza?",
|
||||
"faq_w_bal_2_a": "Cada bloque nuevo minado sobre el bloque que contiene tu transacción añade una confirmación. Más confirmaciones significan que el pago está más firmemente asentado. Los fondos recibidos se pueden gastar después de la primera confirmación.",
|
||||
"faq_w_bal_2_q": "¿Qué son las confirmaciones?",
|
||||
"faq_w_bal_3_a": "El total incluye fondos que aún se están confirmando o están temporalmente bloqueados (por ejemplo, monedas en proceso de blindarse). El saldo disponible es lo que puedes enviar ahora mismo. Ambos convergen a medida que las transacciones se confirman.",
|
||||
"faq_w_bal_3_q": "¿Cuál es la diferencia entre el saldo total y el disponible?",
|
||||
"faq_w_bal_title": "Saldo y sincronización",
|
||||
"faq_w_chat_1_a": "Chat es un mensajero cifrado en la cadena, integrado en la cartera. Los mensajes se envían como memos blindados privados, así que solo tú y tu contacto pueden leerlos.",
|
||||
"faq_w_chat_1_q": "¿Qué es la función de Chat?",
|
||||
"faq_w_chat_2_a": "Tu identidad de chat se deriva de la frase de recuperación de tu cartera, así que viaja con tu cartera — restaura la cartera y tu identidad vuelve. No creas una cuenta ni una contraseña aparte.",
|
||||
"faq_w_chat_2_q": "¿Cómo funciona mi identidad de chat?",
|
||||
"faq_w_chat_3_a": "Añade un contacto por su dirección en la pestaña Contactos, opcionalmente con un nombre y un avatar. Los contactos se pueden mantener privados solo para la cartera actual o compartirse entre todas las carteras que abras en este equipo.",
|
||||
"faq_w_chat_3_q": "¿Cómo funcionan los contactos?",
|
||||
"faq_w_chat_4_a": "Abre una conversación y usa los íconos de su encabezado. Ocultar (el ojo) la quita de la lista pero conserva todos los mensajes — un mensaje nuevo la vuelve a mostrar. Eliminar (la papelera) abre un diálogo: \"Eliminar\" borra el historial en este dispositivo (si esa persona te escribe de nuevo, la conversación vuelve), mientras que \"Eliminar y bloquear\" también impide sus mensajes futuros hasta que la desbloquees desde la lista \"Bloqueados\" encima de la lista de conversaciones. Todo esto es local a este dispositivo — los mensajes permanecen en la cadena de bloques y la otra persona conserva su propia copia, así que no es un \"deshacer envío\".",
|
||||
"faq_w_chat_4_q": "¿Cómo oculto, elimino o bloqueo una conversación?",
|
||||
"faq_w_chat_title": "Chat y contactos",
|
||||
"faq_w_gs_1_a": "ObsidianDragon es una cartera de nodo completo para DragonX (DRGX). Gestiona tus monedas y, en la versión de escritorio, ejecuta un nodo completo de DragonX (el daemon) en segundo plano para que tu cartera verifique la cadena de bloques por sí misma en lugar de confiar en un servidor de terceros.\n\nPiénsalo como dos partes que trabajan juntas: la cartera (claves, saldos, envíos) y el nodo (la cadena de bloques y la red). Las preguntas aquí están divididas del mismo modo — mira las pestañas Cartera y Daemon arriba.",
|
||||
"faq_w_gs_1_q": "¿Qué es ObsidianDragon?",
|
||||
"faq_w_gs_2_a": "En el primer inicio, el asistente de configuración crea una cartera por ti automáticamente. Las carteras nuevas están respaldadas por una frase de recuperación secreta (una lista de palabras). Anota esa frase y guárdala sin conexión — es la única forma de restaurar tus fondos si pierdes este equipo.\n\nPuedes revisar o respaldar la frase en cualquier momento desde Configuración > Copia de seguridad y datos > Frase de recuperación.",
|
||||
"faq_w_gs_2_q": "¿Cómo creo una nueva cartera?",
|
||||
"faq_w_gs_3_a": "Usa el flujo de restauración con frase de recuperación si tienes una frase de recuperación. (En la versión de nodo completo también puedes copiar un archivo wallet.dat existente en el directorio de datos de la cartera antes de iniciarla.)\n\nDespués de restaurar, la cartera vuelve a escanear para encontrar tus transacciones pasadas, así que tu saldo e historial pueden tardar un poco en aparecer la primera vez.",
|
||||
"faq_w_gs_3_q": "¿Cómo restauro una cartera que ya tengo?",
|
||||
"faq_w_gs_4_a": "El asistente te permite elegir una apariencia/tema, configurar el cifrado y un PIN y, en la versión de nodo completo, descargar opcionalmente un bootstrap para acelerar la primera sincronización. Puedes omitir cualquier paso y cambiar todo esto más tarde en Configuración.",
|
||||
"faq_w_gs_4_q": "¿Qué hace la configuración inicial?",
|
||||
"faq_w_gs_title": "Primeros pasos",
|
||||
"faq_w_sec_1_a": "Ve a Configuración > Nodo y seguridad y define una contraseña (la sección SEGURIDAD). El cifrado protege tus claves privadas en el disco, de modo que alguien con acceso a los archivos aún no podría gastar tus monedas.\n\nElige una contraseña segura y no la pierdas — no hay forma de recuperar una cartera cifrada sin ella.",
|
||||
"faq_w_sec_1_q": "¿Cómo cifro mi cartera?",
|
||||
"faq_w_sec_2_a": "El PIN bloquea la pantalla de la app para que los saldos y las acciones queden ocultos cuando te alejas. Es un bloqueo de conveniencia adicional al cifrado; lo configuras durante el asistente o en Configuración, después de cifrar la cartera.",
|
||||
"faq_w_sec_2_q": "¿Qué es el PIN o la pantalla de bloqueo?",
|
||||
"faq_w_sec_3_a": "No. El cifrado de la cartera no se puede eludir. Si aún conservas tu frase de recuperación, puedes restaurar la cartera a partir de ella en una cartera nueva y establecer una contraseña nueva. Sin la contraseña ni la frase de recuperación, los fondos no se pueden recuperar.",
|
||||
"faq_w_sec_3_q": "Olvidé mi contraseña — ¿se puede recuperar?",
|
||||
"faq_w_sec_title": "Seguridad y cifrado",
|
||||
"faq_w_seed_1_a": "Ve a Configuración > Copia de seguridad y datos > Frase de recuperación. Anota las palabras en papel en orden y guárdalas en un lugar seguro y sin conexión. Cualquiera que tenga la frase puede gastar tus monedas, así que nunca la guardes en una foto, un correo o una nota en la nube.",
|
||||
"faq_w_seed_1_q": "¿Cómo respaldo mi frase de recuperación?",
|
||||
"faq_w_seed_2_a": "Las carteras antiguas (heredadas) no se basaban en una frase de recuperación. La cartera puede migrar una cartera heredada a una moderna con frase de recuperación: crea una nueva cartera con frase y transfiere tus fondos a ella. Busca \"Migrar a una cartera con frase…\" en Configuración > Copia de seguridad y datos. Esta función necesita la versión actual del daemon.",
|
||||
"faq_w_seed_2_q": "Mi cartera no tiene frase de recuperación — ¿puedo añadir una?",
|
||||
"faq_w_seed_3_a": "Usa el flujo de restauración con frase de recuperación al configurar una cartera e ingresa tus palabras en orden. Tu cartera luego vuelve a escanear para reconstruir tu saldo e historial, lo cual puede tardar un poco la primera vez.",
|
||||
"faq_w_seed_3_q": "¿Cómo restauro a partir de mi frase de recuperación?",
|
||||
"faq_w_seed_4_a": "Tu frase de recuperación es el respaldo principal y basta para restaurarlo todo. Una copia de tu archivo de cartera es un respaldo secundario práctico que además conserva las etiquetas y la configuración. Mantén cualquier respaldo sin conexión y privado.",
|
||||
"faq_w_seed_4_q": "¿Debería respaldar también wallet.dat?",
|
||||
"faq_w_seed_title": "Frase de recuperación y copia de seguridad",
|
||||
"faq_w_send_1_a": "Abre la pestaña Enviar, pega la dirección del destinatario, ingresa un monto y confirma. Si la cartera está cifrada, se te pedirá desbloquearla para la transacción.\n\nLos envíos a direcciones blindadas son privados; los envíos a direcciones transparentes son públicos.",
|
||||
"faq_w_send_1_q": "¿Cómo envío fondos?",
|
||||
"faq_w_send_2_a": "Las comisiones de DragonX son muy bajas y se establecen automáticamente. La comisión se muestra antes de confirmar. Normalmente no necesitas cambiarla.",
|
||||
"faq_w_send_2_q": "¿Qué comisión pago?",
|
||||
"faq_w_send_3_a": "Sí — al enviar a una dirección blindada (z) puedes incluir un memo cifrado. Solo el destinatario puede leerlo. Las direcciones transparentes no admiten memos.",
|
||||
"faq_w_send_3_q": "¿Puedo adjuntar un mensaje a un pago?",
|
||||
"faq_w_send_4_a": "Una transacción está \"pendiente\" hasta que se mina en un bloque y obtiene confirmaciones. Esto suele tardar uno o dos minutos. Las transacciones blindadas también necesitan que tu cartera esté sincronizada para construir la prueba.\n\nSi un envío permanece pendiente durante un tiempo inusualmente largo, verifica que tu cartera esté conectada y sincronizada (mira la barra de estado en la parte inferior de la ventana).",
|
||||
"faq_w_send_4_q": "Envié un pago pero dice pendiente — ¿por qué?",
|
||||
"faq_w_send_title": "Envío y recepción",
|
||||
"faq_w_set_1_a": "Abre Ajustes > Apariencia para elegir un tema y un color de acento. Se incluyen varios estilos claros y oscuros, y los efectos como el desenfoque se pueden desactivar en equipos menos potentes.",
|
||||
"faq_w_set_1_q": "¿Cómo cambio el tema?",
|
||||
"faq_w_set_2_a": "Sí — Ajustes > Apariencia tiene un selector de idioma. La cartera incluye varias traducciones; lo que aún no esté traducido se muestra en inglés.",
|
||||
"faq_w_set_2_q": "¿Puedo cambiar el idioma?",
|
||||
"faq_w_set_3_a": "Usa la opción de escala de fuente en Ajustes > Apariencia. Escala toda la interfaz, lo cual es útil en pantallas de alta resolución (HiDPI) donde la app podría verse pequeña.",
|
||||
"faq_w_set_3_q": "El texto es demasiado pequeño (o grande) — ¿puedo escalarlo?",
|
||||
"faq_w_set_4_a": "Activa la obtención de precios en Ajustes. La cartera mostrará entonces un valor fiat aproximado junto a los saldos y puede mostrar un pequeño gráfico de mercado. Los precios son solo informativos.",
|
||||
"faq_w_set_4_q": "¿Cómo muestro precios en moneda fiat?",
|
||||
"faq_w_set_title": "Ajustes y apariencia",
|
||||
"favorite_address": "Marcar como favorito",
|
||||
"fee": "Comisión",
|
||||
"fee_high": "Alta",
|
||||
@@ -593,6 +878,63 @@
|
||||
"general": "General",
|
||||
"generating": "Generando",
|
||||
"go_to_receive": "Ir a Recibir",
|
||||
"grpa_current_block_paren": "(Actual: %d)",
|
||||
"grpa_days_ago": "hace %lld días",
|
||||
"grpa_dbg_addrman": "Seguimiento y gestión de direcciones de pares",
|
||||
"grpa_dbg_alert": "Mensajes del sistema de alertas",
|
||||
"grpa_dbg_bench": "Tiempos de referencia de las operaciones",
|
||||
"grpa_dbg_coindb": "Operaciones de lectura/escritura de la base de datos de monedas",
|
||||
"grpa_dbg_db": "Operaciones de Berkeley DB",
|
||||
"grpa_dbg_estimatefee": "Algoritmo de estimación de comisiones",
|
||||
"grpa_dbg_http": "Actividad del servidor RPC HTTP",
|
||||
"grpa_dbg_libevent": "Biblioteca de red Libevent",
|
||||
"grpa_dbg_lock": "Depuración de contención de bloqueos",
|
||||
"grpa_dbg_mempool": "Actividad del pool de memoria de transacciones",
|
||||
"grpa_dbg_net": "Conexiones y mensajes de red",
|
||||
"grpa_dbg_paymentdisclosure": "Protocolo de divulgación de pagos",
|
||||
"grpa_dbg_pow": "Actividad de minería por prueba de trabajo",
|
||||
"grpa_dbg_proxy": "Conexiones de proxy SOCKS5",
|
||||
"grpa_dbg_prune": "Operaciones de poda de bloques",
|
||||
"grpa_dbg_rand": "Generación de números aleatorios",
|
||||
"grpa_dbg_reindex": "Progreso de reindexación de la cadena de bloques",
|
||||
"grpa_dbg_rpc": "Procesamiento de comandos RPC",
|
||||
"grpa_dbg_selectcoins": "Selección de monedas para transacciones",
|
||||
"grpa_dbg_tor": "Integración de Tor e información de circuitos",
|
||||
"grpa_dbg_zmq": "Sistema de notificaciones ZeroMQ",
|
||||
"grpa_dbg_zrpc": "Operaciones RPC blindadas (z-addr)",
|
||||
"grpa_enter_private_key_to_import": "Introduce una clave privada para importar.",
|
||||
"grpa_error_prefix": "Error: ",
|
||||
"grpa_hr_ago": "hace %lld h",
|
||||
"grpa_invalid_response_from_daemon": "Respuesta no válida del daemon",
|
||||
"grpa_invalid_suffix": " (no válido)",
|
||||
"grpa_min_ago": "hace %lld min",
|
||||
"grpa_sec_ago": "hace %lld s",
|
||||
"grpa_seed_demo_chat": "Chat de demostración con semilla",
|
||||
"grpa_showing_first_100_of": "... mostrando los primeros 100 de %d",
|
||||
"grpa_tab_about": "Acerca de",
|
||||
"grpa_tab_appearance": "Apariencia",
|
||||
"grpa_tab_backup_data": "Copia de seguridad y datos",
|
||||
"grpa_tab_chat": "Chat",
|
||||
"grpa_tab_explorer": "Explorador",
|
||||
"grpa_tab_node_security": "Nodo y seguridad",
|
||||
"grpa_tab_wallet": "Cartera",
|
||||
"grpa_unexpected_getblockhash_result": "resultado inesperado de getblockhash",
|
||||
"grpb_copy": "Copiar",
|
||||
"grpb_max": "Máx.",
|
||||
"grpb_new_badge_suffix": " [NUEVO]",
|
||||
"grpb_preview_msg_payment_through": "¿Se realizó el pago? 🙂",
|
||||
"grpb_preview_msg_sending_rest": "Enviando el resto ahora 👍",
|
||||
"grpb_preview_msg_yep_confirmed": "Sí — acabo de confirmarlo ✅",
|
||||
"grpb_selected_suffix": "\n(seleccionado)",
|
||||
"grpb_tooltip_address_balance": "%s\nSaldo: %.8f %s%s",
|
||||
"grpb_undo_clear": "Deshacer borrado",
|
||||
"grpc_benchmark_inconclusive": "Prueba de rendimiento no concluyente: no se registraron muestras de tasa de hash. Comprueba la conexión con el pool e inténtalo de nuevo.",
|
||||
"grpc_benchmark_takes_secs": "La prueba de rendimiento tarda ~%ds e interrumpe la minería. Haz clic de nuevo para empezar.",
|
||||
"grpc_bootstrap_failed": "Error en el arranque inicial",
|
||||
"grpc_bootstrap_not_initialized": "Arranque inicial no inicializado",
|
||||
"grpc_hashrate_fee": "%s %s%% de comisión",
|
||||
"grpc_key_not_available": "Clave no disponible para esta dirección",
|
||||
"grpc_na": "N/D",
|
||||
"height": "Altura",
|
||||
"help": "Ayuda",
|
||||
"hidden_tag": " (oculto)",
|
||||
@@ -817,9 +1159,27 @@
|
||||
"memo_optional": "MEMO (OPCIONAL)",
|
||||
"memo_upper": "MEMO",
|
||||
"memo_z_only": "Nota: Los memos solo están disponibles al enviar a direcciones blindadas (z)",
|
||||
"merge_addr_created": "Dirección blindada creada.",
|
||||
"merge_advanced": "Avanzado",
|
||||
"merge_back": "Atrás",
|
||||
"merge_batch_fmt": "Fusiona hasta %d entradas por ejecución — repite para completar el resto.",
|
||||
"merge_confirm_btn": "Confirmar",
|
||||
"merge_confirm_fmt": "¿Consolidar ~%s DRGX de %d entrada(s) en %s?",
|
||||
"merge_create_zaddr": "Crear dirección blindada",
|
||||
"merge_creating": "Creando dirección…",
|
||||
"merge_description": "Fusiona múltiples UTXOs en una sola dirección protegida. Esto puede ayudar a reducir el tamaño de la cartera y mejorar la privacidad.",
|
||||
"merge_fee_hint": "Comisión de red para esta transacción.",
|
||||
"merge_funds": "Fusionar Fondos",
|
||||
"merge_max_inputs": "Máximo de entradas por lote",
|
||||
"merge_no_spendable": "Aún no hay entradas disponibles para consolidar.",
|
||||
"merge_progress": "Consolidando… esto puede tardar varios minutos. Puedes cerrar esta ventana.",
|
||||
"merge_scope_fmt": "%d entradas transparentes + %d blindadas · ~%s DRGX disponibles",
|
||||
"merge_scope_loading": "Comprobando tus entradas…",
|
||||
"merge_send_failed": "Error al fusionar: ",
|
||||
"merge_source": "Consolidar",
|
||||
"merge_src_both": "Ambas",
|
||||
"merge_src_shielded": "Blindadas",
|
||||
"merge_src_transparent": "Transparentes",
|
||||
"merge_started": "Operación de fusión iniciada",
|
||||
"merge_title": "Fusionar a Dirección",
|
||||
"mig_adopt": "Hacer que esta sea mi cartera",
|
||||
@@ -1059,6 +1419,13 @@
|
||||
"peers_upper": "NODOS",
|
||||
"peers_version": "Versión",
|
||||
"pending": "Pendiente",
|
||||
"picker_dat_count": "%d archivo(s) de cartera en esta carpeta",
|
||||
"picker_dat_none": "No hay archivos de cartera directamente en esta carpeta",
|
||||
"picker_empty": "Esta carpeta no tiene subcarpetas ni archivos de cartera.",
|
||||
"picker_home": "Carpeta de inicio",
|
||||
"picker_select": "Escanear esta carpeta",
|
||||
"picker_title": "Selecciona una carpeta para escanear",
|
||||
"picker_up": "Subir un nivel",
|
||||
"pin_change_desc": "Cambia tu PIN de desbloqueo. Necesitas tu PIN actual y un nuevo PIN.",
|
||||
"pin_confirm_new_label": "Confirmar nuevo PIN:",
|
||||
"pin_current_label": "PIN actual:",
|
||||
@@ -1195,6 +1562,7 @@
|
||||
"save_z_transactions": "Guardar Z-tx en lista",
|
||||
"sb_auth_failed": "Autenticación fallida — verifique rpcuser/rpcpassword",
|
||||
"sb_block": "Bloque: %d",
|
||||
"sb_block_db_unreadable": "Base de datos de bloques ilegible — es necesario reconstruirla",
|
||||
"sb_building_witnesses": "Estableciendo testigos",
|
||||
"sb_building_witnesses_pct": "Reconstruyendo testigos %.0f%%",
|
||||
"sb_connecting_daemon": "Conectando a dragonxd...",
|
||||
@@ -1210,6 +1578,7 @@
|
||||
"sb_dragonxd_stopped": "dragonxd detenido",
|
||||
"sb_dragonxd_stopping": "Deteniendo dragonxd...",
|
||||
"sb_extracting_sapling": "Extrayendo parámetros Sapling...",
|
||||
"sb_finishing_repair": "Terminando la reparación de la cartera — reescaneando…",
|
||||
"sb_importing_keys": "Importando claves",
|
||||
"sb_loading_config": "Cargando configuración...",
|
||||
"sb_mining_hs": "%.1f H/s",
|
||||
@@ -1231,6 +1600,7 @@
|
||||
"sb_waiting_config": "Esperando configuración del daemon...",
|
||||
"sb_waiting_daemon": "Esperando a dragonxd...",
|
||||
"sb_waiting_daemon_err": "Esperando a dragonxd — %s",
|
||||
"sb_wallet_needs_recovery": "Reparación de cartera disponible",
|
||||
"sb_warming_up": "Calentando...",
|
||||
"sb_witness_cache": "Reconstruyendo testigos",
|
||||
"scale_effects": "ESCALA Y EFECTOS",
|
||||
@@ -1240,6 +1610,45 @@
|
||||
"screenshot_sweep_full": "Barrido completo de la interfaz",
|
||||
"search_icons": "Buscar iconos...",
|
||||
"search_placeholder": "Buscar...",
|
||||
"sec_changing_passphrase": "Cambiando la frase de contraseña...",
|
||||
"sec_changing_pin": "Cambiando el PIN...",
|
||||
"sec_couldnt_lock_wallet": "No se pudo bloquear la cartera; sigue desbloqueada. Comprueba la conexión con el daemon.",
|
||||
"sec_encrypted_backup_suffix": "\nCopia de seguridad cifrada: wallet.dat.encrypted.bak",
|
||||
"sec_encrypting_wallet": "Cifrando la cartera...",
|
||||
"sec_encryption_did_not_complete": "El cifrado de la cartera no se completó; tu cartera NO está cifrada. Abre Ajustes para terminar de cifrarla.",
|
||||
"sec_encryption_failed_prefix": "Error de cifrado: ",
|
||||
"sec_failed_prefix": "Error: ",
|
||||
"sec_failed_to_create_vault": "No se pudo crear la bóveda",
|
||||
"sec_importing_keys_rescanning": "Importando claves y volviendo a escanear la cadena de bloques; la cartera se puede usar mientras tanto",
|
||||
"sec_incorrect_current_pin": "PIN actual incorrecto",
|
||||
"sec_incorrect_passphrase_decrypt": "Frase de contraseña incorrecta",
|
||||
"sec_incorrect_passphrase_pin_setup": "Frase de contraseña incorrecta",
|
||||
"sec_incorrect_pin_remove": "PIN incorrecto",
|
||||
"sec_internal_error_change_pin": "Error interno",
|
||||
"sec_internal_error_remove_pin": "Error interno",
|
||||
"sec_mode_passphrase": " Frase de contraseña",
|
||||
"sec_not_connected_to_daemon": "Sin conexión con el daemon",
|
||||
"sec_not_connected_to_daemon_pin": "Sin conexión con el daemon",
|
||||
"sec_passphrase_changed_successfully": "Frase de contraseña cambiada correctamente",
|
||||
"sec_pin_changed_successfully": "PIN cambiado correctamente",
|
||||
"sec_pin_removed": "PIN eliminado",
|
||||
"sec_pin_set_successfully": "PIN configurado correctamente",
|
||||
"sec_restart_daemon_for_encryption": "Reinicia el daemon para que el cifrado surta efecto.",
|
||||
"sec_too_many_attempts_wait": "Demasiados intentos. Espera %.0f segundos...",
|
||||
"sec_total_elapsed_fmt": "Tiempo total: %dm %02ds",
|
||||
"sec_unlock_button": "Desbloquear",
|
||||
"sec_unlock_failed_prefix": "Error al desbloquear: ",
|
||||
"sec_unlocking_fmt": "Desbloqueando%s",
|
||||
"sec_use_passphrase_instead": "Usar frase de contraseña en su lugar",
|
||||
"sec_use_pin_instead": "Usar PIN en su lugar",
|
||||
"sec_verifying_passphrase": "Verificando la frase de contraseña...",
|
||||
"sec_verifying_pin": "Verificando el PIN...",
|
||||
"sec_wallet_decrypted_all_keys_imported": "¡Cartera descifrada correctamente! Todas las claves importadas.",
|
||||
"sec_wallet_encrypted_and_pin_set": "Cartera cifrada y PIN configurado",
|
||||
"sec_wallet_encrypted_but_pin_vault_failed": "Cartera cifrada, pero falló la bóveda del PIN",
|
||||
"sec_wallet_encrypted_restarting_daemon": "Cartera cifrada. Reiniciando el daemon...",
|
||||
"sec_wallet_encrypted_successfully": "Cartera cifrada correctamente",
|
||||
"sec_wallet_locked_title": "Cartera bloqueada",
|
||||
"security": "SEGURIDAD",
|
||||
"seed_backup_button": "Frase de recuperación",
|
||||
"seed_backup_close": "Cerrar",
|
||||
@@ -1263,6 +1672,7 @@
|
||||
"send_amount": "Cantidad",
|
||||
"send_amount_details": "DETALLES DE CANTIDAD",
|
||||
"send_amount_upper": "CANTIDAD",
|
||||
"send_available_note": "disponible",
|
||||
"send_cannot_send_now": "No se puede enviar ahora — revisa la conexión, la sincronización y el saldo disponible.",
|
||||
"send_clear_fields": "¿Limpiar todos los campos del formulario?",
|
||||
"send_contacts_button": "Elegir de contactos",
|
||||
@@ -1450,17 +1860,34 @@
|
||||
"status": "Estado",
|
||||
"stop_external": "Detener daemon externo",
|
||||
"stop_mining": "Detener Minería",
|
||||
"stratum_allowip_hint": "Permitir mineros desde IP o CIDR (en blanco = solo este equipo)",
|
||||
"stratum_expose_warn": "Abre un puerto de minería a la red que permitas — úsalo solo en una LAN de confianza.",
|
||||
"stratum_host": "Alojar un pool de minería (stratum)",
|
||||
"stratum_host_hint": "Los mineros se conectan a este equipo en el puerto 22769 (puerto RPC + 1000) con un minero stratum RandomX. Reinicia el demonio para aplicar el cambio.",
|
||||
"stratum_host_section": "ALOJAMIENTO DE POOL DE MINERÍA",
|
||||
"submitting_transaction": "Enviando transacción...",
|
||||
"success": "Éxito",
|
||||
"summary": "Resumen",
|
||||
"sweep_button": "Barrer",
|
||||
"sweep_caveat": "Importa la clave para firmar una transacción que mueve todos sus fondos a tu dirección. La clave permanece en tu monedero con saldo cero.",
|
||||
"sweep_current_theme_only": "Solo el tema actual",
|
||||
"sweep_dest_label": "Enviar los fondos barridos a",
|
||||
"sweep_dest_new": "Nueva dirección blindada (recomendado)",
|
||||
"sweep_done": "Listo — fondos barridos a tu dirección.",
|
||||
"sweep_to": "Barrido a:",
|
||||
"sweep_toggle": "Barrer a mi monedero (no conservar la clave)",
|
||||
"sweep_tx": "Transacción:",
|
||||
"swin_connection_failed": "Error de conexión: ",
|
||||
"swin_connection_successful": "¡Conexión correcta!\nVersión de dragonxd: ",
|
||||
"swin_invalid_suffix": " (no válido)",
|
||||
"swin_no_history_file_found": "No se encontró ningún archivo de historial",
|
||||
"swin_rescan_failed": "Error en el reescaneo: ",
|
||||
"swin_rescan_started_from_block": "Reescaneo iniciado desde el bloque ",
|
||||
"swin_rescan_to": " hasta ",
|
||||
"swin_rpc_client_not_initialized": "Cliente RPC no inicializado",
|
||||
"swin_settings_saved": "Ajustes guardados",
|
||||
"swin_theme_list_refreshed": "Lista de temas actualizada",
|
||||
"swin_ztx_history_cleared": "Historial de transacciones Z borrado",
|
||||
"switch_corrupt_body": "Esta cartera parece dañada: el nodo no pudo abrirla. Restáurala desde una copia de seguridad, vuelve a crearla o intenta repararla.",
|
||||
"switch_corrupt_repair": "Intentar reparar (salvage)",
|
||||
"switch_progress_background": "Continuar en segundo plano",
|
||||
@@ -1621,6 +2048,8 @@
|
||||
"tt_simple_bg": "Usar un gradiente simple para el fondo\\nAtajo: Ctrl+Up",
|
||||
"tt_simple_bg_alt": "Usar una versión degradada de la imagen de fondo del tema\\nAtajo: Ctrl+Up",
|
||||
"tt_stop_external": "Se aplica al conectarse a un daemon\\niniciado fuera de esta billetera",
|
||||
"tt_stratum_host": "Ejecuta un servidor de pool stratum RandomX en este nodo para que otros mineros RandomX puedan apuntar a este equipo. Requiere un nodo v1.3.0+ y reiniciar el demonio para aplicar el cambio.",
|
||||
"tt_sweep_current_theme_only": "Barrer solo el tema activo en lugar de recorrer todos los temas",
|
||||
"tt_test_conn": "Verificar la conexión RPC al daemon",
|
||||
"tt_theme_effects": "Brillo, resplandor, ciclo de tono por tema",
|
||||
"tt_theme_hotkey": "Atajo: Ctrl+Izquierda/Derecha para cambiar temas",
|
||||
@@ -1629,6 +2058,7 @@
|
||||
"tt_ui_opacity": "Opacidad de tarjetas y barra lateral (100% = totalmente opaco, menor = más transparente)",
|
||||
"tt_validate": "Comprobar si una dirección DragonX es válida",
|
||||
"tt_verbose": "Registrar diagnósticos detallados de conexión,\\nestado del daemon e info de propietario de puerto\\nen la pestaña de Consola",
|
||||
"tt_wallet_size_warn": "Las carteras blindadas crecen con los datos de testigo de cada nota — fusionar muchas notas en una sola dirección lo reduce. Haz una copia de seguridad primero.",
|
||||
"tt_wallets_button": "Enumera tus archivos de cartera y cambia entre ellos",
|
||||
"tt_website": "Abrir el sitio web de DragonX",
|
||||
"tt_window_opacity": "Opacidad del fondo (menor = escritorio visible a través de la ventana)",
|
||||
@@ -1695,8 +2125,68 @@
|
||||
"view_on_explorer": "Ver en Explorador",
|
||||
"waiting_for_daemon": "Esperando conexión al daemon...",
|
||||
"wallet": "CARTERA",
|
||||
"wallet_degraded_notify": "Tu cartera se abrió en modo de funciones reducidas: los fondos existentes están seguros y disponibles, pero crear nuevas direcciones y blindar están desactivados. Respalda tu frase de recuperación y restáurala para reparar la cartera por completo.",
|
||||
"wallet_diagnostics_hdr": "DIAGNÓSTICOS",
|
||||
"wallet_empty": "Tu cartera está vacía",
|
||||
"wallet_empty_hint": "Cambia a Recibir para obtener tu dirección y empezar a recibir fondos.",
|
||||
"wallet_options_hdr": "OPCIONES",
|
||||
"wallet_rebuild_failed": "La reconstrucción no produjo una cartera válida, así que no se cambió nada. Tu cartera no fue modificada.",
|
||||
"wallet_rebuild_install_failed": "No se pudo instalar la cartera reconstruida; tu cartera actual se dejó tal cual.",
|
||||
"wallet_rebuild_no_helper": "El asistente de reconstrucción de cartera no está disponible en esta compilación. Usa Restaurar, o reconstruye manualmente.",
|
||||
"wallet_rebuild_no_source": "No se pudo encontrar una cartera legible para reconstruir. No se cambió nada.",
|
||||
"wallet_rebuild_ok": "Base de datos de la cartera reconstruida — el nodo la está cargando y reescaneando para calcular tu saldo.",
|
||||
"wallet_rebuild_started": "Reparando tu archivo de cartera — por favor no cierres esta ventana.",
|
||||
"wallet_recovered_body": "No se ha eliminado nada. Tu cartera original sigue guardada en tu computadora como un archivo de respaldo con fecha, y cada opción a continuación solo copia o renombra archivos — nunca borra ninguno. Tus claves nunca se regeneran, solo se vuelven a leer.\n\nEs posible que a la copia reparada que está cargada ahora le falten algunas transacciones recientes, así que tu saldo puede verse un poco bajo hasta que termines abajo y se vuelva a escanear.",
|
||||
"wallet_recovered_dismiss": "Ahora no — mantener la copia reparada",
|
||||
"wallet_recovered_dismiss_sub": "No se cambia ningún archivo. Puedes volver a abrir esto cuando quieras desde la barra de estado; tu original queda respaldado de forma segura de todos modos.",
|
||||
"wallet_recovered_notify": "Tu archivo de cartera necesitaba una reparación — tu original se respaldó de forma segura. Abre la app para revisar tus opciones.",
|
||||
"wallet_recovered_open_folder": "Mostrarme los archivos",
|
||||
"wallet_recovered_open_folder_sub": "Abre la carpeta de datos de la cartera para que puedas inspeccionar los archivos de respaldo tú mismo — nada cambia.",
|
||||
"wallet_recovered_rebuild": "Reparar automáticamente (recomendado)",
|
||||
"wallet_recovered_rebuild_sub": "Vuelve a leer cada registro recuperable de tu archivo de cartera más completo y escribe uno nuevo limpio, luego reinicia. La opción más exhaustiva — tu archivo actual se conserva como respaldo con fecha de todos modos.",
|
||||
"wallet_recovered_restore": "Restaurar el archivo original en su lugar",
|
||||
"wallet_recovered_restore_sub": "Vuelve a poner en su lugar tu respaldo más grande sin modificar, tal cual, y luego vuelve a escanear — algo más rápido, pero solo tan completo como lo era ese archivo. Tu archivo actual se conserva como respaldo con fecha de todos modos.",
|
||||
"wallet_recovered_safety": "Tus monedas están a salvo.",
|
||||
"wallet_recovered_title": "Tu archivo de cartera necesita una reparación rápida",
|
||||
"wallet_recovered_warn": "Cuando la app se inició, encontró que tu archivo de cartera no pasó su verificación de consistencia — esto suele ocurrir después de una actualización de la app o un cierre inesperado. La app ya protegió tus datos: apartó el archivo antiguo y cargó una copia reparada para que no te quedes bloqueado.",
|
||||
"wallet_recovery_decide_later": "Decidir más tarde",
|
||||
"wallet_recovery_decide_later_tip": "Cierra esto y mantiene la copia que está cargada ahora. Nada cambia, y puedes reparar cuando quieras — la barra de estado conserva un enlace de “Reparación de cartera disponible”.",
|
||||
"wallet_recovery_details_label": "Mostrar detalles técnicos",
|
||||
"wallet_recovery_done": "Listo",
|
||||
"wallet_recovery_failure_body": "La reparación se ejecutó, pero su resultado no pasó la verificación (no se pudo leer, o no tenía direcciones), así que se descartó automáticamente antes de reemplazar nada. Tu cartera está exactamente como estaba antes de que hicieras clic — nada cambió en el disco.",
|
||||
"wallet_recovery_failure_title": "La reparación no se completó",
|
||||
"wallet_recovery_files_detail": "Tu cartera original sigue aquí — la app la renombró a un respaldo con fecha (wallet.<números>.bak) en tu carpeta de datos y no ha eliminado nada. “Reparar” lee cada registro de tu cartera más completa en un archivo nuevo y limpio. “Restaurar” copia tu respaldo más grande de vuelta exactamente como está. La copia cargada en este momento también se conserva como respaldo.",
|
||||
"wallet_recovery_files_label": "¿Qué pasa con mis archivos?",
|
||||
"wallet_recovery_notnow_short": "Ahora no",
|
||||
"wallet_recovery_other_options": "Otras opciones",
|
||||
"wallet_recovery_rebuild_card": "Reparar automáticamente",
|
||||
"wallet_recovery_rebuild_card_desc": "Lee cada registro recuperable en un archivo limpio, luego reinicia. La opción más exhaustiva.",
|
||||
"wallet_recovery_recommended": "RECOMENDADO",
|
||||
"wallet_recovery_repair_go": "Reparar",
|
||||
"wallet_recovery_rescan_body": "Volviendo a escanear la cadena de bloques para reconstruir tu saldo e historial de transacciones. Esto puede tardar varios minutos — no necesitas hacer nada, y por favor no reinicies el nodo mientras se ejecuta.",
|
||||
"wallet_recovery_rescan_elapsed": "Trabajando desde hace %s",
|
||||
"wallet_recovery_rescan_size": "La cartera reconstruida ahora pesa %s y sigue completándose",
|
||||
"wallet_recovery_rescan_slow": "Esto está tardando más de lo habitual, lo cual es normal en una cartera grande. Es seguro dejarlo en ejecución — puedes ver el progreso en Avanzado ▸ Consola.",
|
||||
"wallet_recovery_rescan_title": "Terminando la reparación de tu cartera",
|
||||
"wallet_recovery_restore_card": "Restaurar original",
|
||||
"wallet_recovery_restore_card_desc": "Vuelve a poner en su lugar tu respaldo más grande sin modificar, tal cual — solo tan completo como era ese archivo.",
|
||||
"wallet_recovery_restore_go": "Restaurar",
|
||||
"wallet_recovery_success_body": "La app cargó tu cartera reparada y está volviendo a escanear para totalizar tu saldo — esto puede tardar unos minutos. Lee cada registro que puede, pero en archivos dañados poco frecuentes puede recuperar algunas direcciones menos — u ocasionalmente más — que antes. Cuando termine el reescaneo, comprueba que tu saldo e historial se vean correctos.",
|
||||
"wallet_recovery_success_restore": "Tu archivo de respaldo más grande ya está en su lugar y cargando ahora, y la app está volviendo a escanear. Se restauró tal cual, así que es exactamente tan completo como era ese archivo — comprueba tu saldo cuando termine el reescaneo.",
|
||||
"wallet_recovery_try_other": "Probar la otra opción",
|
||||
"wallet_recovery_whats_happened": "¿Qué pasó?",
|
||||
"wallet_recovery_working_label": "Trabajando",
|
||||
"wallet_restore_bad_backup": "El archivo de cartera de respaldo parece ilegible, así que NO se restauró — tu cartera actual no cambió. Restaura desde tu propio respaldo en su lugar.",
|
||||
"wallet_restore_busy": "El nodo está ocupado reiniciándose — inténtalo de nuevo en un momento.",
|
||||
"wallet_restore_copy_failed": "No se pudo instalar la cartera de respaldo; tu cartera actual se dejó en su lugar.",
|
||||
"wallet_restore_move_failed": "No se pudo apartar la cartera actual — no se cambió nada.",
|
||||
"wallet_restore_no_backup": "No se encontró un wallet.<timestamp>.bak para restaurar. No se cambió nada.",
|
||||
"wallet_restore_no_restart": "Tu cartera original se restauró, pero el nodo no se reinició — inícialo desde Ajustes.",
|
||||
"wallet_restore_ok": "Cartera original restaurada. El nodo la está cargando ahora.",
|
||||
"wallet_restore_started": "Restaurando tu archivo original — por favor no cierres esta ventana.",
|
||||
"wallet_restore_stop_failed": "El nodo no se detuvo a tiempo, así que no se cambió nada. Inténtalo de nuevo.",
|
||||
"wallet_size_consolidate": "Consolidar notas…",
|
||||
"wallet_size_warn": "Este archivo de cartera es grande. Consolidar tus notas puede frenar un mayor crecimiento.",
|
||||
"wallets_active": "Activa",
|
||||
"wallets_add": "Agregar carpeta",
|
||||
"wallets_add_folder": "Escanear también otra carpeta en busca de archivos de cartera:",
|
||||
@@ -1739,6 +2229,7 @@
|
||||
"wallets_open_inplace_tt": "Abre esta cartera donde está, enlazándola al directorio de datos (sin copiar)",
|
||||
"wallets_remove_folder": "Dejar de escanear esta carpeta",
|
||||
"wallets_reveal": "Mostrar carpeta",
|
||||
"wallets_scan_folder": "Escanear otra carpeta en busca de carteras…",
|
||||
"wallets_scanned_folders": "Carpetas escaneadas:",
|
||||
"wallets_sort_addresses": "Direcciones",
|
||||
"wallets_sort_asc": "Ascendente (más antiguo / menos / más pequeño primero)",
|
||||
@@ -1808,19 +2299,30 @@
|
||||
"wiz_welcome_sub": "Unas cuantas opciones rápidas y tu nodo completo estará listo.",
|
||||
"wiz_welcome_title": "¡Bienvenido a ObsidianDragon!",
|
||||
"wizard_daemon_start_failed": "Error al iniciar el daemon — se reintentará automáticamente",
|
||||
"xmrig_active_tt": "El minero actualmente instalado (activo)",
|
||||
"xmrig_browse_releases": "Ver todos los lanzamientos…",
|
||||
"xmrig_checking": "Buscando el minero más reciente…",
|
||||
"xmrig_current": "Actual:",
|
||||
"xmrig_downgrade_note": "Instalar una versión de minero diferente reemplaza la actual; detén la minería primero, y se aplicará en el siguiente inicio.",
|
||||
"xmrig_download_install": "Descargar e instalar",
|
||||
"xmrig_downloading": "Descargando…",
|
||||
"xmrig_install_this": "Instalar esta versión",
|
||||
"xmrig_installed": "Instalado:",
|
||||
"xmrig_installed_ok": "Minero instalado",
|
||||
"xmrig_installing": "Instalando…",
|
||||
"xmrig_latest": "Más reciente:",
|
||||
"xmrig_latest_badge": "la más reciente",
|
||||
"xmrig_latest_tt": "Versión más reciente disponible",
|
||||
"xmrig_loading_releases": "Cargando versiones…",
|
||||
"xmrig_no_notes": "No hay notas de la versión para esta versión.",
|
||||
"xmrig_none": "ninguno",
|
||||
"xmrig_prerelease_tt": "Compilación preliminar / de pruebas",
|
||||
"xmrig_reinstall": "Reinstalar",
|
||||
"xmrig_released": "Publicado:",
|
||||
"xmrig_releases": "versiones de xmrig",
|
||||
"xmrig_status_current": "Esta versión está instalada",
|
||||
"xmrig_status_latest": "Última versión",
|
||||
"xmrig_status_older": "Anterior a tu versión instalada",
|
||||
"xmrig_stop_mining_first": "Detén la minería antes de actualizar el minero.",
|
||||
"xmrig_unavailable_body": "No hay ninguna versión del minero disponible para esta plataforma.",
|
||||
"xmrig_unavailable_title": "Actualizaciones del minero no disponibles",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"about_license_text": "Ce logiciel est publié sous la licence publique générale GNU v3 (GPLv3). Vous êtes libre d'utiliser, de modifier et de distribuer ce logiciel selon les termes de la licence.",
|
||||
"about_peers_count": "%zu pairs",
|
||||
"about_release": "Version",
|
||||
"about_source": "Source",
|
||||
"about_title": "À propos d'ObsidianDragon",
|
||||
"about_version": "Version :",
|
||||
"about_website": "Site web",
|
||||
@@ -60,7 +61,84 @@
|
||||
"amount_label": "Montant :",
|
||||
"animate_avatars": "Animer les avatars",
|
||||
"appearance": "APPARENCE",
|
||||
"appx_back": "Retour",
|
||||
"appx_back_up_seed_phrase_title": "Sauvegardez votre phrase de récupération",
|
||||
"appx_birthday_block_height": "Naissance (hauteur de bloc) : %llu — sauvegardez-la également.",
|
||||
"appx_blockchain_data_deleted": "Données de la blockchain supprimées (%d éléments). Le démon redémarre pour se resynchroniser depuis le réseau.",
|
||||
"appx_blockchain_maintenance_in_progress": "Une opération de maintenance de la blockchain est déjà en cours.",
|
||||
"appx_blockchain_rescan_complete": "Nouvelle analyse de la blockchain terminée",
|
||||
"appx_bootstrap_complete_reconciling": "Amorçage terminé — réconciliation de votre portefeuille avec les nouvelles données de la chaîne.",
|
||||
"appx_cancel": "Annuler",
|
||||
"appx_cleaning_up": "Nettoyage...",
|
||||
"appx_confirm_your_backup": "Confirmez votre sauvegarde",
|
||||
"appx_copied_clipboard_autoclears": "Copié — le presse-papiers s'efface automatiquement dans 45 s",
|
||||
"appx_copy": "Copier",
|
||||
"appx_could_not_start_restore": "Impossible de démarrer la restauration",
|
||||
"appx_create_failed_prefix": "Échec de la création : ",
|
||||
"appx_creating_your_wallet": "Création de votre portefeuille…",
|
||||
"appx_daemon_error": "Erreur du démon",
|
||||
"appx_daemon_reinstall_in_progress": "La réinstallation du démon est déjà en cours.",
|
||||
"appx_disconnecting": "Déconnexion...",
|
||||
"appx_done": "Terminé",
|
||||
"appx_dragonxd_output": "Sortie de dragonxd",
|
||||
"appx_encrypting_wallet": "Chiffrement du portefeuille...",
|
||||
"appx_fullnode_lifecycle_unavailable_lite": "Les actions de cycle de vie du nœud complet ne sont pas disponibles dans la version allégée",
|
||||
"appx_installing_bundled_daemon": "Installation du démon intégré — le nœud va s'arrêter, se mettre à jour, puis redémarrer...",
|
||||
"appx_invalid_payment_uri_prefix": "URI de paiement invalide : ",
|
||||
"appx_ive_written_it_down": "Je les ai notés",
|
||||
"appx_keep_node_running_and_quit": "Laisser le nœud actif et quitter",
|
||||
"appx_last_block_n": "Dernier bloc : %d",
|
||||
"appx_last_used_wallet_not_found_prefix": "Votre dernier fichier de portefeuille utilisé (",
|
||||
"appx_last_used_wallet_not_found_suffix": ") est introuvable — le portefeuille par défaut a été ouvert à la place. Si vous l'avez déplacé, restaurez-le et revenez à celui-ci depuis la liste des portefeuilles.",
|
||||
"appx_low_spec_mode_disabled": "Mode faibles ressources désactivé",
|
||||
"appx_low_spec_mode_enabled": "Mode faibles ressources activé",
|
||||
"appx_miner_stopped_prefix": "Mineur arrêté : ",
|
||||
"appx_miner_stopped_unexpectedly": "Le mineur s'est arrêté de manière inattendue.",
|
||||
"appx_n_min_n_sec": "%d min %d s",
|
||||
"appx_n_seconds": "%d secondes",
|
||||
"appx_no_bundled_daemon_to_install": "Cette version ne contient aucun démon intégré à installer",
|
||||
"appx_no_embedded_daemon_to_install": "Cette version ne contient aucun démon embarqué à installer",
|
||||
"appx_node_busy_restarting": "Le nœud est occupé à redémarrer — réessayez dans un instant.",
|
||||
"appx_node_rebuilding_witness_cache": "Le nœud reconstruit son cache de témoins",
|
||||
"appx_not_next_word": " — ce n'est pas le mot suivant",
|
||||
"appx_payment_request_loaded": "Demande de paiement chargée",
|
||||
"appx_pool_miner_connected_and_hashing": "Mineur de pool connecté et en cours de hachage.",
|
||||
"appx_progress_n_of_n": "Progression : %d / %d",
|
||||
"appx_rebuilding_sapling_note_witnesses": "Reconstruction des témoins de notes Sapling…",
|
||||
"appx_rebuilding_witness_cache_blocks_left": "Reconstruction du cache de témoins %.0f%% — %d blocs restants",
|
||||
"appx_rebuilding_witness_cache_pct": "Reconstruction du cache de témoins %.0f%%",
|
||||
"appx_recovery_phrase_word_count": "La phrase de récupération doit comporter 24 mots — vous en avez %d.",
|
||||
"appx_restarting_daemon_rescan_flag": "Redémarrage du démon avec l'option -rescan...",
|
||||
"appx_restarting_daemon_zapwallettxes": "Redémarrage du démon avec -zapwallettxes=2 (réparation du portefeuille)...",
|
||||
"appx_restoring_your_wallet": "Restauration de votre portefeuille…",
|
||||
"appx_seed_backup_warning": "Ces 24 mots sont le SEUL moyen de restaurer votre portefeuille. Notez-les dans l'ordre, conservez-les hors ligne et ne les partagez jamais. Si vous les perdez, vos fonds seront perdus à jamais.",
|
||||
"appx_seed_not_backed_up_warning": "Vous n'avez pas sauvegardé votre phrase de récupération — des fonds pourraient être perdus. Ignorer quand même ?",
|
||||
"appx_sending_stop_command_to_daemon": "Envoi de la commande d'arrêt au démon...",
|
||||
"appx_setting_initial_sapling_witnesses": "Définition des témoins Sapling initiaux %.0f%%",
|
||||
"appx_shutdown_complete": "Arrêt terminé",
|
||||
"appx_simple_background_disabled": "Arrière-plan simple désactivé",
|
||||
"appx_simple_background_enabled": "Arrière-plan simple activé",
|
||||
"appx_skip": "Ignorer",
|
||||
"appx_skip_anyway": "Ignorer quand même",
|
||||
"appx_still_status_prefix": "Toujours « ",
|
||||
"appx_still_status_suffix": " » — forcer la fermeture maintenant peut corrompre les données de la chaîne.",
|
||||
"appx_stop_anyway_and_quit": "Arrêter quand même et quitter",
|
||||
"appx_stopping_daemon_deleting_blockchain": "Arrêt du démon et suppression des données de la blockchain...",
|
||||
"appx_stopping_node_discards_rebuild": "Arrêter le nœud maintenant abandonne la reconstruction en cours et la relance (plusieurs minutes) à la prochaine ouverture du portefeuille. Vous pouvez plutôt laisser le nœud en marche.",
|
||||
"appx_stopping_pool_miner": "Arrêt du mineur de pool...",
|
||||
"appx_syncing_pct_block_n_of_n": "Synchronisation %.1f%% — Bloc %d / %d",
|
||||
"appx_tap_words_in_order": "Touchez les mots dans le bon ordre pour confirmer que vous les avez enregistrés.",
|
||||
"appx_theme_effects_disabled": "Effets de thème désactivés",
|
||||
"appx_theme_effects_enabled": "Effets de thème activés",
|
||||
"appx_theme_prefix": "Thème : ",
|
||||
"appx_use_settings_restart_daemon_hint": "Utilisez Paramètres > Redémarrer le démon pour réessayer",
|
||||
"appx_waiting_for_daemon_to_encrypt_wallet": "En attente du chiffrement du portefeuille par le démon...",
|
||||
"appx_wallet_created_and_backed_up": "Portefeuille créé et sauvegardé.",
|
||||
"appx_wallet_open_failed_prefix": "Échec de l'ouverture du portefeuille : ",
|
||||
"auto_shield": "Auto-blindage du minage",
|
||||
"autoshield_by_node": "Votre nœud blinde automatiquement les coinbase minées pour vous.",
|
||||
"autoshield_off_backup_seed": "Les coinbase minées ne sont pas encore blindées automatiquement. Sauvegardez votre phrase de récupération (Paramètres → Sauvegarde et données) pour que votre nœud puisse l'activer en toute sécurité.",
|
||||
"autoshield_off_generic": "Votre nœud ne blinde pas automatiquement les coinbase minées en ce moment.",
|
||||
"av_intro": "Les logiciels de minage sont souvent signalés comme potentiellement indésirables. Suivez ces étapes pour activer le minage en pool :",
|
||||
"av_open_security": "Ouvrir Sécurité Windows",
|
||||
"av_step1": "Étape 1 : Ajouter une exclusion",
|
||||
@@ -100,6 +178,17 @@
|
||||
"balance_shielded_fmt": "Blindé : %.8f",
|
||||
"balance_syncing_pct": "Synchronisation %.1f%%",
|
||||
"balance_transparent_fmt": "Transparent : %.8f",
|
||||
"baltab_market": "Marché",
|
||||
"baltab_market_price_4dp": "Marché : %.4f $",
|
||||
"baltab_market_price_8dp": "Marché : %.8f $",
|
||||
"baltab_pct_change_24h": "%s%.1f%% 24h",
|
||||
"baltab_pct_of_total_zaddr": "%.0f%% du total · %d Z-addr",
|
||||
"baltab_shielded": "Protégé",
|
||||
"baltab_shielded_amount": "Protégé %.8f",
|
||||
"baltab_t_addresses_count": "%d T-addresses",
|
||||
"baltab_total_balance": "Solde total",
|
||||
"baltab_transparent": "Transparent",
|
||||
"baltab_transparent_amount": "Transparent %.8f",
|
||||
"ban": "Bannir",
|
||||
"banned_peers": "Pairs bannis",
|
||||
"block": "Bloc",
|
||||
@@ -107,6 +196,12 @@
|
||||
"block_click_copy": "Cliquez pour copier",
|
||||
"block_click_next": "Cliquez pour voir le bloc suivant",
|
||||
"block_click_prev": "Cliquez pour voir le bloc précédent",
|
||||
"block_db_reindex_body": "Cela arrive généralement après qu'une mise à jour du démon change le format sur disque, ou si l'index des blocs est endommagé. Votre portefeuille et vos pièces sont en sécurité — le nœud ne peut simplement pas charger la chaîne, donc les soldes affichent zéro.\n\nLa reconstruction relit vos fichiers de blocs existants et peut prendre du temps (elle rescanne aussi votre portefeuille). Rien n'est téléchargé.",
|
||||
"block_db_reindex_confirm": "Reconstruire la base de données de blocs",
|
||||
"block_db_reindex_notify": "Le nœud ne peut pas lire sa base de données de blocs (souvent après une mise à jour du démon). Reconstruisez-la pour restaurer votre solde — voir l'invite, ou Paramètres › Nœud.",
|
||||
"block_db_reindex_started": "Reconstruction de la base de données de blocs à partir de vos blocs — cela peut prendre du temps.",
|
||||
"block_db_reindex_title": "Reconstruire la base de données de blocs ?",
|
||||
"block_db_reindex_warn": "Le nœud ne peut pas lire sa base de données de blocs.",
|
||||
"block_explorer": "Explorateur de blocs",
|
||||
"block_get_info": "Obtenir les infos du bloc",
|
||||
"block_hash": "Hash du bloc :",
|
||||
@@ -154,6 +249,10 @@
|
||||
"chat_accent_theme": "Thème",
|
||||
"chat_add_contact": "Ajouter un contact",
|
||||
"chat_awaiting_key": "En attente de réponse",
|
||||
"chat_blocked_desc": "Les conversations bloquées sont supprimées et leurs messages sont rejetés — anciens et nouveaux — jusqu'à ce que vous débloquiez. Débloquer réimporte la conversation depuis la chaîne.",
|
||||
"chat_blocked_manage": "Bloqué",
|
||||
"chat_blocked_title": "Conversations bloquées",
|
||||
"chat_blocked_toast": "Conversation supprimée et bloquée",
|
||||
"chat_bubble_minimal": "Minimale",
|
||||
"chat_bubble_rounded": "Arrondie",
|
||||
"chat_bubble_square": "Carrée",
|
||||
@@ -166,6 +265,16 @@
|
||||
"chat_contact_added": "Contact ajouté — renommez-le dans Contacts",
|
||||
"chat_contact_request": "demande de contact",
|
||||
"chat_copy_address_tip": "Cliquer pour copier l'adresse",
|
||||
"chat_delete": "Supprimer la conversation",
|
||||
"chat_delete_block": "Supprimer et bloquer",
|
||||
"chat_delete_body_prefix": "Supprimer votre copie locale de la conversation avec ",
|
||||
"chat_delete_body_suffix": " ?",
|
||||
"chat_delete_confirm": "Supprimer",
|
||||
"chat_delete_failed": "Impossible de supprimer la conversation — rien n'a été modifié.",
|
||||
"chat_delete_local_note": "Cela n'affecte que cet appareil — les messages restent sur la blockchain et l'autre personne conserve sa copie.",
|
||||
"chat_delete_revive_note": "« Supprimer » efface l'historique sur cet appareil. Si cette personne vous écrit à nouveau, la conversation revient.",
|
||||
"chat_delete_title": "Supprimer la conversation ?",
|
||||
"chat_deleted_toast": "Conversation supprimée",
|
||||
"chat_density_comfortable": "Confortable",
|
||||
"chat_density_compact": "Compacte",
|
||||
"chat_emoji_color": "Couleur",
|
||||
@@ -189,6 +298,7 @@
|
||||
"chat_new_button": "Nouvelle discussion",
|
||||
"chat_new_message": "Message",
|
||||
"chat_new_message_toast": "Nouveau message chiffré",
|
||||
"chat_new_needs_zaddr": "Le chat nécessite une adresse blindée (z) — les adresses transparentes (t) ne peuvent pas recevoir de messages chiffrés.",
|
||||
"chat_new_send": "Envoyer la demande",
|
||||
"chat_new_title": "Nouvelle discussion",
|
||||
"chat_new_zaddr": "Adresse Z du destinataire",
|
||||
@@ -234,6 +344,8 @@
|
||||
"chat_ts_24h": "24 heures",
|
||||
"chat_ts_global": "Suivre global",
|
||||
"chat_ts_global_short": "Global",
|
||||
"chat_unblock": "Débloquer",
|
||||
"chat_unblocked_toast": "Conversation débloquée",
|
||||
"chat_unhide": "Afficher",
|
||||
"chat_unmute": "Réactiver",
|
||||
"chat_verify_key": "Clé d'identité — comparez pour vérifier",
|
||||
@@ -286,6 +398,7 @@
|
||||
"console": "Console",
|
||||
"console_accents": "Accents de couleur",
|
||||
"console_app": "App",
|
||||
"console_auto_focus": "Placer le focus sur le champ de saisie à l'ouverture",
|
||||
"console_auto_scroll": "Défilement auto",
|
||||
"console_available_commands": "Commandes disponibles :",
|
||||
"console_backend_reference": "Référence des commandes du backend",
|
||||
@@ -380,11 +493,15 @@
|
||||
"console_tab_completion": "Tab pour compléter",
|
||||
"console_text_colors": "Couleurs du texte",
|
||||
"console_toggle_accents": "Basculer les accents de couleur des lignes",
|
||||
"console_toggle_auto_focus": "Placer le curseur dans la zone de commande à l'ouverture de l'onglet Console",
|
||||
"console_toggle_text_color": "Basculer les couleurs du texte des lignes",
|
||||
"console_type_help": "Tapez 'help' pour les commandes disponibles",
|
||||
"console_welcome": "Bienvenue dans la console ObsidianDragon",
|
||||
"console_zoom_in": "Agrandir",
|
||||
"console_zoom_out": "Réduire",
|
||||
"consolidate_desc": "Combinez de nombreuses petites entrées en une seule note blindée. Moins de notes signifie un fichier de portefeuille plus petit et une meilleure confidentialité.",
|
||||
"consolidate_funds_btn": "Consolider",
|
||||
"consolidate_title": "Consolider les fonds",
|
||||
"contact_avatar": "AVATAR",
|
||||
"contact_avatar_bad_image": "Cette image n'a pas pu être chargée.",
|
||||
"contact_avatar_badge": "Badge",
|
||||
@@ -414,6 +531,7 @@
|
||||
"contacts_shape_square": "Carré",
|
||||
"contacts_shape_tab": "Onglet",
|
||||
"copied": "Copié !",
|
||||
"copied_to_clipboard": "Copié dans le presse-papiers",
|
||||
"copy": "Copier",
|
||||
"copy_address": "Copier l'adresse complète",
|
||||
"copy_error": "Copier l'erreur",
|
||||
@@ -429,9 +547,13 @@
|
||||
"daemon_maintenance_label": "MAINTENANCE",
|
||||
"daemon_none_bundled": "aucun dans cette version",
|
||||
"daemon_not_installed": "non installé",
|
||||
"daemon_status_diff": "Version différente",
|
||||
"daemon_status_differ": "Le binaire installé diffère de la version intégrée.",
|
||||
"daemon_status_match": "Le binaire installé correspond à la version intégrée.",
|
||||
"daemon_status_missing": "Aucun daemon installé — installez la version intégrée.",
|
||||
"daemon_status_none": "Non installé",
|
||||
"daemon_status_ok": "À jour",
|
||||
"daemon_update_active_tt": "Le nœud actuellement installé (actif)",
|
||||
"daemon_update_available": "Une nouvelle version du nœud est disponible",
|
||||
"daemon_update_body": "Cette version du portefeuille inclut un nœud DragonX plus récent que celui actuellement installé. La mise à jour remplace le dragonxd installé (ainsi que dragonx-cli/dragonx-tx), puis arrête et redémarre le nœud pour que la nouvelle version prenne effet. Recommandé — un nœud plus récent peut ajouter des fonctionnalités (par exemple la prise en charge des phrases de récupération) que l'ancien n'a pas.",
|
||||
"daemon_update_browse": "Parcourir toutes les versions…",
|
||||
@@ -441,19 +563,28 @@
|
||||
"daemon_update_download_install": "Télécharger et installer",
|
||||
"daemon_update_downloading": "Téléchargement…",
|
||||
"daemon_update_failed": "Échec de la mise à jour",
|
||||
"daemon_update_install_this": "Installer cette version",
|
||||
"daemon_update_installed": "Installé :",
|
||||
"daemon_update_installed_ok": "Nœud installé",
|
||||
"daemon_update_installing": "Installation…",
|
||||
"daemon_update_keep": "Conserver l'actuel",
|
||||
"daemon_update_later": "Plus tard",
|
||||
"daemon_update_latest": "Dernière :",
|
||||
"daemon_update_latest_badge": "dernière",
|
||||
"daemon_update_latest_tt": "Dernière version disponible",
|
||||
"daemon_update_loading": "Chargement des versions…",
|
||||
"daemon_update_no_notes": "Aucune note de version pour cette version.",
|
||||
"daemon_update_now": "Mettre à jour",
|
||||
"daemon_update_prerelease_tt": "Version préliminaire / de test",
|
||||
"daemon_update_prompt_title": "Mettre à jour le démon du nœud ?",
|
||||
"daemon_update_reinstall": "Réinstaller",
|
||||
"daemon_update_released": "Publiée :",
|
||||
"daemon_update_restart_note": "Redémarrez le daemon pour lancer la nouvelle version.",
|
||||
"daemon_update_restart_now": "Redémarrer le daemon maintenant",
|
||||
"daemon_update_safe": "Votre portefeuille, vos clés et vos données de blockchain ne sont pas touchés — seuls les fichiers du programme daemon sont remplacés. Si vous exécutez délibérément un nœud personnalisé, choisissez Conserver l'actuel.",
|
||||
"daemon_update_status_current": "Cette version est installée",
|
||||
"daemon_update_status_latest": "Dernière version",
|
||||
"daemon_update_status_older": "Antérieure à votre version installée",
|
||||
"daemon_update_title": "Mettre à jour le daemon du nœud ?",
|
||||
"daemon_update_unavailable_body": "Aucune version du nœud n'est disponible pour cette plateforme.",
|
||||
"daemon_update_unavailable_title": "Mises à jour du nœud indisponibles",
|
||||
@@ -469,6 +600,13 @@
|
||||
"data_stale_tooltip": "Le solde est peut-être obsolète — le portefeuille n'a pas reçu de mise à jour récente. Vérifiez la connexion à votre nœud.",
|
||||
"date": "Date",
|
||||
"date_label": "Date :",
|
||||
"debug_gate_confirm": "Afficher les options de débogage",
|
||||
"debug_gate_incorrect": "PIN ou phrase secrète incorrect.",
|
||||
"debug_gate_pass_prompt": "Entrez la phrase secrète de votre portefeuille pour continuer :",
|
||||
"debug_gate_pin_prompt": "Entrez votre PIN de déverrouillage pour continuer :",
|
||||
"debug_gate_title": "Afficher les options de débogage ?",
|
||||
"debug_gate_verifying": "Vérification…",
|
||||
"debug_gate_warning": "Ce sont des options avancées de diagnostic et de réglage du nœud. Elles peuvent exposer des informations sensibles et modifier le fonctionnement de votre nœud. Ne continuez que si vous savez ce que vous faites.",
|
||||
"debug_logging": "JOURNALISATION DE DÉBOGAGE",
|
||||
"decrypt_desc": "Le portefeuille sera exporté, le daemon redémarré avec un nouveau portefeuille non chiffré, et toutes les clés réimportées. Cela peut prendre plusieurs minutes selon la taille du portefeuille.",
|
||||
"decrypt_error_title": "Échec du déchiffrement",
|
||||
@@ -491,6 +629,7 @@
|
||||
"disconnected": "Déconnecté",
|
||||
"dismiss": "Ignorer",
|
||||
"display": "Affichage",
|
||||
"done": "Terminé",
|
||||
"download": "Télécharger",
|
||||
"download_bootstrap": "Télécharger Bootstrap",
|
||||
"dragonx_green": "DragonX (Vert)",
|
||||
@@ -544,6 +683,7 @@
|
||||
"explorer_tx_outputs": "Sorties",
|
||||
"explorer_tx_size": "Taille",
|
||||
"explorer_txid": "TxID :",
|
||||
"explorer_urls_hdr": "URLS",
|
||||
"export": "Exporter",
|
||||
"export_csv": "Exporter en CSV",
|
||||
"export_keys_btn": "Exporter les clés",
|
||||
@@ -571,6 +711,151 @@
|
||||
"export_viewing_key": "Exporter la clé de visualisation",
|
||||
"failed_create_shielded": "Échec de la création de l'adresse blindée",
|
||||
"failed_create_transparent": "Échec de la création de l'adresse transparente",
|
||||
"faq": "FAQ",
|
||||
"faq_d_mgmt_1_a": "Le nœud intégré démarre automatiquement avec le portefeuille et s'arrête quand il le faut, donc vous n'avez normalement pas besoin de le gérer manuellement. Les contrôles avancés (y compris le redémarrage du démon) se trouvent dans Paramètres > Nœud et sécurité.",
|
||||
"faq_d_mgmt_1_q": "Comment démarrer ou arrêter le nœud ?",
|
||||
"faq_d_mgmt_2_a": "Si vous connectez le portefeuille à un nœud que vous avez démarré vous-même, cette option détermine si la fermeture du portefeuille arrête aussi ce nœud. Laissez-la désactivée si vous voulez que votre nœud continue de fonctionner après la fermeture du portefeuille.",
|
||||
"faq_d_mgmt_2_q": "À quoi sert « fermer le démon externe à la sortie » ?",
|
||||
"faq_d_mgmt_3_a": "Les données du nœud et du portefeuille se trouvent dans le répertoire de données DragonX sous votre profil utilisateur (par exemple, dans AppData sous Windows ou votre dossier personnel sous Linux/macOS). La blockchain en est la partie volumineuse ; gardez assez d'espace disque libre pour sa croissance.",
|
||||
"faq_d_mgmt_3_q": "Où sont stockées la blockchain et les données du portefeuille ?",
|
||||
"faq_d_mgmt_title": "Gestion du nœud",
|
||||
"faq_d_mine_1_a": "L'onglet Minage vous permet de miner avec votre processeur (CPU). DragonX utilise l'algorithme RandomX, conçu pour les processeurs classiques. Définissez le nombre de threads et démarrez — les récompenses s'accumulent dans votre portefeuille.",
|
||||
"faq_d_mine_1_q": "Comment miner du DragonX ?",
|
||||
"faq_d_mine_2_a": "Le minage solo vous envoie les récompenses directement, mais ne paie que lorsque vous trouvez un bloc, ce qui est peu fréquent sauf si vous avez beaucoup de puissance de calcul. Le minage en pool partage le travail avec d'autres pour des gains plus petits mais plus réguliers. L'onglet Minage prend en charge les deux.",
|
||||
"faq_d_mine_2_q": "Minage solo ou en pool — lequel choisir ?",
|
||||
"faq_d_mine_3_a": "La section pool de l'onglet Minage propose un bouton « Mettre à jour le mineur… » qui télécharge, vérifie et installe la dernière version optimisée du mineur DragonX. Comme pour la mise à jour du nœud, chaque téléchargement est vérifié par somme de contrôle et signature avant l'installation.",
|
||||
"faq_d_mine_3_q": "Comment mettre à jour le mineur ?",
|
||||
"faq_d_mine_4_a": "Le nœud actuel peut exécuter un serveur stratum intégré afin que d'autres mineurs puissent se connecter à votre machine. Une option se trouve dans Paramètres > Nœud et sécurité. Par défaut, il n'écoute qu'en local ; l'exposer à d'autres ordinateurs nécessite de l'ouvrir volontairement.",
|
||||
"faq_d_mine_4_q": "Puis-je héberger une pool de minage ?",
|
||||
"faq_d_mine_title": "Minage et pools",
|
||||
"faq_d_net_1_a": "Juste après le lancement, le nœud a besoin d'un instant pour trouver des pairs, donc afficher brièvement « 0 pair » est normal. Si cela persiste, vérifiez votre connexion internet et qu'un pare-feu ne bloque pas le nœud. Le nœud trouve automatiquement des pairs via des graines intégrées.",
|
||||
"faq_d_net_1_q": "Le portefeuille n'affiche aucun pair ni connexion — que se passe-t-il ?",
|
||||
"faq_d_net_2_a": "Oui — les nœuds DragonX communiquent entre eux via TLS, donc les connexions entre pairs sont chiffrées. Le portefeuille communique aussi avec son propre nœud via un canal local sécurisé.",
|
||||
"faq_d_net_2_q": "Ma connexion au réseau est-elle chiffrée ?",
|
||||
"faq_d_net_title": "Pairs et réseau",
|
||||
"faq_d_node_1_a": "Le démon, dragonxd, est le nœud complet de DragonX. Il télécharge et vérifie l'intégralité de la blockchain, relaie les transactions vers le réseau et répond aux requêtes du portefeuille. Exécuter votre propre nœud signifie que vous n'avez à faire confiance au serveur de personne d'autre.",
|
||||
"faq_d_node_1_q": "Qu'est-ce que le démon (dragonxd) ?",
|
||||
"faq_d_node_2_a": "Par défaut, le portefeuille lance et gère son propre nœud intégré (embarqué) — vous n'avez rien à faire. Si vous exécutez déjà dragonxd vous-même, le portefeuille peut se connecter à ce nœud externe à la place. Le portefeuille détecte la situation et se comporte en conséquence.",
|
||||
"faq_d_node_2_q": "Nœud intégré ou externe — quelle est la différence ?",
|
||||
"faq_d_node_3_a": "Un nœud complet valide indépendamment chaque règle de la blockchain, de sorte que votre portefeuille fait confiance aux mathématiques plutôt qu'à un tiers. Cela renforce aussi le réseau. La contrepartie est l'espace disque et une synchronisation initiale.",
|
||||
"faq_d_node_3_q": "Pourquoi exécuter un nœud complet ?",
|
||||
"faq_d_node_title": "Le nœud complet",
|
||||
"faq_d_perf_1_a": "La blockchain est l'élément le plus volumineux et croît avec le temps, donc gardez plusieurs gigaoctets libres. Le nœud utilise un cache de base de données pour la rapidité ; les valeurs par défaut intégrées sont adaptées aux machines courantes. Les utilisateurs avancés peuvent ajuster le cache de base de données du nœud en modifiant dbcache dans son fichier de configuration (DRAGONX.conf).",
|
||||
"faq_d_perf_1_q": "Combien d'espace disque le nœud utilise-t-il, et puis-je régler la mémoire ?",
|
||||
"faq_d_perf_2_a": "Activez le téléchargement du bootstrap (proposé dans l'assistant de configuration) pour récupérer une copie récente et vérifiée de la chaîne au lieu de valider chaque bloc depuis le début. Un disque rapide (SSD) aide aussi beaucoup.",
|
||||
"faq_d_perf_2_q": "Comment accélérer la synchronisation initiale ?",
|
||||
"faq_d_perf_title": "Stockage et performances",
|
||||
"faq_d_sync_1_a": "Au lancement, le nœud charge et valide les blocs avant de pouvoir servir le portefeuille. L'écran « Traitement des blocs / Application des blocs pour construire l'état actuel de la chaîne » correspond à cette phase de préparation. C'est normal ; le portefeuille devient utilisable une fois que le nœud a terminé et signalé sa hauteur.",
|
||||
"faq_d_sync_1_q": "Que signifie « Traitement des blocs » au démarrage ?",
|
||||
"faq_d_sync_2_a": "Pour vos fonds blindés, le nœud conserve des « témoins » cryptographiques qui vous permettent de dépenser en privé. Après certains redémarrages ou mises à jour, il reconstruit ce cache en réanalysant les blocs récents. Sur un portefeuille avec de nombreuses transactions, cela peut prendre plusieurs minutes et utiliser de la mémoire supplémentaire.\n\nCe n'est pas bloqué — laissez-le terminer. Forcer l'arrêt en plein milieu ne fait que redémarrer la reconstruction la prochaine fois.",
|
||||
"faq_d_sync_2_q": "Qu'est-ce que « Reconstruction du cache de témoins » ?",
|
||||
"faq_d_sync_3_a": "Près de la pointe de la chaîne, le travail qui analyse votre portefeuille (vérification des soldes et des notes) est en concurrence avec le nœud pour le même verrou interne, ce qui peut ralentir la connexion des derniers blocs sur un gros portefeuille. Les versions récentes limitent cette analyse pour laisser le nœud rattraper son retard. Rester sur un onglet plus léger (ou simplement attendre) permet de terminer plus vite.",
|
||||
"faq_d_sync_3_q": "Pourquoi la synchronisation est-elle lente alors que je suis presque à jour ?",
|
||||
"faq_d_sync_4_a": "La première synchronisation télécharge et vérifie toute la chaîne et peut prendre du temps selon votre connexion et votre disque. Vous pouvez l'accélérer considérablement en activant le téléchargement du bootstrap dans l'assistant de configuration, qui récupère une copie récente et vérifiée des données de la chaîne.",
|
||||
"faq_d_sync_4_q": "Combien de temps dure la synchronisation initiale ?",
|
||||
"faq_d_sync_title": "Synchronisation et blockchain",
|
||||
"faq_d_trbl_1_a": "Généralement non. Sur un gros portefeuille, le nœud peut passer plusieurs minutes à reconstruire les témoins blindés ou à appliquer des blocs, période durant laquelle il semble en pause et peut répondre lentement. Laissez-lui du temps — le journal de débogage (Paramètres) montre une progression régulière s'il fonctionne.\n\nÉvitez de forcer l'arrêt pendant cette phase ; cela redémarre le travail depuis le début au prochain lancement.",
|
||||
"faq_d_trbl_1_q": "Le nœud semble bloqué sur « Activation de la meilleure chaîne » — est-il figé ?",
|
||||
"faq_d_trbl_2_a": "Si un fichier de portefeuille est récupéré mais qu'une partie de ses données de clés est manquante, le nœud peut l'ouvrir dans un mode « dégradé » limité où il ne peut pas dériver de nouvelles adresses ni blinder des fonds. La solution la plus sûre est de restaurer à partir de votre phrase de récupération dans un nouveau portefeuille.",
|
||||
"faq_d_trbl_2_q": "Qu'est-ce que le « mode dégradé » ?",
|
||||
"faq_d_trbl_3_a": "Une réanalyse fait relire la chaîne par le portefeuille pour redécouvrir vos transactions (utile après l'import de clés). Une réindexation reconstruit la base de données blockchain du nœud et n'est nécessaire que si cette base est corrompue ou incompatible après un changement de version. Les deux peuvent prendre du temps ; lancez-les depuis Paramètres > Nœud et sécurité.",
|
||||
"faq_d_trbl_3_q": "Quand dois-je réindexer ou réanalyser ?",
|
||||
"faq_d_trbl_4_a": "L'utilisation de la mémoire augmente pendant un travail intensif comme la reconstruction des témoins ou une synchronisation initiale, puis redescend une fois terminé. Une mémoire élevée soutenue au repos est inhabituelle — redémarrer le portefeuille la libère. Sur une machine à faible RAM, vérifiez que d'autres applications gourmandes en mémoire ne sont pas en concurrence.",
|
||||
"faq_d_trbl_4_q": "Le nœud utilise beaucoup de mémoire — est-ce normal ?",
|
||||
"faq_d_trbl_title": "Dépannage",
|
||||
"faq_d_upd_1_a": "Ouvrez Paramètres > Nœud et sécurité > Binaire du démon et utilisez « Rechercher des mises à jour… ». Le portefeuille télécharge la dernière version vérifiée du nœud et l'installe ; la nouvelle version prend effet au prochain démarrage du démon.",
|
||||
"faq_d_upd_1_q": "Comment mettre à jour le nœud ?",
|
||||
"faq_d_upd_2_a": "Oui — la boîte de dialogue de mise à jour liste toutes les versions publiées afin que vous puissiez en choisir une spécifique ou plus ancienne. Soyez prudent en cas de rétrogradation : un nœud plus ancien peut ne pas accepter des données blockchain écrites par une version plus récente et pourrait nécessiter une réindexation.",
|
||||
"faq_d_upd_2_q": "Puis-je installer une version spécifique du nœud ?",
|
||||
"faq_d_upd_3_a": "Chaque archive de nœud téléchargée est vérifiée par une somme de contrôle et une signature cryptographique avant d'être installée. Si la vérification échoue, le portefeuille refuse la mise à jour.",
|
||||
"faq_d_upd_3_q": "La mise à jour est-elle sûre ?",
|
||||
"faq_d_upd_title": "Mise à jour du nœud",
|
||||
"faq_group_daemon": "Démon",
|
||||
"faq_group_wallet": "Portefeuille",
|
||||
"faq_intro": "Réponses sur votre portefeuille et le nœud DragonX. Cherchez, ou parcourez par sujet ci-dessous.",
|
||||
"faq_intro_lite": "Réponses sur votre portefeuille Lite. Cherchez, ou parcourez par sujet ci-dessous.",
|
||||
"faq_l_gs_1_a": "ObsidianDragonLite est le portefeuille DragonX allégé. Au lieu d'exécuter un nœud complet, il se connecte à un serveur DragonX lite, ce qui lui permet de démarrer rapidement et de ne pas télécharger toute la blockchain.\n\nIl gère vos clés, vos soldes et vos envois tout comme le portefeuille complet — voir la section « Portefeuille Lite » ci-dessous pour comprendre le fonctionnement de la connexion au serveur et le compromis en matière de confidentialité.",
|
||||
"faq_l_gs_1_q": "Qu'est-ce qu'ObsidianDragonLite ?",
|
||||
"faq_l_lite_1_a": "Le portefeuille Lite communique avec un serveur DragonX lite au lieu d'exécuter son propre nœud complet. Cela le rend rapide à démarrer et léger en disque et en bande passante, car il ne télécharge ni ne vérifie lui-même toute la blockchain. Vos clés privées restent toujours sur votre appareil — elles ne sont jamais envoyées au serveur.",
|
||||
"faq_l_lite_1_q": "En quoi le portefeuille Lite diffère-t-il du portefeuille complet ?",
|
||||
"faq_l_lite_2_a": "Ouvrez Paramètres et choisissez un serveur. Vous pouvez changer de serveur à tout moment et le portefeuille se resynchronise avec le nouveau. Si un serveur est lent ou inaccessible, essayez-en un autre.",
|
||||
"faq_l_lite_2_q": "Comment choisir ou changer de serveur ?",
|
||||
"faq_l_lite_3_a": "Vos clés ne quittent jamais votre appareil, et les montants blindés restent chiffrés. Mais le serveur lite voit quelles adresses votre portefeuille interroge ainsi que votre adresse IP, ce qui lui permet de relier vos adresses entre elles — un compromis entre confidentialité et commodité. Pour un maximum de confidentialité, utilisez le portefeuille à nœud complet, qui vérifie lui-même la chaîne et ne révèle pas vos adresses à un serveur.",
|
||||
"faq_l_lite_3_q": "Le portefeuille Lite est-il aussi privé que le nœud complet ?",
|
||||
"faq_l_lite_title": "Portefeuille Lite",
|
||||
"faq_l_sec_1_a": "Ouvrez Paramètres > Portefeuille et définissez une phrase secrète dans la section sécurité. Le chiffrement protège vos clés privées sur le disque, de sorte qu'une personne ayant accès aux fichiers ne puisse toujours pas dépenser vos pièces.\n\nChoisissez une phrase secrète forte et ne la perdez pas — il n'existe aucun moyen de récupérer un portefeuille chiffré sans elle.",
|
||||
"faq_no_results": "Aucun résultat. Essayez un autre terme de recherche.",
|
||||
"faq_open_tooltip": "Aide et FAQ",
|
||||
"faq_search_hint": "Rechercher dans la FAQ…",
|
||||
"faq_title": "Aide et FAQ",
|
||||
"faq_w_addr_1_a": "Les adresses transparentes (elles commencent par R ou t) fonctionnent comme la plupart des cryptomonnaies : les montants et adresses sont publics sur la blockchain.\n\nLes adresses blindées (adresses Z, commençant par zs) utilisent la cryptographie à divulgation nulle de connaissance — le montant, l'expéditeur et le destinataire sont chiffrés sur la chaîne. Les soldes détenus sur des adresses blindées sont privés.",
|
||||
"faq_w_addr_1_q": "Quelle est la différence entre les adresses transparentes et blindées ?",
|
||||
"faq_w_addr_2_a": "Privilégiez les adresses blindées (Z) autant que possible — elles préservent la confidentialité de votre solde et de votre historique de paiements. DragonX est une cryptomonnaie axée sur la confidentialité, et le blindage est le mode par défaut pour les fonds reçus.\n\nN'utilisez une adresse transparente que lorsqu'un service avec lequel vous interagissez ne peut pas gérer les adresses blindées.",
|
||||
"faq_w_addr_2_q": "Quel type d'adresse dois-je utiliser ?",
|
||||
"faq_w_addr_3_a": "Pour les adresses blindées, la réutilisation ne pose aucun problème et ne révèle pas l'historique. Pour les adresses transparentes, utiliser une nouvelle adresse à chaque paiement améliore la confidentialité. Créez de nouvelles adresses depuis l'onglet Recevoir.",
|
||||
"faq_w_addr_3_q": "Dois-je réutiliser une adresse ou en créer une nouvelle ?",
|
||||
"faq_w_addr_4_a": "Le blindage déplace des pièces d'une adresse transparente vers une adresse blindée, les rendant privées. Les pièces nouvellement minées arrivent en transparent et sont blindées automatiquement pour vous.\n\nVous pouvez aussi blinder manuellement depuis le portefeuille ; un blindage est simplement une transaction spéciale, il faut donc un temps de confirmation normal pour qu'elle se termine.",
|
||||
"faq_w_addr_4_q": "Qu'est-ce que le « blindage » ?",
|
||||
"faq_w_addr_title": "Adresses et confidentialité",
|
||||
"faq_w_bal_1_a": "Tant que votre portefeuille est encore en cours de synchronisation, votre solde est incomplet et peut afficher 0 — il n'a pas encore analysé toutes vos transactions. Il se complète une fois la synchronisation terminée.\n\nSurveillez la barre d'état en bas : elle indique l'état de la connexion et la hauteur de bloc. Une fois la synchronisation terminée, votre solde est exact.",
|
||||
"faq_w_bal_1_q": "Pourquoi mon solde est-il à 0 ou ne se met-il pas à jour ?",
|
||||
"faq_w_bal_2_a": "Chaque nouveau bloc miné au-dessus du bloc contenant votre transaction ajoute une confirmation. Plus il y a de confirmations, plus le paiement est fermement établi. Les fonds reçus deviennent dépensables après la première confirmation.",
|
||||
"faq_w_bal_2_q": "Que sont les confirmations ?",
|
||||
"faq_w_bal_3_a": "Le total inclut les fonds encore en cours de confirmation ou temporairement verrouillés (par exemple, des pièces en cours de blindage). Le solde dépensable est ce que vous pouvez envoyer immédiatement. Les deux convergent au fur et à mesure que les transactions se confirment.",
|
||||
"faq_w_bal_3_q": "Quelle est la différence entre le solde total et le solde dépensable ?",
|
||||
"faq_w_bal_title": "Solde et synchronisation",
|
||||
"faq_w_chat_1_a": "Le chat est une messagerie chiffrée intégrée au portefeuille, fonctionnant sur la chaîne. Les messages sont envoyés sous forme de mémos blindés privés, de sorte que seuls vous et votre contact pouvez les lire.",
|
||||
"faq_w_chat_1_q": "Qu'est-ce que la fonction Chat ?",
|
||||
"faq_w_chat_2_a": "Votre identité de chat est dérivée de la phrase de récupération de votre portefeuille, elle vous suit donc avec votre portefeuille — restaurez le portefeuille et votre identité revient. Vous ne créez pas de compte ou de mot de passe distinct.",
|
||||
"faq_w_chat_2_q": "Comment fonctionne mon identité de chat ?",
|
||||
"faq_w_chat_3_a": "Ajoutez un contact via son adresse dans l'onglet Contacts, avec éventuellement un nom et un avatar. Les contacts peuvent rester privés au portefeuille actuel ou être partagés entre tous les portefeuilles que vous ouvrez sur cet ordinateur.",
|
||||
"faq_w_chat_3_q": "Comment fonctionnent les contacts ?",
|
||||
"faq_w_chat_4_a": "Ouvrez une conversation et utilisez les icônes de son en-tête. Masquer (l'œil) la retire de la liste mais conserve tous les messages — un nouveau message la fait réapparaître. Supprimer (la corbeille) ouvre une boîte de dialogue : « Supprimer » efface l'historique sur cet appareil (si cette personne vous écrit à nouveau, la conversation revient), tandis que « Supprimer et bloquer » arrête aussi ses futurs messages jusqu'à ce que vous la débloquiez depuis la liste « Bloqués » au-dessus de la liste des conversations. Tout ceci est local à cet appareil — les messages restent sur la blockchain et l'autre personne conserve sa propre copie, ce n'est donc pas un « retrait de message ».",
|
||||
"faq_w_chat_4_q": "Comment masquer, supprimer ou bloquer une conversation ?",
|
||||
"faq_w_chat_title": "Chat et contacts",
|
||||
"faq_w_gs_1_a": "ObsidianDragon est un portefeuille à nœud complet pour DragonX (DRGX). Il gère vos pièces et, sur la version bureau, exécute un nœud complet DragonX (le démon) en arrière-plan afin que votre portefeuille vérifie lui-même la blockchain plutôt que de faire confiance à un serveur tiers.\n\nVoyez-le comme deux parties fonctionnant ensemble : le portefeuille (clés, soldes, envoi) et le nœud (la blockchain et le réseau). Les questions ici sont réparties de la même façon — voir les onglets Portefeuille et Démon ci-dessus.",
|
||||
"faq_w_gs_1_q": "Qu'est-ce qu'ObsidianDragon ?",
|
||||
"faq_w_gs_2_a": "Au premier lancement, l'assistant de configuration crée automatiquement un portefeuille pour vous. Les nouveaux portefeuilles sont sécurisés par une phrase de récupération secrète (une liste de mots). Notez cette phrase et conservez-la hors ligne — c'est le seul moyen de restaurer vos fonds si cet ordinateur est perdu.\n\nVous pouvez consulter ou sauvegarder la phrase à tout moment depuis Paramètres > Sauvegarde et données > Phrase de récupération.",
|
||||
"faq_w_gs_2_q": "Comment créer un nouveau portefeuille ?",
|
||||
"faq_w_gs_3_a": "Utilisez le flux de restauration par phrase de récupération si vous en avez une. (Sur la version à nœud complet, vous pouvez aussi copier un fichier wallet.dat existant dans le dossier de données du portefeuille avant le lancement.)\n\nAprès la restauration, le portefeuille se réanalyse pour retrouver vos transactions passées ; votre solde et votre historique peuvent donc mettre un moment à apparaître la première fois.",
|
||||
"faq_w_gs_3_q": "Comment restaurer un portefeuille que j'ai déjà ?",
|
||||
"faq_w_gs_4_a": "L'assistant vous permet de choisir une apparence/un thème, de configurer le chiffrement et un code PIN, et — sur la version à nœud complet — de télécharger en option un bootstrap pour accélérer la première synchronisation. Vous pouvez ignorer n'importe quelle étape et modifier tout cela plus tard dans Paramètres.",
|
||||
"faq_w_gs_4_q": "Que fait la configuration au premier lancement ?",
|
||||
"faq_w_gs_title": "Prise en main",
|
||||
"faq_w_sec_1_a": "Ouvrez Paramètres > Nœud et sécurité et définissez une phrase secrète (section SÉCURITÉ). Le chiffrement protège vos clés privées sur le disque, de sorte qu'une personne ayant accès aux fichiers ne puisse toujours pas dépenser vos pièces.\n\nChoisissez une phrase secrète forte et ne la perdez pas — il n'existe aucun moyen de récupérer un portefeuille chiffré sans elle.",
|
||||
"faq_w_sec_1_q": "Comment chiffrer mon portefeuille ?",
|
||||
"faq_w_sec_2_a": "Le code PIN verrouille l'écran de l'application afin que les soldes et actions soient masqués lorsque vous vous absentez. C'est un verrouillage de confort qui s'ajoute au chiffrement ; vous le configurez pendant l'assistant ou dans Paramètres, après avoir chiffré le portefeuille.",
|
||||
"faq_w_sec_2_q": "Qu'est-ce que le code PIN / l'écran de verrouillage ?",
|
||||
"faq_w_sec_3_a": "Non. Le chiffrement du portefeuille ne peut pas être contourné. Si vous avez encore votre phrase de récupération, vous pouvez restaurer le portefeuille à partir de celle-ci dans un nouveau portefeuille et définir une nouvelle phrase secrète. Sans la phrase secrète ni la phrase de récupération, les fonds ne peuvent pas être récupérés.",
|
||||
"faq_w_sec_3_q": "J'ai oublié ma phrase secrète — peut-elle être récupérée ?",
|
||||
"faq_w_sec_title": "Sécurité et chiffrement",
|
||||
"faq_w_seed_1_a": "Allez dans Paramètres > Sauvegarde et données > Phrase de récupération. Notez les mots sur papier dans l'ordre et conservez-les en lieu sûr et hors ligne. Toute personne possédant la phrase peut dépenser vos pièces, ne la stockez donc jamais dans une photo, un e-mail ou une note dans le cloud.",
|
||||
"faq_w_seed_1_q": "Comment sauvegarder ma phrase de récupération ?",
|
||||
"faq_w_seed_2_a": "Les anciens portefeuilles (hérités) n'étaient pas basés sur une phrase de récupération. Le portefeuille peut migrer un portefeuille hérité vers un portefeuille moderne à phrase de récupération : il crée un nouveau portefeuille à phrase et y transfère vos fonds. Cherchez « Migrer vers un portefeuille à phrase… » dans Paramètres > Sauvegarde et données. Cette fonctionnalité nécessite la version actuelle du démon.",
|
||||
"faq_w_seed_2_q": "Mon portefeuille n'a pas de phrase de récupération — puis-je en ajouter une ?",
|
||||
"faq_w_seed_3_a": "Utilisez le flux de restauration par phrase de récupération lors de la configuration d'un portefeuille et saisissez vos mots dans l'ordre. Votre portefeuille se réanalyse ensuite pour reconstruire votre solde et votre historique, ce qui peut prendre un moment la première fois.",
|
||||
"faq_w_seed_3_q": "Comment restaurer à partir de ma phrase de récupération ?",
|
||||
"faq_w_seed_4_a": "Votre phrase de récupération est la sauvegarde principale et suffit à tout restaurer. Une copie de votre fichier de portefeuille est une sauvegarde secondaire pratique qui conserve aussi les libellés et les paramètres. Conservez toute sauvegarde hors ligne et en lieu privé.",
|
||||
"faq_w_seed_4_q": "Dois-je aussi sauvegarder wallet.dat ?",
|
||||
"faq_w_seed_title": "Phrase de récupération et sauvegarde",
|
||||
"faq_w_send_1_a": "Ouvrez l'onglet Envoyer, collez l'adresse du destinataire, saisissez un montant et confirmez. Si le portefeuille est chiffré, on vous demandera de le déverrouiller pour la transaction.\n\nLes envois vers des adresses blindées sont privés ; les envois vers des adresses transparentes sont publics.",
|
||||
"faq_w_send_1_q": "Comment envoyer des fonds ?",
|
||||
"faq_w_send_2_a": "Les frais DragonX sont très faibles et définis automatiquement. Les frais sont affichés avant que vous ne confirmiez. Vous n'avez normalement pas besoin de les modifier.",
|
||||
"faq_w_send_2_q": "Quels frais dois-je payer ?",
|
||||
"faq_w_send_3_a": "Oui — lors de l'envoi vers une adresse blindée (Z), vous pouvez inclure un mémo chiffré. Seul le destinataire peut le lire. Les adresses transparentes ne prennent pas en charge les mémos.",
|
||||
"faq_w_send_3_q": "Puis-je joindre un message à un paiement ?",
|
||||
"faq_w_send_4_a": "Une transaction est « en attente » jusqu'à ce qu'elle soit minée dans un bloc et gagne des confirmations. Cela prend généralement une minute ou deux. Les transactions blindées nécessitent aussi que votre portefeuille soit synchronisé pour construire la preuve.\n\nSi un envoi reste en attente anormalement longtemps, vérifiez que votre portefeuille est connecté et synchronisé (voir la barre d'état en bas de la fenêtre).",
|
||||
"faq_w_send_4_q": "J'ai envoyé un paiement mais il indique « en attente » — pourquoi ?",
|
||||
"faq_w_send_title": "Envoi et réception",
|
||||
"faq_w_set_1_a": "Ouvrez Paramètres > Apparence pour choisir un thème et un accent. Plusieurs habillages clairs et sombres sont inclus, et des effets comme le flou peuvent être désactivés pour les machines moins puissantes.",
|
||||
"faq_w_set_1_q": "Comment changer le thème ?",
|
||||
"faq_w_set_2_a": "Oui — Paramètres > Apparence possède un sélecteur de langue. Le portefeuille est fourni avec plusieurs traductions ; ce qui n'est pas encore traduit revient à l'anglais.",
|
||||
"faq_w_set_2_q": "Puis-je changer la langue ?",
|
||||
"faq_w_set_3_a": "Utilisez l'option d'échelle de police dans Paramètres > Apparence. Elle redimensionne toute l'interface, ce qui est pratique sur les écrans haute résolution (HiDPI) où l'application pourrait sinon s'afficher trop petite.",
|
||||
"faq_w_set_3_q": "Le texte est trop petit (ou trop grand) — puis-je le redimensionner ?",
|
||||
"faq_w_set_4_a": "Activez la récupération des prix dans Paramètres. Le portefeuille affiche alors une valeur fiduciaire approximative à côté des soldes et peut afficher un petit graphique de marché. Les prix sont fournis à titre informatif uniquement.",
|
||||
"faq_w_set_4_q": "Comment afficher les prix en monnaie fiduciaire ?",
|
||||
"faq_w_set_title": "Paramètres et apparence",
|
||||
"favorite_address": "Ajouter aux favoris",
|
||||
"fee": "Frais",
|
||||
"fee_high": "Élevés",
|
||||
@@ -593,6 +878,63 @@
|
||||
"general": "Général",
|
||||
"generating": "Génération",
|
||||
"go_to_receive": "Aller à Recevoir",
|
||||
"grpa_current_block_paren": "(Actuel : %d)",
|
||||
"grpa_days_ago": "il y a %lld jours",
|
||||
"grpa_dbg_addrman": "Suivi et gestion des adresses des pairs",
|
||||
"grpa_dbg_alert": "Messages du système d'alerte",
|
||||
"grpa_dbg_bench": "Chronométrage des performances des opérations",
|
||||
"grpa_dbg_coindb": "Opérations de lecture/écriture de la base de données des pièces",
|
||||
"grpa_dbg_db": "Opérations Berkeley DB",
|
||||
"grpa_dbg_estimatefee": "Algorithme d'estimation des frais",
|
||||
"grpa_dbg_http": "Activité du serveur RPC HTTP",
|
||||
"grpa_dbg_libevent": "Bibliothèque réseau Libevent",
|
||||
"grpa_dbg_lock": "Débogage de la contention des verrous",
|
||||
"grpa_dbg_mempool": "Activité du pool mémoire des transactions",
|
||||
"grpa_dbg_net": "Connexions et messages réseau",
|
||||
"grpa_dbg_paymentdisclosure": "Protocole de divulgation de paiement",
|
||||
"grpa_dbg_pow": "Activité de minage par preuve de travail",
|
||||
"grpa_dbg_proxy": "Connexions proxy SOCKS5",
|
||||
"grpa_dbg_prune": "Opérations d'élagage des blocs",
|
||||
"grpa_dbg_rand": "Génération de nombres aléatoires",
|
||||
"grpa_dbg_reindex": "Progression de la réindexation de la blockchain",
|
||||
"grpa_dbg_rpc": "Traitement des commandes RPC",
|
||||
"grpa_dbg_selectcoins": "Sélection des pièces pour les transactions",
|
||||
"grpa_dbg_tor": "Intégration de Tor et informations de circuit",
|
||||
"grpa_dbg_zmq": "Système de notification ZeroMQ",
|
||||
"grpa_dbg_zrpc": "Opérations RPC protégées (z-addr)",
|
||||
"grpa_enter_private_key_to_import": "Saisissez une clé privée à importer.",
|
||||
"grpa_error_prefix": "Erreur : ",
|
||||
"grpa_hr_ago": "il y a %lld h",
|
||||
"grpa_invalid_response_from_daemon": "Réponse invalide du démon",
|
||||
"grpa_invalid_suffix": " (invalide)",
|
||||
"grpa_min_ago": "il y a %lld min",
|
||||
"grpa_sec_ago": "il y a %lld s",
|
||||
"grpa_seed_demo_chat": "Chat de démonstration seed",
|
||||
"grpa_showing_first_100_of": "... affichage des 100 premiers sur %d",
|
||||
"grpa_tab_about": "À propos",
|
||||
"grpa_tab_appearance": "Apparence",
|
||||
"grpa_tab_backup_data": "Sauvegarde et données",
|
||||
"grpa_tab_chat": "Chat",
|
||||
"grpa_tab_explorer": "Explorateur",
|
||||
"grpa_tab_node_security": "Nœud et sécurité",
|
||||
"grpa_tab_wallet": "Portefeuille",
|
||||
"grpa_unexpected_getblockhash_result": "résultat getblockhash inattendu",
|
||||
"grpb_copy": "Copier",
|
||||
"grpb_max": "Max",
|
||||
"grpb_new_badge_suffix": " [NOUVEAU]",
|
||||
"grpb_preview_msg_payment_through": "Le paiement est-il passé ? 🙂",
|
||||
"grpb_preview_msg_sending_rest": "J'envoie le reste maintenant 👍",
|
||||
"grpb_preview_msg_yep_confirmed": "Oui — je viens de confirmer ✅",
|
||||
"grpb_selected_suffix": "\n(sélectionné)",
|
||||
"grpb_tooltip_address_balance": "%s\nSolde : %.8f %s%s",
|
||||
"grpb_undo_clear": "Annuler l'effacement",
|
||||
"grpc_benchmark_inconclusive": "Benchmark non concluant : aucun échantillon de taux de hachage n'a été enregistré. Vérifiez la connexion au pool et réessayez.",
|
||||
"grpc_benchmark_takes_secs": "Le benchmark prend environ %d s et interrompt le minage. Cliquez de nouveau pour démarrer.",
|
||||
"grpc_bootstrap_failed": "Échec de l'amorçage",
|
||||
"grpc_bootstrap_not_initialized": "Amorçage non initialisé",
|
||||
"grpc_hashrate_fee": "%s %s%% de frais",
|
||||
"grpc_key_not_available": "Clé non disponible pour cette adresse",
|
||||
"grpc_na": "N/D",
|
||||
"height": "Hauteur",
|
||||
"help": "Aide",
|
||||
"hidden_tag": " (masqué)",
|
||||
@@ -817,9 +1159,27 @@
|
||||
"memo_optional": "MÉMO (OPTIONNEL)",
|
||||
"memo_upper": "MÉMO",
|
||||
"memo_z_only": "Note : Les mémos ne sont disponibles que lors de l'envoi vers des adresses blindées (z)",
|
||||
"merge_addr_created": "Adresse blindée créée.",
|
||||
"merge_advanced": "Avancé",
|
||||
"merge_back": "Retour",
|
||||
"merge_batch_fmt": "Fusionne jusqu'à %d entrées par passage — répétez pour terminer le reste.",
|
||||
"merge_confirm_btn": "Confirmer",
|
||||
"merge_confirm_fmt": "Consolider ~%s DRGX depuis %d entrée(s) vers %s ?",
|
||||
"merge_create_zaddr": "Créer une adresse blindée",
|
||||
"merge_creating": "Création de l'adresse…",
|
||||
"merge_description": "Fusionnez plusieurs UTXOs en une seule adresse blindée. Cela peut réduire la taille du portefeuille et améliorer la confidentialité.",
|
||||
"merge_fee_hint": "Frais réseau pour cette transaction.",
|
||||
"merge_funds": "Fusionner les fonds",
|
||||
"merge_max_inputs": "Nombre max d'entrées par lot",
|
||||
"merge_no_spendable": "Aucune entrée dépensable à consolider pour l'instant.",
|
||||
"merge_progress": "Consolidation en cours… cela peut prendre quelques minutes. Vous pouvez fermer cette fenêtre.",
|
||||
"merge_scope_fmt": "%d entrées transparentes + %d blindées · ~%s DRGX dépensables",
|
||||
"merge_scope_loading": "Vérification de vos entrées…",
|
||||
"merge_send_failed": "Échec de la fusion : ",
|
||||
"merge_source": "Consolider",
|
||||
"merge_src_both": "Les deux",
|
||||
"merge_src_shielded": "Blindées",
|
||||
"merge_src_transparent": "Transparentes",
|
||||
"merge_started": "Opération de fusion démarrée",
|
||||
"merge_title": "Fusionner vers une adresse",
|
||||
"mig_adopt": "Faire de ceci mon portefeuille",
|
||||
@@ -1059,6 +1419,13 @@
|
||||
"peers_upper": "PAIRS",
|
||||
"peers_version": "Version",
|
||||
"pending": "En attente",
|
||||
"picker_dat_count": "%d fichier(s) de portefeuille dans ce dossier",
|
||||
"picker_dat_none": "Aucun fichier de portefeuille directement dans ce dossier",
|
||||
"picker_empty": "Ce dossier ne contient aucun sous-dossier ni fichier de portefeuille.",
|
||||
"picker_home": "Dossier personnel",
|
||||
"picker_select": "Analyser ce dossier",
|
||||
"picker_title": "Sélectionnez un dossier à analyser",
|
||||
"picker_up": "Remonter d'un niveau",
|
||||
"pin_change_desc": "Modifiez votre code PIN de déverrouillage. Vous avez besoin de votre code PIN actuel et d'un nouveau code PIN.",
|
||||
"pin_confirm_new_label": "Confirmer le nouveau code PIN :",
|
||||
"pin_current_label": "Code PIN actuel :",
|
||||
@@ -1195,6 +1562,7 @@
|
||||
"save_z_transactions": "Enregistrer les Z-tx dans la liste",
|
||||
"sb_auth_failed": "Authentification échouée — vérifiez rpcuser/rpcpassword",
|
||||
"sb_block": "Bloc : %d",
|
||||
"sb_block_db_unreadable": "Base de données des blocs illisible — reconstruction requise",
|
||||
"sb_building_witnesses": "Configuration des témoins",
|
||||
"sb_building_witnesses_pct": "Reconstruction des témoins %.0f%%",
|
||||
"sb_connecting_daemon": "Connexion à dragonxd...",
|
||||
@@ -1210,6 +1578,7 @@
|
||||
"sb_dragonxd_stopped": "dragonxd arrêté",
|
||||
"sb_dragonxd_stopping": "Arrêt de dragonxd...",
|
||||
"sb_extracting_sapling": "Extraction des paramètres Sapling...",
|
||||
"sb_finishing_repair": "Finalisation de la réparation du portefeuille — nouvelle analyse en cours…",
|
||||
"sb_importing_keys": "Importation des clés",
|
||||
"sb_loading_config": "Chargement de la configuration...",
|
||||
"sb_mining_hs": "%.1f H/s",
|
||||
@@ -1231,6 +1600,7 @@
|
||||
"sb_waiting_config": "En attente de la configuration du daemon...",
|
||||
"sb_waiting_daemon": "En attente de dragonxd...",
|
||||
"sb_waiting_daemon_err": "En attente de dragonxd — %s",
|
||||
"sb_wallet_needs_recovery": "Réparation du portefeuille disponible",
|
||||
"sb_warming_up": "Démarrage...",
|
||||
"sb_witness_cache": "Reconstruction des témoins",
|
||||
"scale_effects": "ÉCHELLE ET EFFETS",
|
||||
@@ -1240,6 +1610,45 @@
|
||||
"screenshot_sweep_full": "Balayage complet de l'interface",
|
||||
"search_icons": "Rechercher des icônes...",
|
||||
"search_placeholder": "Rechercher...",
|
||||
"sec_changing_passphrase": "Modification de la phrase secrète...",
|
||||
"sec_changing_pin": "Modification du PIN...",
|
||||
"sec_couldnt_lock_wallet": "Impossible de verrouiller le portefeuille — il reste déverrouillé. Vérifiez la connexion au démon.",
|
||||
"sec_encrypted_backup_suffix": "\nSauvegarde chiffrée : wallet.dat.encrypted.bak",
|
||||
"sec_encrypting_wallet": "Chiffrement du portefeuille...",
|
||||
"sec_encryption_did_not_complete": "Le chiffrement du portefeuille ne s'est pas terminé — votre portefeuille N'est PAS chiffré. Ouvrez les Paramètres pour terminer le chiffrement.",
|
||||
"sec_encryption_failed_prefix": "Échec du chiffrement : ",
|
||||
"sec_failed_prefix": "Échec : ",
|
||||
"sec_failed_to_create_vault": "Échec de la création du coffre",
|
||||
"sec_importing_keys_rescanning": "Importation des clés et nouvelle analyse de la blockchain — le portefeuille reste utilisable pendant l'opération",
|
||||
"sec_incorrect_current_pin": "PIN actuel incorrect",
|
||||
"sec_incorrect_passphrase_decrypt": "Phrase secrète incorrecte",
|
||||
"sec_incorrect_passphrase_pin_setup": "Phrase secrète incorrecte",
|
||||
"sec_incorrect_pin_remove": "PIN incorrect",
|
||||
"sec_internal_error_change_pin": "Erreur interne",
|
||||
"sec_internal_error_remove_pin": "Erreur interne",
|
||||
"sec_mode_passphrase": " Phrase secrète",
|
||||
"sec_not_connected_to_daemon": "Non connecté au démon",
|
||||
"sec_not_connected_to_daemon_pin": "Non connecté au démon",
|
||||
"sec_passphrase_changed_successfully": "Phrase secrète modifiée avec succès",
|
||||
"sec_pin_changed_successfully": "PIN modifié avec succès",
|
||||
"sec_pin_removed": "PIN supprimé",
|
||||
"sec_pin_set_successfully": "PIN défini avec succès",
|
||||
"sec_restart_daemon_for_encryption": "Veuillez redémarrer votre démon pour que le chiffrement prenne effet.",
|
||||
"sec_too_many_attempts_wait": "Trop de tentatives. Patientez %.0f secondes...",
|
||||
"sec_total_elapsed_fmt": "Temps total écoulé : %dm %02ds",
|
||||
"sec_unlock_button": "Déverrouiller",
|
||||
"sec_unlock_failed_prefix": "Échec du déverrouillage : ",
|
||||
"sec_unlocking_fmt": "Déverrouillage%s",
|
||||
"sec_use_passphrase_instead": "Utiliser une phrase secrète",
|
||||
"sec_use_pin_instead": "Utiliser un PIN",
|
||||
"sec_verifying_passphrase": "Vérification de la phrase secrète...",
|
||||
"sec_verifying_pin": "Vérification du PIN...",
|
||||
"sec_wallet_decrypted_all_keys_imported": "Portefeuille déchiffré avec succès ! Toutes les clés ont été importées.",
|
||||
"sec_wallet_encrypted_and_pin_set": "Portefeuille chiffré et PIN défini",
|
||||
"sec_wallet_encrypted_but_pin_vault_failed": "Portefeuille chiffré mais échec du coffre PIN",
|
||||
"sec_wallet_encrypted_restarting_daemon": "Portefeuille chiffré. Redémarrage du démon...",
|
||||
"sec_wallet_encrypted_successfully": "Portefeuille chiffré avec succès",
|
||||
"sec_wallet_locked_title": "Portefeuille verrouillé",
|
||||
"security": "SÉCURITÉ",
|
||||
"seed_backup_button": "Phrase de récupération",
|
||||
"seed_backup_close": "Fermer",
|
||||
@@ -1263,6 +1672,7 @@
|
||||
"send_amount": "Montant",
|
||||
"send_amount_details": "DÉTAILS DU MONTANT",
|
||||
"send_amount_upper": "MONTANT",
|
||||
"send_available_note": "disponible",
|
||||
"send_cannot_send_now": "Envoi impossible pour le moment — vérifiez la connexion, la synchronisation et le solde disponible.",
|
||||
"send_clear_fields": "Effacer tous les champs du formulaire ?",
|
||||
"send_contacts_button": "Choisir parmi les contacts",
|
||||
@@ -1450,17 +1860,34 @@
|
||||
"status": "Statut",
|
||||
"stop_external": "Arrêter le daemon externe",
|
||||
"stop_mining": "Arrêter le minage",
|
||||
"stratum_allowip_hint": "Autoriser les mineurs depuis une IP ou un CIDR (vide = cet ordinateur uniquement)",
|
||||
"stratum_expose_warn": "Ouvre un port de minage vers le réseau que vous autorisez — à utiliser uniquement sur un réseau local de confiance.",
|
||||
"stratum_host": "Héberger un pool de minage (stratum)",
|
||||
"stratum_host_hint": "Les mineurs se connectent à cet ordinateur sur le port 22769 (port RPC + 1000) avec un mineur stratum RandomX. Redémarrez le démon pour appliquer.",
|
||||
"stratum_host_section": "HÉBERGEMENT DE POOL DE MINAGE",
|
||||
"submitting_transaction": "Soumission de la transaction...",
|
||||
"success": "Succès",
|
||||
"summary": "Résumé",
|
||||
"sweep_button": "Balayer",
|
||||
"sweep_caveat": "Importe la clé pour signer une transaction qui déplace tous ses fonds vers votre adresse. La clé reste dans votre portefeuille avec un solde vide.",
|
||||
"sweep_current_theme_only": "Thème actuel uniquement",
|
||||
"sweep_dest_label": "Envoyer les fonds balayés vers",
|
||||
"sweep_dest_new": "Nouvelle adresse blindée (recommandé)",
|
||||
"sweep_done": "Terminé — fonds balayés vers votre adresse.",
|
||||
"sweep_to": "Balayé vers :",
|
||||
"sweep_toggle": "Balayer vers mon portefeuille (ne pas conserver la clé)",
|
||||
"sweep_tx": "Transaction :",
|
||||
"swin_connection_failed": "Échec de la connexion : ",
|
||||
"swin_connection_successful": "Connexion réussie !\nVersion de dragonxd : ",
|
||||
"swin_invalid_suffix": " (invalide)",
|
||||
"swin_no_history_file_found": "Aucun fichier d'historique trouvé",
|
||||
"swin_rescan_failed": "Échec de la nouvelle analyse : ",
|
||||
"swin_rescan_started_from_block": "Nouvelle analyse démarrée à partir du bloc ",
|
||||
"swin_rescan_to": " à ",
|
||||
"swin_rpc_client_not_initialized": "Client RPC non initialisé",
|
||||
"swin_settings_saved": "Paramètres enregistrés",
|
||||
"swin_theme_list_refreshed": "Liste des thèmes actualisée",
|
||||
"swin_ztx_history_cleared": "Historique des transactions Z effacé",
|
||||
"switch_corrupt_body": "Ce portefeuille semble corrompu — le nœud n'a pas pu l'ouvrir. Restaurez-le depuis une sauvegarde, recréez-le ou tentez de le réparer.",
|
||||
"switch_corrupt_repair": "Tenter une réparation (salvage)",
|
||||
"switch_progress_background": "Continuer en arrière-plan",
|
||||
@@ -1621,6 +2048,8 @@
|
||||
"tt_simple_bg": "Utiliser un dégradé simple pour l'arrière-plan\\nRaccourci : Ctrl+Up",
|
||||
"tt_simple_bg_alt": "Utiliser une version dégradée de l'image d'arrière-plan du thème\\nRaccourci : Ctrl+Up",
|
||||
"tt_stop_external": "S'applique lors de la connexion à un daemon\\ndémarré en dehors de ce portefeuille",
|
||||
"tt_stratum_host": "Exécute un serveur de pool stratum RandomX sur ce nœud afin que d'autres mineurs RandomX puissent pointer vers cet ordinateur. Nécessite un nœud v1.3.0+ et un redémarrage du démon pour appliquer.",
|
||||
"tt_sweep_current_theme_only": "Balayer uniquement le thème actif au lieu de parcourir tous les thèmes",
|
||||
"tt_test_conn": "Vérifier la connexion RPC au daemon",
|
||||
"tt_theme_effects": "Scintillement, lueur, cycle de teinte par thème",
|
||||
"tt_theme_hotkey": "Raccourci : Ctrl+Gauche/Droite pour changer de thème",
|
||||
@@ -1629,6 +2058,7 @@
|
||||
"tt_ui_opacity": "Opacité des cartes et de la barre latérale (100% = entièrement opaque, plus bas = plus transparent)",
|
||||
"tt_validate": "Vérifier si une adresse DragonX est valide",
|
||||
"tt_verbose": "Journaliser les diagnostics de connexion détaillés,\\nl'état du daemon et les informations de propriétaire de port\\ndans l'onglet Console",
|
||||
"tt_wallet_size_warn": "Les portefeuilles blindés grandissent avec les données de témoin de chaque note — fusionner de nombreuses notes en une seule adresse réduit cette taille. Sauvegardez d'abord.",
|
||||
"tt_wallets_button": "Répertoriez vos fichiers de portefeuille et passez de l'un à l'autre",
|
||||
"tt_website": "Ouvrir le site web DragonX",
|
||||
"tt_window_opacity": "Opacité de l'arrière-plan (plus bas = bureau visible à travers la fenêtre)",
|
||||
@@ -1695,8 +2125,68 @@
|
||||
"view_on_explorer": "Voir dans l'explorateur",
|
||||
"waiting_for_daemon": "En attente de la connexion au daemon...",
|
||||
"wallet": "PORTEFEUILLE",
|
||||
"wallet_degraded_notify": "Votre portefeuille s'est ouvert en mode à fonctionnalités réduites : les fonds existants sont en sécurité et dépensables, mais la création de nouvelles adresses et le blindage sont désactivés. Sauvegardez votre phrase de récupération et restaurez-la pour réparer entièrement le portefeuille.",
|
||||
"wallet_diagnostics_hdr": "DIAGNOSTICS",
|
||||
"wallet_empty": "Votre portefeuille est vide",
|
||||
"wallet_empty_hint": "Passez à Recevoir pour obtenir votre adresse et commencer à recevoir des fonds.",
|
||||
"wallet_options_hdr": "OPTIONS",
|
||||
"wallet_rebuild_failed": "La reconstruction n'a pas produit de portefeuille valide, donc rien n'a été modifié. Votre portefeuille est intact.",
|
||||
"wallet_rebuild_install_failed": "Impossible d'installer le portefeuille reconstruit ; votre portefeuille actuel a été laissé en place.",
|
||||
"wallet_rebuild_no_helper": "L'assistant de reconstruction du portefeuille n'est pas disponible dans cette version. Utilisez Restaurer, ou reconstruisez manuellement.",
|
||||
"wallet_rebuild_no_source": "Impossible de trouver un portefeuille lisible à reconstruire. Rien n'a été modifié.",
|
||||
"wallet_rebuild_ok": "Base de données du portefeuille reconstruite — le nœud la charge et effectue une nouvelle analyse pour retrouver votre solde.",
|
||||
"wallet_rebuild_started": "Réparation de votre fichier de portefeuille en cours — veuillez ne pas fermer cette fenêtre.",
|
||||
"wallet_recovered_body": "Rien n'a été supprimé. Votre portefeuille d'origine est toujours enregistré sur votre ordinateur sous forme de fichier de sauvegarde daté, et chaque option ci-dessous se contente de copier ou de renommer des fichiers — elle n'en efface jamais un. Vos clés ne sont jamais régénérées, seulement relues.\n\nLa copie réparée actuellement chargée peut ne pas inclure quelques transactions récentes ; votre solde peut donc paraître légèrement bas jusqu'à ce que vous terminiez ci-dessous et qu'il se re-synchronise.",
|
||||
"wallet_recovered_dismiss": "Pas maintenant — conserver la copie réparée",
|
||||
"wallet_recovered_dismiss_sub": "Aucun fichier ne change. Vous pouvez rouvrir ceci à tout moment depuis la barre d'état ; votre original reste sauvegardé dans tous les cas.",
|
||||
"wallet_recovered_notify": "Votre fichier de portefeuille avait besoin d'une réparation — votre original a été sauvegardé en toute sécurité. Ouvrez l'application pour examiner vos options.",
|
||||
"wallet_recovered_open_folder": "Montrez-moi les fichiers",
|
||||
"wallet_recovered_open_folder_sub": "Ouvre le dossier de données du portefeuille pour que vous puissiez inspecter vous-même les fichiers de sauvegarde — rien n'est modifié.",
|
||||
"wallet_recovered_rebuild": "Réparer automatiquement (recommandé)",
|
||||
"wallet_recovered_rebuild_sub": "Relit chaque enregistrement récupérable de votre fichier de portefeuille le plus complet et en écrit un nouveau propre, puis redémarre. L'option la plus complète — votre fichier actuel est conservé comme sauvegarde datée dans tous les cas.",
|
||||
"wallet_recovered_restore": "Restaurer le fichier original à la place",
|
||||
"wallet_recovered_restore_sub": "Remet en place votre plus grande sauvegarde intacte, telle quelle, puis relance l'analyse — un peu plus rapide, mais seulement aussi complet que ce fichier l'était. Votre fichier actuel est conservé comme sauvegarde datée dans tous les cas.",
|
||||
"wallet_recovered_safety": "Vos DRGX sont en sécurité.",
|
||||
"wallet_recovered_title": "Votre fichier de portefeuille a besoin d'une réparation rapide",
|
||||
"wallet_recovered_warn": "Au démarrage de l'application, elle a détecté que votre fichier de portefeuille ne passait pas son contrôle de cohérence — cela arrive généralement après une mise à jour de l'application ou un arrêt brutal. L'application a déjà protégé vos données : elle a mis de côté l'ancien fichier et chargé une copie réparée pour que vous ne soyez pas bloqué.",
|
||||
"wallet_recovery_decide_later": "Décider plus tard",
|
||||
"wallet_recovery_decide_later_tip": "Ferme ceci et conserve la copie actuellement chargée. Rien n'est modifié, et vous pouvez réparer à tout moment — la barre d'état garde un lien « Réparation du portefeuille disponible ».",
|
||||
"wallet_recovery_details_label": "Afficher les détails techniques",
|
||||
"wallet_recovery_done": "Terminé",
|
||||
"wallet_recovery_failure_body": "La réparation s'est exécutée, mais son résultat n'a pas passé la vérification (il était illisible ou ne contenait aucune adresse), il a donc été automatiquement rejeté avant de remplacer quoi que ce soit. Votre portefeuille est exactement comme avant que vous cliquiez — rien n'a changé sur le disque.",
|
||||
"wallet_recovery_failure_title": "La réparation n'a pas abouti",
|
||||
"wallet_recovery_files_detail": "Votre portefeuille original est toujours là — l'application l'a renommé en sauvegarde datée (wallet.<numbers>.bak) dans votre dossier de données et n'a rien supprimé. « Réparer » lit chaque enregistrement de votre fichier le plus complet vers un tout nouveau fichier propre. « Restaurer » recopie votre plus grande sauvegarde exactement telle quelle. La copie actuellement chargée est elle aussi conservée comme sauvegarde.",
|
||||
"wallet_recovery_files_label": "Qu'advient-il de mes fichiers ?",
|
||||
"wallet_recovery_notnow_short": "Pas maintenant",
|
||||
"wallet_recovery_other_options": "Autres options",
|
||||
"wallet_recovery_rebuild_card": "Réparer automatiquement",
|
||||
"wallet_recovery_rebuild_card_desc": "Lit chaque enregistrement récupérable vers un fichier propre, puis redémarre. L'option la plus complète.",
|
||||
"wallet_recovery_recommended": "RECOMMANDÉ",
|
||||
"wallet_recovery_repair_go": "Réparer",
|
||||
"wallet_recovery_rescan_body": "Réanalyse de la blockchain pour reconstruire votre solde et l'historique des transactions. Cela peut prendre plusieurs minutes — vous n'avez rien à faire, et merci de ne pas redémarrer le nœud pendant l'opération.",
|
||||
"wallet_recovery_rescan_elapsed": "En cours depuis %s",
|
||||
"wallet_recovery_rescan_size": "Le portefeuille reconstruit fait maintenant %s et continue de se remplir",
|
||||
"wallet_recovery_rescan_slow": "Cela prend plus de temps que d'habitude, ce qui est normal pour un grand portefeuille. Vous pouvez le laisser tourner en toute sécurité — vous pouvez suivre la progression sous Avancé ▸ Console.",
|
||||
"wallet_recovery_rescan_title": "Finalisation de la réparation de votre portefeuille",
|
||||
"wallet_recovery_restore_card": "Restaurer l'original",
|
||||
"wallet_recovery_restore_card_desc": "Remet en place votre plus grande sauvegarde intacte, telle quelle — seulement aussi complète que ce fichier l'était.",
|
||||
"wallet_recovery_restore_go": "Restaurer",
|
||||
"wallet_recovery_success_body": "L'application a chargé votre portefeuille réparé et réanalyse la blockchain pour totaliser votre solde — cela peut prendre quelques minutes. Elle lit chaque enregistrement possible, mais sur de rares fichiers endommagés, elle peut récupérer légèrement moins — ou parfois plus — d'adresses qu'avant. Une fois la réanalyse terminée, vérifiez que votre solde et votre historique semblent corrects.",
|
||||
"wallet_recovery_success_restore": "Votre plus grande sauvegarde est remise en place et se charge maintenant, et l'application réanalyse la blockchain. Elle est restaurée telle quelle, donc aussi complète que ce fichier l'était — vérifiez votre solde une fois la réanalyse terminée.",
|
||||
"wallet_recovery_try_other": "Essayer l'autre option",
|
||||
"wallet_recovery_whats_happened": "Que s'est-il passé ?",
|
||||
"wallet_recovery_working_label": "En cours",
|
||||
"wallet_restore_bad_backup": "Le fichier de sauvegarde du portefeuille semble illisible, il N'A DONC PAS été restauré — votre portefeuille actuel n'a pas changé. Restaurez plutôt à partir de votre propre sauvegarde.",
|
||||
"wallet_restore_busy": "Le nœud est occupé à redémarrer — réessayez dans un instant.",
|
||||
"wallet_restore_copy_failed": "Impossible d'installer le portefeuille de sauvegarde ; votre portefeuille actuel a été laissé en place.",
|
||||
"wallet_restore_move_failed": "Impossible de mettre de côté le portefeuille actuel — rien n'a été modifié.",
|
||||
"wallet_restore_no_backup": "Impossible de trouver un fichier wallet.<timestamp>.bak à restaurer. Rien n'a été modifié.",
|
||||
"wallet_restore_no_restart": "Votre portefeuille original a été restauré, mais le nœud n'a pas redémarré — démarrez-le depuis les Paramètres.",
|
||||
"wallet_restore_ok": "Portefeuille original restauré. Le nœud le charge maintenant.",
|
||||
"wallet_restore_started": "Restauration de votre fichier original — veuillez ne pas fermer cette fenêtre.",
|
||||
"wallet_restore_stop_failed": "Le nœud ne s'est pas arrêté à temps, donc rien n'a été modifié. Réessayez.",
|
||||
"wallet_size_consolidate": "Consolider les notes…",
|
||||
"wallet_size_warn": "Ce fichier de portefeuille est volumineux. Consolider vos notes peut freiner sa croissance.",
|
||||
"wallets_active": "Actif",
|
||||
"wallets_add": "Ajouter le dossier",
|
||||
"wallets_add_folder": "Analyser aussi un autre dossier à la recherche de fichiers de portefeuille :",
|
||||
@@ -1739,6 +2229,7 @@
|
||||
"wallets_open_inplace_tt": "Ouvre ce portefeuille à son emplacement — lié au répertoire de données (sans copie)",
|
||||
"wallets_remove_folder": "Ne plus analyser ce dossier",
|
||||
"wallets_reveal": "Afficher le dossier",
|
||||
"wallets_scan_folder": "Analyser un autre dossier pour des portefeuilles…",
|
||||
"wallets_scanned_folders": "Dossiers analysés :",
|
||||
"wallets_sort_addresses": "Adresses",
|
||||
"wallets_sort_asc": "Croissant (plus ancien / moins / plus petit d'abord)",
|
||||
@@ -1808,19 +2299,30 @@
|
||||
"wiz_welcome_sub": "Quelques choix rapides et votre nœud complet est prêt.",
|
||||
"wiz_welcome_title": "Bienvenue dans ObsidianDragon !",
|
||||
"wizard_daemon_start_failed": "Échec du démarrage du daemon — sera réessayé automatiquement",
|
||||
"xmrig_active_tt": "Le mineur actuellement installé (actif)",
|
||||
"xmrig_browse_releases": "Parcourir toutes les versions…",
|
||||
"xmrig_checking": "Recherche du dernier mineur…",
|
||||
"xmrig_current": "Actuel :",
|
||||
"xmrig_downgrade_note": "Installer une autre version du mineur remplace la version actuelle ; arrêtez d'abord le minage, l'effet prendra place au prochain démarrage.",
|
||||
"xmrig_download_install": "Télécharger et installer",
|
||||
"xmrig_downloading": "Téléchargement…",
|
||||
"xmrig_install_this": "Installer cette version",
|
||||
"xmrig_installed": "Installé :",
|
||||
"xmrig_installed_ok": "Mineur installé",
|
||||
"xmrig_installing": "Installation…",
|
||||
"xmrig_latest": "Dernière :",
|
||||
"xmrig_latest_badge": "la plus récente",
|
||||
"xmrig_latest_tt": "Version la plus récente disponible",
|
||||
"xmrig_loading_releases": "Chargement des versions…",
|
||||
"xmrig_no_notes": "Aucune note de version pour cette version.",
|
||||
"xmrig_none": "aucun",
|
||||
"xmrig_prerelease_tt": "Version préliminaire / de test",
|
||||
"xmrig_reinstall": "Réinstaller",
|
||||
"xmrig_released": "Publiée le :",
|
||||
"xmrig_releases": "versions de xmrig",
|
||||
"xmrig_status_current": "Cette version est installée",
|
||||
"xmrig_status_latest": "Dernière version",
|
||||
"xmrig_status_older": "Plus ancienne que votre version installée",
|
||||
"xmrig_stop_mining_first": "Arrêtez le minage avant de mettre à jour le mineur.",
|
||||
"xmrig_unavailable_body": "Aucune version du mineur n'est disponible pour cette plateforme.",
|
||||
"xmrig_unavailable_title": "Mises à jour du mineur indisponibles",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"about_license_text": "本ソフトウェアはGNU General Public License v3 (GPLv3)の下で公開されています。ライセンス条項に従い、自由に使用、変更、配布できます。",
|
||||
"about_peers_count": "%zu ピア",
|
||||
"about_release": "リリース",
|
||||
"about_source": "ソース",
|
||||
"about_title": "ObsidianDragonについて",
|
||||
"about_version": "バージョン:",
|
||||
"about_website": "ウェブサイト",
|
||||
@@ -60,7 +61,84 @@
|
||||
"amount_label": "金額:",
|
||||
"animate_avatars": "アバターをアニメーション",
|
||||
"appearance": "外観",
|
||||
"appx_back": "戻る",
|
||||
"appx_back_up_seed_phrase_title": "シードフレーズをバックアップ",
|
||||
"appx_birthday_block_height": "誕生日(ブロック高): %llu — これもバックアップしてください。",
|
||||
"appx_blockchain_data_deleted": "ブロックチェーンデータを削除しました(%d件)。ネットワークから再同期するため、デーモンを再起動しています。",
|
||||
"appx_blockchain_maintenance_in_progress": "ブロックチェーンのメンテナンス操作がすでに進行中です。",
|
||||
"appx_blockchain_rescan_complete": "ブロックチェーンの再スキャンが完了しました",
|
||||
"appx_bootstrap_complete_reconciling": "ブートストラップが完了しました。新しいチェーンデータとウォレットを照合しています。",
|
||||
"appx_cancel": "キャンセル",
|
||||
"appx_cleaning_up": "クリーンアップしています...",
|
||||
"appx_confirm_your_backup": "バックアップを確認",
|
||||
"appx_copied_clipboard_autoclears": "コピーしました — クリップボードは45秒後に自動的にクリアされます",
|
||||
"appx_copy": "コピー",
|
||||
"appx_could_not_start_restore": "復元を開始できませんでした",
|
||||
"appx_create_failed_prefix": "作成に失敗しました: ",
|
||||
"appx_creating_your_wallet": "ウォレットを作成しています…",
|
||||
"appx_daemon_error": "デーモンエラー",
|
||||
"appx_daemon_reinstall_in_progress": "デーモンの再インストールがすでに進行中です。",
|
||||
"appx_disconnecting": "切断しています...",
|
||||
"appx_done": "完了",
|
||||
"appx_dragonxd_output": "dragonxd の出力",
|
||||
"appx_encrypting_wallet": "ウォレットを暗号化しています...",
|
||||
"appx_fullnode_lifecycle_unavailable_lite": "フルノードのライフサイクル操作はライトビルドでは利用できません",
|
||||
"appx_installing_bundled_daemon": "同梱デーモンをインストールしています。ノードが停止、更新、再起動されます...",
|
||||
"appx_invalid_payment_uri_prefix": "無効な支払いURI: ",
|
||||
"appx_ive_written_it_down": "書き留めました",
|
||||
"appx_keep_node_running_and_quit": "ノードを起動したまま終了",
|
||||
"appx_last_block_n": "最新ブロック: %d",
|
||||
"appx_last_used_wallet_not_found_prefix": "前回使用したウォレットファイル(",
|
||||
"appx_last_used_wallet_not_found_suffix": ")が見つかりませんでした。代わりにデフォルトのウォレットを開きました。移動した場合は、元に戻してからウォレット一覧で切り替えてください。",
|
||||
"appx_low_spec_mode_disabled": "低スペックモードを無効にしました",
|
||||
"appx_low_spec_mode_enabled": "低スペックモードを有効にしました",
|
||||
"appx_miner_stopped_prefix": "マイナーが停止しました: ",
|
||||
"appx_miner_stopped_unexpectedly": "マイナーが予期せず停止しました。",
|
||||
"appx_n_min_n_sec": "%d分 %d秒",
|
||||
"appx_n_seconds": "%d秒",
|
||||
"appx_no_bundled_daemon_to_install": "このビルドにはインストールできる同梱デーモンがありません",
|
||||
"appx_no_embedded_daemon_to_install": "このビルドにはインストールできる組み込みデーモンがありません",
|
||||
"appx_node_busy_restarting": "ノードが再起動中でビジー状態です。少し待ってから再試行してください。",
|
||||
"appx_node_rebuilding_witness_cache": "ノードがウィットネスキャッシュを再構築しています",
|
||||
"appx_not_next_word": " — 次の単語ではありません",
|
||||
"appx_payment_request_loaded": "支払いリクエストを読み込みました",
|
||||
"appx_pool_miner_connected_and_hashing": "プールマイナーが接続し、ハッシュ計算中です。",
|
||||
"appx_progress_n_of_n": "進捗: %d / %d",
|
||||
"appx_rebuilding_sapling_note_witnesses": "Saplingノートウィットネスを再構築しています…",
|
||||
"appx_rebuilding_witness_cache_blocks_left": "ウィットネスキャッシュを再構築中 %.0f%% — 残り%dブロック",
|
||||
"appx_rebuilding_witness_cache_pct": "ウィットネスキャッシュを再構築中 %.0f%%",
|
||||
"appx_recovery_phrase_word_count": "リカバリーフレーズは24単語である必要があります。現在は%d単語です。",
|
||||
"appx_restarting_daemon_rescan_flag": "-rescanフラグ付きでデーモンを再起動しています...",
|
||||
"appx_restarting_daemon_zapwallettxes": "-zapwallettxes=2(ウォレット修復)付きでデーモンを再起動しています...",
|
||||
"appx_restoring_your_wallet": "ウォレットを復元しています…",
|
||||
"appx_seed_backup_warning": "この24個の単語は、ウォレットを復元する唯一の手段です。順番どおりに書き留め、オフラインで保管し、決して他人に教えないでください。紛失すると、資金は永久に失われます。",
|
||||
"appx_seed_not_backed_up_warning": "シードをバックアップしていません。資金を失う可能性があります。それでもスキップしますか?",
|
||||
"appx_sending_stop_command_to_daemon": "デーモンに停止コマンドを送信しています...",
|
||||
"appx_setting_initial_sapling_witnesses": "初期Saplingウィットネスを設定中 %.0f%%",
|
||||
"appx_shutdown_complete": "シャットダウンが完了しました",
|
||||
"appx_simple_background_disabled": "シンプル背景を無効にしました",
|
||||
"appx_simple_background_enabled": "シンプル背景を有効にしました",
|
||||
"appx_skip": "スキップ",
|
||||
"appx_skip_anyway": "それでもスキップ",
|
||||
"appx_still_status_prefix": "まだ「",
|
||||
"appx_still_status_suffix": "」です — 今強制終了するとチェーンデータが破損する可能性があります。",
|
||||
"appx_stop_anyway_and_quit": "それでも停止して終了",
|
||||
"appx_stopping_daemon_deleting_blockchain": "デーモンを停止し、ブロックチェーンデータを削除しています...",
|
||||
"appx_stopping_node_discards_rebuild": "今ノードを停止すると、進行中の再構築が破棄され、次回ウォレットを開いたときに再度実行されます(数分かかります)。代わりにノードを起動したままにすることもできます。",
|
||||
"appx_stopping_pool_miner": "プールマイナーを停止しています...",
|
||||
"appx_syncing_pct_block_n_of_n": "同期中 %.1f%% — ブロック %d / %d",
|
||||
"appx_tap_words_in_order": "保存したことを確認するため、単語を正しい順番でタップしてください。",
|
||||
"appx_theme_effects_disabled": "テーマ効果を無効にしました",
|
||||
"appx_theme_effects_enabled": "テーマ効果を有効にしました",
|
||||
"appx_theme_prefix": "テーマ: ",
|
||||
"appx_use_settings_restart_daemon_hint": "設定 > デーモンを再起動 から再試行してください",
|
||||
"appx_waiting_for_daemon_to_encrypt_wallet": "デーモンによるウォレットの暗号化を待っています...",
|
||||
"appx_wallet_created_and_backed_up": "ウォレットを作成し、バックアップしました。",
|
||||
"appx_wallet_open_failed_prefix": "ウォレットを開けませんでした: ",
|
||||
"auto_shield": "マイニング自動シールド",
|
||||
"autoshield_by_node": "あなたのノードは、マイニングしたコインベースを自動的にシールドします。",
|
||||
"autoshield_off_backup_seed": "マイニングしたコインベースはまだ自動シールドされていません。ノードが安全に有効化できるよう、シードフレーズをバックアップしてください(設定 → バックアップとデータ)。",
|
||||
"autoshield_off_generic": "現在、あなたのノードはマイニングしたコインベースを自動シールドしていません。",
|
||||
"av_intro": "マイニングソフトウェアは、望ましくない可能性があるものとしてフラグが立てられることがよくあります。プールマイニングを有効にするには、次の手順に従ってください。",
|
||||
"av_open_security": "Windows セキュリティを開く",
|
||||
"av_step1": "ステップ1: 除外の追加",
|
||||
@@ -100,6 +178,17 @@
|
||||
"balance_shielded_fmt": "シールド: %.8f",
|
||||
"balance_syncing_pct": "同期中 %.1f%%",
|
||||
"balance_transparent_fmt": "透明: %.8f",
|
||||
"baltab_market": "市場",
|
||||
"baltab_market_price_4dp": "市場価格: $%.4f",
|
||||
"baltab_market_price_8dp": "市場価格: $%.8f",
|
||||
"baltab_pct_change_24h": "%s%.1f%% 24h",
|
||||
"baltab_pct_of_total_zaddr": "全体の%.0f%% · %d Z-addr",
|
||||
"baltab_shielded": "シールド",
|
||||
"baltab_shielded_amount": "シールド %.8f",
|
||||
"baltab_t_addresses_count": "%d 個のT-address",
|
||||
"baltab_total_balance": "合計残高",
|
||||
"baltab_transparent": "透明",
|
||||
"baltab_transparent_amount": "透明 %.8f",
|
||||
"ban": "ブロック",
|
||||
"banned_peers": "ブロック済みピア",
|
||||
"block": "ブロック",
|
||||
@@ -107,6 +196,12 @@
|
||||
"block_click_copy": "クリックしてコピー",
|
||||
"block_click_next": "クリックして次のブロックを表示",
|
||||
"block_click_prev": "クリックして前のブロックを表示",
|
||||
"block_db_reindex_body": "これは通常、デーモンの更新でディスク上のフォーマットが変わった後、またはブロックインデックスが破損している場合に発生します。ウォレットとコインは安全です — ノードがチェーンを読み込めないだけなので、残高は0と表示されます。\n\n再構築は既存のブロックファイルを再読み込みするもので、時間がかかることがあります(ウォレットの再スキャンも行います)。ダウンロードは発生しません。",
|
||||
"block_db_reindex_confirm": "ブロックデータベースを再構築",
|
||||
"block_db_reindex_notify": "ノードがブロックデータベースを読み込めません(デーモンの更新後によく発生します)。残高を復元するには再構築してください — プロンプトを確認するか、設定 › ノードへ。",
|
||||
"block_db_reindex_started": "保存済みのブロックからブロックデータベースを再構築しています — 時間がかかる場合があります。",
|
||||
"block_db_reindex_title": "ブロックデータベースを再構築しますか?",
|
||||
"block_db_reindex_warn": "ノードがブロックデータベースを読み込めません。",
|
||||
"block_explorer": "ブロックエクスプローラー",
|
||||
"block_get_info": "ブロック情報を取得",
|
||||
"block_hash": "ブロックハッシュ:",
|
||||
@@ -154,6 +249,10 @@
|
||||
"chat_accent_theme": "テーマ",
|
||||
"chat_add_contact": "連絡先に追加",
|
||||
"chat_awaiting_key": "返信待ち",
|
||||
"chat_blocked_desc": "ブロックされた会話は削除され、ブロック解除するまでメッセージ(過去・新規とも)は反映されません。ブロックを解除すると、チェーンから会話が再インポートされます。",
|
||||
"chat_blocked_manage": "ブロック中",
|
||||
"chat_blocked_title": "ブロックされた会話",
|
||||
"chat_blocked_toast": "会話を削除してブロックしました",
|
||||
"chat_bubble_minimal": "ミニマル",
|
||||
"chat_bubble_rounded": "角丸",
|
||||
"chat_bubble_square": "角ばった",
|
||||
@@ -166,6 +265,16 @@
|
||||
"chat_contact_added": "連絡先を追加しました — 連絡先で名前を変更できます",
|
||||
"chat_contact_request": "連絡リクエスト",
|
||||
"chat_copy_address_tip": "クリックしてアドレスをコピー",
|
||||
"chat_delete": "会話を削除",
|
||||
"chat_delete_block": "削除してブロック",
|
||||
"chat_delete_body_prefix": "次の相手との会話のローカルコピーを削除しますか:",
|
||||
"chat_delete_body_suffix": "?",
|
||||
"chat_delete_confirm": "削除",
|
||||
"chat_delete_failed": "会話を削除できませんでした — 変更はありません。",
|
||||
"chat_delete_local_note": "これはこの端末のみに影響します — メッセージはブロックチェーン上に残り、相手も自分のコピーを保持し続けます。",
|
||||
"chat_delete_revive_note": "「削除」はこの端末上の履歴を消去します。相手から再度メッセージが届くと、会話は復活します。",
|
||||
"chat_delete_title": "会話を削除しますか?",
|
||||
"chat_deleted_toast": "会話を削除しました",
|
||||
"chat_density_comfortable": "ゆったり",
|
||||
"chat_density_compact": "コンパクト",
|
||||
"chat_emoji_color": "カラー",
|
||||
@@ -189,6 +298,7 @@
|
||||
"chat_new_button": "新しいチャット",
|
||||
"chat_new_message": "メッセージ",
|
||||
"chat_new_message_toast": "新しい暗号化チャットメッセージ",
|
||||
"chat_new_needs_zaddr": "チャットにはシールド(z)アドレスが必要です — 透過(t)アドレスは暗号化メッセージを受信できません。",
|
||||
"chat_new_send": "リクエストを送信",
|
||||
"chat_new_title": "新しいチャット",
|
||||
"chat_new_zaddr": "宛先Zアドレス",
|
||||
@@ -234,6 +344,8 @@
|
||||
"chat_ts_24h": "24時間",
|
||||
"chat_ts_global": "全体設定に従う",
|
||||
"chat_ts_global_short": "全体",
|
||||
"chat_unblock": "ブロック解除",
|
||||
"chat_unblocked_toast": "会話のブロックを解除しました",
|
||||
"chat_unhide": "再表示",
|
||||
"chat_unmute": "ミュート解除",
|
||||
"chat_verify_key": "識別鍵 — 照合して確認",
|
||||
@@ -286,6 +398,7 @@
|
||||
"console": "コンソール",
|
||||
"console_accents": "カラーアクセント",
|
||||
"console_app": "アプリ",
|
||||
"console_auto_focus": "開いたときに入力欄へフォーカス",
|
||||
"console_auto_scroll": "自動スクロール",
|
||||
"console_available_commands": "利用可能なコマンド:",
|
||||
"console_backend_reference": "バックエンドコマンドリファレンス",
|
||||
@@ -380,11 +493,15 @@
|
||||
"console_tab_completion": "Tabで補完",
|
||||
"console_text_colors": "テキスト色",
|
||||
"console_toggle_accents": "行のカラーアクセントを切り替え",
|
||||
"console_toggle_auto_focus": "コンソールタブを開いたときにコマンド入力欄にカーソルを置きます",
|
||||
"console_toggle_text_color": "行のテキスト色を切り替え",
|
||||
"console_type_help": "'help'と入力して利用可能なコマンドを表示",
|
||||
"console_welcome": "ObsidianDragonコンソールへようこそ",
|
||||
"console_zoom_in": "拡大",
|
||||
"console_zoom_out": "縮小",
|
||||
"consolidate_desc": "多数の小さな入力を1つのシールドノートにまとめます。ノート数が減るとウォレットファイルが小さくなり、プライバシーも向上します。",
|
||||
"consolidate_funds_btn": "統合",
|
||||
"consolidate_title": "資金を統合",
|
||||
"contact_avatar": "アバター",
|
||||
"contact_avatar_bad_image": "その画像を読み込めませんでした。",
|
||||
"contact_avatar_badge": "バッジ",
|
||||
@@ -414,6 +531,7 @@
|
||||
"contacts_shape_square": "四角",
|
||||
"contacts_shape_tab": "左タブ",
|
||||
"copied": "コピーしました!",
|
||||
"copied_to_clipboard": "クリップボードにコピーしました",
|
||||
"copy": "コピー",
|
||||
"copy_address": "完全なアドレスをコピー",
|
||||
"copy_error": "エラーをコピー",
|
||||
@@ -429,9 +547,13 @@
|
||||
"daemon_maintenance_label": "メンテナンス",
|
||||
"daemon_none_bundled": "このビルドにはなし",
|
||||
"daemon_not_installed": "未インストール",
|
||||
"daemon_status_diff": "バージョンが異なります",
|
||||
"daemon_status_differ": "インストール済みのバイナリはバンドル版と異なります。",
|
||||
"daemon_status_match": "インストール済みのバイナリはバンドル版と一致しています。",
|
||||
"daemon_status_missing": "デーモンがインストールされていません — バンドル版をインストールしてください。",
|
||||
"daemon_status_none": "未インストール",
|
||||
"daemon_status_ok": "最新です",
|
||||
"daemon_update_active_tt": "現在インストールされている(有効な)ノード",
|
||||
"daemon_update_available": "新しいノードバージョンが利用可能です",
|
||||
"daemon_update_body": "このウォレットビルドには、現在インストールされているものよりも新しいDragonXノードがバンドルされています。更新すると、インストール済みのdragonxd(およびdragonx-cli/dragonx-tx)が置き換えられ、その後ノードを停止して再起動し、新しいバージョンが有効になります。新しいノードには、古いものにない機能(例:シードフレーズのサポート)が追加されている場合があるため、更新を推奨します。",
|
||||
"daemon_update_browse": "すべてのリリースを表示…",
|
||||
@@ -441,19 +563,28 @@
|
||||
"daemon_update_download_install": "ダウンロードしてインストール",
|
||||
"daemon_update_downloading": "ダウンロード中…",
|
||||
"daemon_update_failed": "更新に失敗しました",
|
||||
"daemon_update_install_this": "このバージョンをインストール",
|
||||
"daemon_update_installed": "インストール済み:",
|
||||
"daemon_update_installed_ok": "ノードをインストールしました",
|
||||
"daemon_update_installing": "インストール中…",
|
||||
"daemon_update_keep": "現在のまま使用",
|
||||
"daemon_update_later": "後で",
|
||||
"daemon_update_latest": "最新:",
|
||||
"daemon_update_latest_badge": "最新",
|
||||
"daemon_update_latest_tt": "利用可能な最新バージョン",
|
||||
"daemon_update_loading": "リリースを読み込み中…",
|
||||
"daemon_update_no_notes": "このバージョンのリリースノートはありません。",
|
||||
"daemon_update_now": "今すぐ更新",
|
||||
"daemon_update_prerelease_tt": "プレリリース/テストビルド",
|
||||
"daemon_update_prompt_title": "ノードデーモンを更新しますか?",
|
||||
"daemon_update_reinstall": "再インストール",
|
||||
"daemon_update_released": "リリース日:",
|
||||
"daemon_update_restart_note": "新しいバージョンを実行するにはデーモンを再起動してください。",
|
||||
"daemon_update_restart_now": "今すぐデーモンを再起動",
|
||||
"daemon_update_safe": "ウォレット、鍵、ブロックチェーンデータには影響しません — 置き換えられるのはデーモンのプログラムファイルのみです。意図的にカスタムノードを実行している場合は、「現在のまま使用」を選択してください。",
|
||||
"daemon_update_status_current": "このバージョンはインストール済みです",
|
||||
"daemon_update_status_latest": "最新バージョン",
|
||||
"daemon_update_status_older": "インストール済みバージョンより古いです",
|
||||
"daemon_update_title": "ノードデーモンを更新しますか?",
|
||||
"daemon_update_unavailable_body": "このプラットフォーム向けのノードビルドはありません。",
|
||||
"daemon_update_unavailable_title": "ノードの更新は利用できません",
|
||||
@@ -469,6 +600,13 @@
|
||||
"data_stale_tooltip": "残高が最新でない可能性があります。ウォレットは最近更新を受信していません。ノード接続を確認してください。",
|
||||
"date": "日付",
|
||||
"date_label": "日付:",
|
||||
"debug_gate_confirm": "デバッグオプションを表示",
|
||||
"debug_gate_incorrect": "PINまたはパスフレーズが正しくありません。",
|
||||
"debug_gate_pass_prompt": "続行するにはウォレットのパスフレーズを入力してください:",
|
||||
"debug_gate_pin_prompt": "続行するにはロック解除PINを入力してください:",
|
||||
"debug_gate_title": "デバッグオプションを表示しますか?",
|
||||
"debug_gate_verifying": "確認中…",
|
||||
"debug_gate_warning": "これらは高度な診断・ノード調整用オプションです。機密情報が露出したり、ノードの動作が変わったりする可能性があります。内容を理解している場合のみ続行してください。",
|
||||
"debug_logging": "デバッグログ",
|
||||
"decrypt_desc": "ウォレットがエクスポートされ、デーモンが新しい暗号化されていないウォレットで再起動し、すべての鍵が再インポートされます。ウォレットのサイズによっては数分かかる場合があります。",
|
||||
"decrypt_error_title": "復号に失敗しました",
|
||||
@@ -491,6 +629,7 @@
|
||||
"disconnected": "切断済み",
|
||||
"dismiss": "閉じる",
|
||||
"display": "表示",
|
||||
"done": "完了",
|
||||
"download": "ダウンロード",
|
||||
"download_bootstrap": "ブートストラップをダウンロード",
|
||||
"dragonx_green": "DragonX(グリーン)",
|
||||
@@ -544,6 +683,7 @@
|
||||
"explorer_tx_outputs": "出力",
|
||||
"explorer_tx_size": "サイズ",
|
||||
"explorer_txid": "TxID:",
|
||||
"explorer_urls_hdr": "URL",
|
||||
"export": "エクスポート",
|
||||
"export_csv": "CSVエクスポート",
|
||||
"export_keys_btn": "鍵をエクスポート",
|
||||
@@ -571,6 +711,151 @@
|
||||
"export_viewing_key": "閲覧鍵をエクスポート",
|
||||
"failed_create_shielded": "シールドアドレスの作成に失敗しました",
|
||||
"failed_create_transparent": "透明アドレスの作成に失敗しました",
|
||||
"faq": "FAQ",
|
||||
"faq_d_mgmt_1_a": "組み込みノードはウォレットと同時に自動的に起動し、適切なタイミングで停止するため、通常は手動で管理する必要はありません。高度な操作(デーモンの再起動を含む)は設定 > ノードとセキュリティにあります。",
|
||||
"faq_d_mgmt_1_q": "ノードの起動・停止はどうすればいいですか?",
|
||||
"faq_d_mgmt_2_a": "自分で起動したノードにウォレットを接続している場合、このオプションはウォレットを閉じたときにそのノードも停止するかどうかを決めます。ウォレットを閉じた後もノードを動かし続けたい場合はオフのままにしてください。",
|
||||
"faq_d_mgmt_2_q": "「終了時に外部デーモンを閉じる」とは何をする機能ですか?",
|
||||
"faq_d_mgmt_3_a": "ノードとウォレットのデータは、ユーザープロファイル配下のDragonXデータディレクトリに保存されます(例:WindowsではAppData内、Linux/macOSではホームフォルダ内)。ブロックチェーンがその大部分を占め、増え続けるので十分な空き容量を確保してください。",
|
||||
"faq_d_mgmt_3_q": "ブロックチェーンとウォレットのデータはどこに保存されますか?",
|
||||
"faq_d_mgmt_title": "ノード管理",
|
||||
"faq_d_mine_1_a": "「マイニング」タブでは、CPUを使ってマイニングできます。DragonXは、一般的なプロセッサ向けに設計されたRandomXアルゴリズムを使用します。スレッド数を設定して開始すると、報酬がウォレットに蓄積されます。",
|
||||
"faq_d_mine_1_q": "DragonXをマイニングするには?",
|
||||
"faq_d_mine_2_a": "ソロマイニングは報酬を直接受け取れますが、ブロックを見つけたときにしか支払われないため、十分なハッシュレートがない限り頻度は低くなります。プールマイニングは他の参加者と作業を分担し、小さいながらも安定した支払いを受けられます。マイニングタブは両方に対応しています。",
|
||||
"faq_d_mine_2_q": "ソロマイニングとプールマイニング、どちらを選べばいい?",
|
||||
"faq_d_mine_3_a": "マイニングタブのプールセクションにある「マイナーを更新…」ボタンから、最新の最適化されたDragonXマイナービルドをダウンロード・検証・インストールできます。ノードのアップデータと同様に、ダウンロードごとにチェックサムと署名を検証してからインストールします。",
|
||||
"faq_d_mine_3_q": "マイナーを更新するには?",
|
||||
"faq_d_mine_4_a": "現行のノードは組み込みのstratumサーバーを実行でき、他のマイナーがあなたのマシンに接続できるようになります。Settings > Node & Security に切り替えスイッチがあります。デフォルトではローカルのみで待ち受けており、他のコンピュータに公開するには明示的な設定が必要です。",
|
||||
"faq_d_mine_4_q": "マイニングプールをホストできますか?",
|
||||
"faq_d_mine_title": "マイニングとプール",
|
||||
"faq_d_net_1_a": "起動直後はノードがピアを見つけるまでに少し時間がかかるため、「0ピア」と一時的に表示されるのは正常です。長く続く場合は、インターネット接続とファイアウォールがノードをブロックしていないか確認してください。ノードは組み込みのシードを通じて自動的にピアを見つけます。",
|
||||
"faq_d_net_1_q": "ウォレットにピアや接続が表示されません。どうすればいい?",
|
||||
"faq_d_net_2_a": "はい — DragonXノード同士はTLSで通信するため、ピア接続は暗号化されています。ウォレットも自身のノードとは安全なローカルチャネルで通信します。",
|
||||
"faq_d_net_2_q": "ネットワークへの接続は暗号化されていますか?",
|
||||
"faq_d_net_title": "ピアとネットワーク",
|
||||
"faq_d_node_1_a": "デーモン(dragonxd)は、DragonXのフルノードです。ブロックチェーン全体をダウンロード・検証し、取引をネットワークに中継し、ウォレットの問い合わせに応答します。自分のノードを実行することで、他者のサーバーを信頼する必要がなくなります。",
|
||||
"faq_d_node_1_q": "デーモン(dragonxd)とは何ですか?",
|
||||
"faq_d_node_2_a": "デフォルトでは、ウォレットは同梱された独自のノード(組み込み)を起動・管理するため、あなたが何かする必要はありません。すでに自分でdragonxdを実行している場合は、ウォレットをその外部ノードに接続することもできます。ウォレットはどちらの状況かを検出し、それに応じて動作します。",
|
||||
"faq_d_node_2_q": "組み込みノードと外部ノードの違いは?",
|
||||
"faq_d_node_3_a": "フルノードはブロックチェーンのすべてのルールを独自に検証するため、あなたのウォレットは第三者ではなく数学を信頼します。ネットワーク自体の強化にもつながります。トレードオフはディスク容量と初回同期です。",
|
||||
"faq_d_node_3_q": "そもそもなぜフルノードを実行するのですか?",
|
||||
"faq_d_node_title": "フルノードについて",
|
||||
"faq_d_perf_1_a": "ブロックチェーンが最も容量を占め、時間とともに増加するため、数ギガバイトの空き容量を確保しておいてください。ノードは高速化のためにデータベースキャッシュを使用しており、同梱のデフォルト値は一般的なマシン向けに調整されています。上級者は設定ファイル(DRAGONX.conf)のdbcacheを編集して、ノードのデータベースキャッシュを調整できます。",
|
||||
"faq_d_perf_1_q": "ノードはどれくらいディスクを使いますか?メモリは調整できますか?",
|
||||
"faq_d_perf_2_a": "セットアップウィザードで提供されるブートストラップダウンロードを有効にすると、すべてのブロックをゼロから検証する代わりに、最近の検証済みチェーンのコピーを取得できます。高速なディスク(SSD)も大いに役立ちます。",
|
||||
"faq_d_perf_2_q": "初回同期を速くするには?",
|
||||
"faq_d_perf_title": "ストレージとパフォーマンス",
|
||||
"faq_d_sync_1_a": "起動時、ノードはウォレットに応答できるようになる前にブロックを読み込んで検証します。「ブロックを処理中/現在のチェーン状態を構築するためにブロックを適用中」という画面はそのウォームアップです。これは正常な動作で、ノードが処理を終えて高さを報告すると、ウォレットは使用可能になります。",
|
||||
"faq_d_sync_1_q": "起動時の「ブロックを処理中」とはどういう意味ですか?",
|
||||
"faq_d_sync_2_a": "シールドされた資金については、ノードはプライベートに送金できるよう暗号学的な「ウィットネス」を保持しています。再起動やアップデートの後、最近のブロックを再スキャンしてこのキャッシュを再構築することがあります。取引の多いウォレットでは、これに数分かかり、追加のメモリを使用することがあります。\n\nこれは停止しているわけではないので、完了するまでお待ちください。途中で強制終了すると、次回は再構築を最初からやり直すことになります。",
|
||||
"faq_d_sync_2_q": "「ウィットネスキャッシュを再構築中」とは何ですか?",
|
||||
"faq_d_sync_3_a": "チェーンの先端付近では、ウォレットをスキャンする処理(残高やノートの確認)がノードと同じ内部ロックを奪い合うため、大きなウォレットでは最後のブロックの接続が遅くなることがあります。最近のバージョンではこのスキャンを制限し、ノードが追いつけるようにしています。負荷の軽いタブにとどまる(または単に待つ)ことで、より速く完了します。",
|
||||
"faq_d_sync_3_q": "ほぼ追いついているのに同期が遅いのはなぜですか?",
|
||||
"faq_d_sync_4_a": "初回同期ではチェーン全体をダウンロード・検証するため、回線とディスクの性能によっては時間がかかることがあります。セットアップウィザードでブートストラップダウンロードを有効にすると、最近の検証済みチェーンデータのコピーを取得できるため、大幅に高速化できます。",
|
||||
"faq_d_sync_4_q": "初回同期にはどれくらい時間がかかりますか?",
|
||||
"faq_d_sync_title": "同期とブロックチェーン",
|
||||
"faq_d_trbl_1_a": "通常は問題ありません。大きなウォレットでは、ノードがシールドウィットネスの再構築やブロックの適用に数分かかることがあり、その間は一時停止しているように見え、応答が遅くなることがあります。しばらくお待ちください — 動作中であれば、デバッグログ(Settings)に着実な進捗が表示されます。\n\nこの段階での強制終了は避けてください。次回起動時に作業が最初からやり直しになります。",
|
||||
"faq_d_trbl_1_q": "ノードが「最良チェーンを有効化中」で止まっているようです。フリーズしていますか?",
|
||||
"faq_d_trbl_2_a": "ウォレットファイルが復旧されたものの、鍵データの一部が欠けている場合、ノードは新しいアドレスの生成やシールドができない限定的な「デグレード」モードで開くことがあります。最も安全な対処法は、シードリカバリーフレーズから新しいウォレットに復元することです。",
|
||||
"faq_d_trbl_2_q": "「デグレードモード」とは何ですか?",
|
||||
"faq_d_trbl_3_a": "再スキャンは、ウォレットにチェーンを再読み込みさせて取引を再検出するもので(鍵をインポートした後などに便利です)。再インデックスは、ノードのブロックチェーンデータベースを再構築するもので、そのデータベースが破損している、またはバージョン変更後に非互換になっている場合にのみ必要です。どちらも時間がかかることがあります。Settings > Node & Security から開始してください。",
|
||||
"faq_d_trbl_3_q": "再インデックスや再スキャンはいつ行うべきですか?",
|
||||
"faq_d_trbl_4_a": "ウィットネスの再構築や初回同期のような重い処理の間、メモリ使用量が急増し、完了すると元に戻ります。アイドル時にメモリ使用量が高いままなのは通常ではありません — ウォレットを再起動すると解消します。低メモリのマシンでは、他のメモリを大量に使うアプリと競合していないか確認してください。",
|
||||
"faq_d_trbl_4_q": "ノードのメモリ使用量が多いのですが、正常ですか?",
|
||||
"faq_d_trbl_title": "トラブルシューティング",
|
||||
"faq_d_upd_1_a": "Settings > Node & Security > Daemon binary を開き、「アップデートを確認…」を使用してください。ウォレットが最新の検証済みノードビルドをダウンロードしてインストールし、新しいバージョンは次回デーモン起動時に有効になります。",
|
||||
"faq_d_upd_1_q": "ノードを更新するには?",
|
||||
"faq_d_upd_2_a": "はい — アップデートダイアログにはすべてのリリースが一覧表示されるため、特定のバージョンや古いビルドを選べます。ダウングレードには注意してください。古いノードは新しいノードが書き込んだブロックチェーンデータを受け付けない場合があり、再インデックスが必要になることがあります。",
|
||||
"faq_d_upd_2_q": "特定のノードバージョンをインストールできますか?",
|
||||
"faq_d_upd_3_a": "ダウンロードされたノードアーカイブは、インストール前に必ずチェックサムと暗号署名で検証されます。検証に失敗した場合、ウォレットはアップデートを拒否します。",
|
||||
"faq_d_upd_3_q": "このアップデートは安全ですか?",
|
||||
"faq_d_upd_title": "ノードの更新",
|
||||
"faq_group_daemon": "デーモン",
|
||||
"faq_group_wallet": "ウォレット",
|
||||
"faq_intro": "ウォレットとDragonXノードに関する回答です。検索するか、下のトピックから参照してください。",
|
||||
"faq_intro_lite": "Liteウォレットに関する回答です。検索するか、下のトピックから参照してください。",
|
||||
"faq_l_gs_1_a": "ObsidianDragonLiteは軽量版のDragonXウォレットです。フルノードを実行する代わりに、DragonX Liteサーバーに接続するため、起動が速く、ブロックチェーン全体をダウンロードする必要がありません。\n\n鍵、残高、送金の管理はフルウォレットと同じように行えます。サーバー接続の仕組みとプライバシー上のトレードオフについては、下記の「Liteウォレット」セクションをご覧ください。",
|
||||
"faq_l_gs_1_q": "ObsidianDragonLiteとは何ですか?",
|
||||
"faq_l_lite_1_a": "Liteウォレットは、自身でフルノードを実行する代わりにDragonX Liteサーバーと通信します。ブロックチェーン全体を自分でダウンロード・検証しないため、起動が速く、ディスクと帯域の消費も軽くなります。秘密鍵は常にあなたのデバイス内にとどまり、サーバーに送信されることはありません。",
|
||||
"faq_l_lite_1_q": "Liteウォレットはフルウォレットとどう違いますか?",
|
||||
"faq_l_lite_2_a": "Settingsを開いてサーバーを選択してください。サーバーはいつでも切り替えられ、ウォレットは新しいサーバーから再同期します。サーバーの応答が遅い、または到達できない場合は、別のサーバーを試してください。",
|
||||
"faq_l_lite_2_q": "サーバーの選び方や変更方法は?",
|
||||
"faq_l_lite_3_a": "あなたの鍵がデバイスの外に出ることはなく、シールドされた金額は暗号化されたままです。ただし、Liteサーバーはあなたのウォレットがどのアドレスについて問い合わせているかとあなたのIPアドレスを把握できるため、あなたのアドレス同士を関連付けられる可能性があります — 利便性とプライバシーのトレードオフです。最もプライバシーを重視する場合は、チェーンを自身で検証し、アドレスをサーバーに明かさないフルノードウォレットをご利用ください。",
|
||||
"faq_l_lite_3_q": "Liteウォレットはフルノードと同じくらいプライベートですか?",
|
||||
"faq_l_lite_title": "Liteウォレット",
|
||||
"faq_l_sec_1_a": "設定 > ウォレット を開き、セキュリティのセクションでパスフレーズを設定してください。暗号化はディスク上の秘密鍵を保護するので、ファイルにアクセスできる人がいてもコインを送金することはできません。\n\n強力なパスフレーズを選び、絶対に紛失しないでください。パスフレーズなしでは暗号化されたウォレットを復元する方法はありません。",
|
||||
"faq_no_results": "該当する結果がありません。別のキーワードでお試しください。",
|
||||
"faq_open_tooltip": "ヘルプ&FAQ",
|
||||
"faq_search_hint": "FAQを検索…",
|
||||
"faq_title": "ヘルプ&FAQ",
|
||||
"faq_w_addr_1_a": "透明アドレス(RまたはtまたはR始まり)はほとんどのコインと同様に動作します。金額とアドレスはブロックチェーン上で公開されます。\n\nシールドアドレス(zアドレス、zsで始まる)はゼロ知識暗号を使用し、金額・送信者・受信者はチェーン上で暗号化されます。シールドアドレスに保有された残高は非公開です。",
|
||||
"faq_w_addr_1_q": "透明アドレスとシールドアドレスの違いは何ですか?",
|
||||
"faq_w_addr_2_a": "できる限りシールド(z)アドレスを使用してください。残高と支払い履歴を非公開に保てます。DragonXはプライバシーコインであり、受け取る資金はデフォルトでシールドされます。\n\n透明アドレスは、やり取りするサービスがシールドアドレスに対応していない場合のみ使用してください。",
|
||||
"faq_w_addr_2_q": "どちらのアドレスタイプを使うべきですか?",
|
||||
"faq_w_addr_3_a": "シールドアドレスは再利用しても問題なく、履歴が漏れることもありません。透明アドレスは支払いごとに新しいアドレスを使うとプライバシーが向上します。新しいアドレスは受信タブから作成できます。",
|
||||
"faq_w_addr_3_q": "アドレスは使い回すべきですか、それとも新しく作るべきですか?",
|
||||
"faq_w_addr_4_a": "シールド化とは、コインを透明アドレスからシールドアドレスへ移し、非公開にすることです。新しくマイニングされたコインは透明な状態で届き、自動的にシールドされます。\n\n手動でシールドすることもできます。シールドは特別な種類のトランザクションにすぎないため、完了までに通常の確認時間がかかります。",
|
||||
"faq_w_addr_4_q": "「シールド化」とは何ですか?",
|
||||
"faq_w_addr_title": "アドレスとプライバシー",
|
||||
"faq_w_bal_1_a": "ウォレットがまだ同期中の場合、残高は不完全で0と表示されることがあります。まだすべてのトランザクションをスキャンしていないためです。同期が完了すると反映されます。\n\n画面下部のステータスバーを確認してください。接続状態とブロック高が表示されます。同期が完了すれば残高は正確になります。",
|
||||
"faq_w_bal_1_q": "残高が0のまま、または更新されないのはなぜですか?",
|
||||
"faq_w_bal_2_a": "あなたのトランザクションを含むブロックの上に新しいブロックがマイニングされるたびに、確認数が1つ増えます。確認数が多いほど、支払いはより確実に確定します。受け取った資金は最初の確認後に送金可能になります。",
|
||||
"faq_w_bal_2_q": "確認数とは何ですか?",
|
||||
"faq_w_bal_3_a": "合計残高には、まだ確認中の資金や一時的にロックされている資金(例えばシールド化の途中のコイン)も含まれます。送金可能残高は今すぐ送金できる金額です。トランザクションが確認されるにつれて両者は一致していきます。",
|
||||
"faq_w_bal_3_q": "合計残高と送金可能残高の違いは何ですか?",
|
||||
"faq_w_bal_title": "残高と同期",
|
||||
"faq_w_chat_1_a": "チャットはウォレットに組み込まれた、暗号化されたオンチェーンのメッセンジャー機能です。メッセージはシールドされたメモとして送信されるため、あなたと相手だけが読むことができます。",
|
||||
"faq_w_chat_1_q": "チャット機能とは何ですか?",
|
||||
"faq_w_chat_2_a": "チャットのアイデンティティはウォレットのリカバリーフレーズから導出されるため、ウォレットと一緒に持ち運ばれます。ウォレットを復元すればアイデンティティも戻ってきます。別途アカウントやパスワードを作成する必要はありません。",
|
||||
"faq_w_chat_2_q": "チャットのアイデンティティはどのように機能しますか?",
|
||||
"faq_w_chat_3_a": "連絡先タブでアドレスを入力して連絡先を追加します。名前やアバターの設定は任意です。連絡先は現在のウォレットのみに限定することも、このコンピューター上で開くすべてのウォレットで共有することもできます。",
|
||||
"faq_w_chat_3_q": "連絡先はどのように機能しますか?",
|
||||
"faq_w_chat_4_a": "会話を開き、ヘッダーのアイコンを使用してください。非表示(目のアイコン)はリストから削除しますが、すべてのメッセージは保持されます。新しいメッセージが届くと再表示されます。削除(ゴミ箱のアイコン)はダイアログを開きます。「削除」はこの端末上の履歴を消去します(相手から再度メッセージが届くと会話は復活します)。「削除してブロック」はさらに、会話リスト上部の「ブロック済み」リストから解除するまで相手からの今後のメッセージも停止します。ここでの操作はすべてこの端末のみに影響し、メッセージはブロックチェーン上に残り、相手も自分のコピーを保持し続けるため、「送信取り消し」ではありません。",
|
||||
"faq_w_chat_4_q": "会話を非表示、削除、またはブロックするにはどうすればよいですか?",
|
||||
"faq_w_chat_title": "チャットと連絡先",
|
||||
"faq_w_gs_1_a": "ObsidianDragonはDragonX(DRGX)のフルノードウォレットです。コインを管理し、デスクトップ版ではバックグラウンドでDragonXフルノード(デーモン)を実行するため、サードパーティのサーバーを信頼することなく、ウォレット自身がブロックチェーンを検証します。\n\nウォレット(鍵、残高、送金)とノード(ブロックチェーンとネットワーク)という2つの部分が連携して動作するイメージです。ここでの質問も同様に分かれています。上部の「ウォレット」タブと「デーモン」タブをご覧ください。",
|
||||
"faq_w_gs_1_q": "ObsidianDragonとは何ですか?",
|
||||
"faq_w_gs_2_a": "初回起動時、セットアップウィザードが自動的にウォレットを作成します。新しいウォレットは秘密のリカバリーフレーズ(単語のリスト)でバックアップされます。このフレーズを書き留めてオフラインで保管してください。このコンピューターを失った場合、資金を復元する唯一の方法です。\n\nフレーズはいつでも 設定 > バックアップとデータ > シードフレーズ から確認・バックアップできます。",
|
||||
"faq_w_gs_2_q": "新しいウォレットを作成するにはどうすればよいですか?",
|
||||
"faq_w_gs_3_a": "リカバリーフレーズをお持ちの場合は、シードフレーズ復元フローを使用してください。(フルノード版では、起動前に既存の wallet.dat ファイルをウォレットのデータディレクトリにコピーすることもできます。)\n\n復元後、ウォレットは過去のトランザクションを見つけるために再スキャンを行うため、初回は残高と履歴が表示されるまで時間がかかることがあります。",
|
||||
"faq_w_gs_3_q": "すでに持っているウォレットを復元するにはどうすればよいですか?",
|
||||
"faq_w_gs_4_a": "ウィザードでは、外観/テーマの選択、暗号化とPINの設定を行うことができ、フルノード版ではさらに初回同期を高速化するためのブートストラップのダウンロードを任意で選択できます。どのステップもスキップでき、後から設定でいつでも変更できます。",
|
||||
"faq_w_gs_4_q": "初回起動時のセットアップでは何が行われますか?",
|
||||
"faq_w_gs_title": "はじめに",
|
||||
"faq_w_sec_1_a": "設定 > ノードとセキュリティ を開き、パスフレーズを設定してください(セキュリティのセクション)。暗号化はディスク上の秘密鍵を保護するので、ファイルにアクセスできる人がいてもコインを送金することはできません。\n\n強力なパスフレーズを選び、絶対に紛失しないでください。パスフレーズなしでは暗号化されたウォレットを復元する方法はありません。",
|
||||
"faq_w_sec_1_q": "ウォレットを暗号化するにはどうすればよいですか?",
|
||||
"faq_w_sec_2_a": "PINはアプリの画面をロックし、離席中に残高や操作を非表示にします。これは暗号化に加えた利便性のためのロックで、ウォレットを暗号化した後、ウィザード中または設定で設定します。",
|
||||
"faq_w_sec_2_q": "PIN/ロック画面とは何ですか?",
|
||||
"faq_w_sec_3_a": "いいえ。ウォレットの暗号化を回避する方法はありません。シードリカバリーフレーズをまだお持ちであれば、そこから新しいウォレットに復元し、新しいパスフレーズを設定できます。パスフレーズとシードフレーズのどちらも失った場合、資金を復元することはできません。",
|
||||
"faq_w_sec_3_q": "パスフレーズを忘れました — 復元できますか?",
|
||||
"faq_w_sec_title": "セキュリティと暗号化",
|
||||
"faq_w_seed_1_a": "設定 > バックアップとデータ > シードフレーズ に移動してください。単語を順番どおりに紙に書き留め、安全でオフラインの場所に保管してください。フレーズを持つ人は誰でもあなたのコインを送金できるため、写真、メール、クラウドメモには絶対に保存しないでください。",
|
||||
"faq_w_seed_1_q": "リカバリーフレーズはどうやってバックアップすればよいですか?",
|
||||
"faq_w_seed_2_a": "古い(レガシー)ウォレットはシードベースではありませんでした。このウォレットは、レガシーウォレットを最新のシードベースのウォレットへ移行できます。新しいシードウォレットを作成し、資金をそこへスイープします。設定 > バックアップとデータ の「シードウォレットへ移行…」を探してください。この機能には現在のデーモンバージョンが必要です。",
|
||||
"faq_w_seed_2_q": "ウォレットにリカバリーフレーズがありません — 追加できますか?",
|
||||
"faq_w_seed_3_a": "ウォレットのセットアップ時にシード復元フローを使用し、単語を順番どおりに入力してください。ウォレットは残高と履歴を再構築するために再スキャンを行うため、初回は時間がかかることがあります。",
|
||||
"faq_w_seed_3_q": "リカバリーフレーズから復元するにはどうすればよいですか?",
|
||||
"faq_w_seed_4_a": "シードフレーズは主要なバックアップであり、これだけですべてを復元できます。ウォレットファイルのコピーは便利な二次バックアップで、ラベルや設定も保持されます。どちらのバックアップもオフラインかつ非公開の場所に保管してください。",
|
||||
"faq_w_seed_4_q": "wallet.dat もバックアップすべきですか?",
|
||||
"faq_w_seed_title": "シードフレーズとバックアップ",
|
||||
"faq_w_send_1_a": "送金タブを開き、受取人のアドレスを貼り付け、金額を入力して確認してください。ウォレットが暗号化されている場合、トランザクションのためにロック解除を求められます。\n\nシールドアドレスへの送金は非公開、透明アドレスへの送金は公開されます。",
|
||||
"faq_w_send_1_q": "資金を送金するにはどうすればよいですか?",
|
||||
"faq_w_send_2_a": "DragonXの手数料は非常に低く、自動的に設定されます。手数料は確認前に表示されます。通常は変更する必要はありません。",
|
||||
"faq_w_send_2_q": "どのくらいの手数料がかかりますか?",
|
||||
"faq_w_send_3_a": "はい — シールド(z)アドレスへ送金する際は、暗号化されたメモを添付できます。読めるのは受取人だけです。透明アドレスはメモに対応していません。",
|
||||
"faq_w_send_3_q": "支払いにメッセージを添付できますか?",
|
||||
"faq_w_send_4_a": "トランザクションはブロックに取り込まれ、確認数を得るまで「保留中」となります。通常1~2分ほどかかります。シールドトランザクションでは、証明を生成するためにウォレットが同期済みである必要もあります。\n\n送金が異常に長く保留中のままの場合は、ウォレットが接続され、同期済みであることを確認してください(ウィンドウ下部のステータスバーを参照)。",
|
||||
"faq_w_send_4_q": "支払いを送りましたが「保留中」のままです — なぜですか?",
|
||||
"faq_w_send_title": "送受金",
|
||||
"faq_w_set_1_a": "設定 > 外観 でテーマとアクセントカラーを選べます。ライト/ダークの複数のスキンが含まれており、性能の低いマシン向けにぼかしなどのエフェクトをオフにすることもできます。",
|
||||
"faq_w_set_1_q": "テーマを変更するには?",
|
||||
"faq_w_set_2_a": "はい — 設定 > 外観 に言語選択があります。ウォレットには複数の翻訳が同梱されており、未翻訳の部分は英語のまま表示されます。",
|
||||
"faq_w_set_2_q": "言語を変更できますか?",
|
||||
"faq_w_set_3_a": "設定 > 外観 のフォントスケールオプションを使います。インターフェース全体を拡大縮小でき、高解像度(HiDPI)ディスプレイでアプリの表示が小さくなりすぎる場合に便利です。",
|
||||
"faq_w_set_3_q": "文字が小さすぎる(または大きすぎる)のですが、拡大縮小できますか?",
|
||||
"faq_w_set_4_a": "設定で価格取得を有効にしてください。残高と併せて概算の法定通貨価値が表示され、小さな相場チャートも表示できるようになります。価格は参考情報にすぎません。",
|
||||
"faq_w_set_4_q": "法定通貨での価格を表示するには?",
|
||||
"faq_w_set_title": "設定と外観",
|
||||
"favorite_address": "お気に入りに追加",
|
||||
"fee": "手数料",
|
||||
"fee_high": "高い",
|
||||
@@ -593,6 +878,63 @@
|
||||
"general": "一般",
|
||||
"generating": "生成中",
|
||||
"go_to_receive": "受信へ移動",
|
||||
"grpa_current_block_paren": "(現在: %d)",
|
||||
"grpa_days_ago": "%lld日前",
|
||||
"grpa_dbg_addrman": "ピアアドレスの追跡と管理",
|
||||
"grpa_dbg_alert": "アラートシステムのメッセージ",
|
||||
"grpa_dbg_bench": "操作のベンチマーク計測",
|
||||
"grpa_dbg_coindb": "コインデータベースの読み書き操作",
|
||||
"grpa_dbg_db": "Berkeley DB の操作",
|
||||
"grpa_dbg_estimatefee": "手数料見積もりアルゴリズム",
|
||||
"grpa_dbg_http": "HTTP RPCサーバーの動作",
|
||||
"grpa_dbg_libevent": "Libevent ネットワークライブラリ",
|
||||
"grpa_dbg_lock": "ロック競合のデバッグ",
|
||||
"grpa_dbg_mempool": "トランザクションメモリプールの動作",
|
||||
"grpa_dbg_net": "ネットワーク接続とメッセージ",
|
||||
"grpa_dbg_paymentdisclosure": "支払い開示プロトコル",
|
||||
"grpa_dbg_pow": "プルーフオブワークのマイニング動作",
|
||||
"grpa_dbg_proxy": "SOCKS5 プロキシ接続",
|
||||
"grpa_dbg_prune": "ブロックのプルーニング操作",
|
||||
"grpa_dbg_rand": "乱数生成",
|
||||
"grpa_dbg_reindex": "ブロックチェーンの再インデックス進捗",
|
||||
"grpa_dbg_rpc": "RPCコマンドの処理",
|
||||
"grpa_dbg_selectcoins": "トランザクション用のコイン選択",
|
||||
"grpa_dbg_tor": "Tor統合とサーキット情報",
|
||||
"grpa_dbg_zmq": "ZeroMQ 通知システム",
|
||||
"grpa_dbg_zrpc": "シールド(z-addr)RPC操作",
|
||||
"grpa_enter_private_key_to_import": "インポートする秘密鍵を入力してください。",
|
||||
"grpa_error_prefix": "エラー: ",
|
||||
"grpa_hr_ago": "%lld時間前",
|
||||
"grpa_invalid_response_from_daemon": "デーモンからの応答が無効です",
|
||||
"grpa_invalid_suffix": "(無効)",
|
||||
"grpa_min_ago": "%lld分前",
|
||||
"grpa_sec_ago": "%lld秒前",
|
||||
"grpa_seed_demo_chat": "デモチャットのシード",
|
||||
"grpa_showing_first_100_of": "... %d件中の最初の100件を表示",
|
||||
"grpa_tab_about": "情報",
|
||||
"grpa_tab_appearance": "外観",
|
||||
"grpa_tab_backup_data": "バックアップとデータ",
|
||||
"grpa_tab_chat": "チャット",
|
||||
"grpa_tab_explorer": "エクスプローラー",
|
||||
"grpa_tab_node_security": "ノードとセキュリティ",
|
||||
"grpa_tab_wallet": "ウォレット",
|
||||
"grpa_unexpected_getblockhash_result": "予期しない getblockhash の結果",
|
||||
"grpb_copy": "コピー",
|
||||
"grpb_max": "最大",
|
||||
"grpb_new_badge_suffix": " [新規]",
|
||||
"grpb_preview_msg_payment_through": "支払いは完了しましたか? 🙂",
|
||||
"grpb_preview_msg_sending_rest": "残りを今送金します 👍",
|
||||
"grpb_preview_msg_yep_confirmed": "はい — たった今確認できました ✅",
|
||||
"grpb_selected_suffix": "\n(選択済み)",
|
||||
"grpb_tooltip_address_balance": "%s\n残高: %.8f %s%s",
|
||||
"grpb_undo_clear": "消去を元に戻す",
|
||||
"grpc_benchmark_inconclusive": "ベンチマークの結果が不明確です: ハッシュレートのサンプルが記録されませんでした。プール接続を確認して再試行してください。",
|
||||
"grpc_benchmark_takes_secs": "ベンチマークには約%d秒かかり、マイニングが中断されます。開始するにはもう一度クリックしてください。",
|
||||
"grpc_bootstrap_failed": "ブートストラップに失敗しました",
|
||||
"grpc_bootstrap_not_initialized": "ブートストラップが初期化されていません",
|
||||
"grpc_hashrate_fee": "%s 手数料 %s%%",
|
||||
"grpc_key_not_available": "このアドレスの鍵は利用できません",
|
||||
"grpc_na": "該当なし",
|
||||
"height": "高さ",
|
||||
"help": "ヘルプ",
|
||||
"hidden_tag": " (非表示)",
|
||||
@@ -817,9 +1159,27 @@
|
||||
"memo_optional": "メモ(任意)",
|
||||
"memo_upper": "メモ",
|
||||
"memo_z_only": "注:メモはシールド (z) アドレスへの送金時のみ利用可能です",
|
||||
"merge_addr_created": "シールドアドレスを作成しました。",
|
||||
"merge_advanced": "詳細設定",
|
||||
"merge_back": "戻る",
|
||||
"merge_batch_fmt": "1回の実行で最大%d件の入力を統合します — 残りは繰り返し実行して完了させてください。",
|
||||
"merge_confirm_btn": "確認",
|
||||
"merge_confirm_fmt": "~%s DRGXを入力%d件から%sへ統合しますか?",
|
||||
"merge_create_zaddr": "シールドアドレスを作成",
|
||||
"merge_creating": "アドレスを作成中…",
|
||||
"merge_description": "複数のUTXOを単一のシールドアドレスに統合します。ウォレットサイズの縮小とプライバシーの向上に役立ちます。",
|
||||
"merge_fee_hint": "この取引にかかるネットワーク手数料です。",
|
||||
"merge_funds": "資金を統合",
|
||||
"merge_max_inputs": "バッチあたりの最大入力数",
|
||||
"merge_no_spendable": "統合できる使用可能な入力がまだありません。",
|
||||
"merge_progress": "統合中… 数分かかることがあります。このウィンドウは閉じても構いません。",
|
||||
"merge_scope_fmt": "透明%d件 + シールド%d件の入力 · 使用可能 約%s DRGX",
|
||||
"merge_scope_loading": "入力を確認中…",
|
||||
"merge_send_failed": "マージに失敗しました: ",
|
||||
"merge_source": "統合",
|
||||
"merge_src_both": "両方",
|
||||
"merge_src_shielded": "シールド",
|
||||
"merge_src_transparent": "透明",
|
||||
"merge_started": "統合操作を開始しました",
|
||||
"merge_title": "アドレスに統合",
|
||||
"mig_adopt": "これを自分のウォレットにする",
|
||||
@@ -1059,6 +1419,13 @@
|
||||
"peers_upper": "ピア",
|
||||
"peers_version": "バージョン",
|
||||
"pending": "保留中",
|
||||
"picker_dat_count": "このフォルダー内のウォレットファイル:%d件",
|
||||
"picker_dat_none": "このフォルダー直下にウォレットファイルはありません",
|
||||
"picker_empty": "このフォルダーにはサブフォルダーもウォレットファイルもありません。",
|
||||
"picker_home": "ホームフォルダー",
|
||||
"picker_select": "このフォルダーをスキャン",
|
||||
"picker_title": "スキャンするフォルダーを選択",
|
||||
"picker_up": "1つ上の階層へ",
|
||||
"pin_change_desc": "ロック解除PINを変更します。現在のPINと新しいPINが必要です。",
|
||||
"pin_confirm_new_label": "新しいPINの確認:",
|
||||
"pin_current_label": "現在のPIN:",
|
||||
@@ -1195,6 +1562,7 @@
|
||||
"save_z_transactions": "Z取引を取引リストに保存",
|
||||
"sb_auth_failed": "認証失敗 — rpcuser/rpcpassword を確認してください",
|
||||
"sb_block": "ブロック: %d",
|
||||
"sb_block_db_unreadable": "ブロックデータベースを読み込めません — 再構築が必要です",
|
||||
"sb_building_witnesses": "証人を設定中",
|
||||
"sb_building_witnesses_pct": "ウィットネスを再構築中 %.0f%%",
|
||||
"sb_connecting_daemon": "dragonxd に接続中...",
|
||||
@@ -1202,12 +1570,15 @@
|
||||
"sb_connecting_external": "外部デーモンに接続中...",
|
||||
"sb_connecting_generic": "デーモンに接続中...",
|
||||
"sb_daemon_crashed": "デーモンが %d 回クラッシュしました",
|
||||
"sb_daemon_extract_failed": "デーモンファイルの書き込みに失敗しました — 空きディスク容量と権限を確認してください。",
|
||||
"sb_daemon_files_failed": "デーモンファイルを%sに書き込めませんでした — 空きディスク容量と権限を確認してください。",
|
||||
"sb_daemon_not_found": "デーモンが見つかりません",
|
||||
"sb_daemon_start_failed": "dragonxd を起動できませんでした",
|
||||
"sb_dragonxd_running": "dragonxd 実行中",
|
||||
"sb_dragonxd_stopped": "dragonxd 停止",
|
||||
"sb_dragonxd_stopping": "dragonxd を停止中...",
|
||||
"sb_extracting_sapling": "Sapling パラメータを展開中...",
|
||||
"sb_finishing_repair": "ウォレットの修復を仕上げています — 再スキャン中…",
|
||||
"sb_importing_keys": "鍵をインポート中",
|
||||
"sb_loading_config": "設定を読み込み中...",
|
||||
"sb_mining_hs": "%.1f H/s",
|
||||
@@ -1217,6 +1588,7 @@
|
||||
"sb_net_mhs": "ネット: %.2f MH/s",
|
||||
"sb_no_conf": "DRAGONX.conf が見つかりません",
|
||||
"sb_peers": "ピア: %zu",
|
||||
"sb_plaintext_remote_blocked": "リモートホストへRPC認証情報を平文で送信することを拒否しました。許可するにはDRAGONX.confにrpcallowplaintext=1を追加するか、rpctls=1でTLSを有効にしてください。",
|
||||
"sb_rescanning": "再スキャン",
|
||||
"sb_rescanning_pct": "再スキャン %.0f%%",
|
||||
"sb_restarting_daemon": "デーモンを再起動中...",
|
||||
@@ -1228,6 +1600,7 @@
|
||||
"sb_waiting_config": "デーモン設定を待機中...",
|
||||
"sb_waiting_daemon": "dragonxd を待機中...",
|
||||
"sb_waiting_daemon_err": "dragonxd を待機中 — %s",
|
||||
"sb_wallet_needs_recovery": "ウォレットの修復が利用可能です",
|
||||
"sb_warming_up": "ウォームアップ中...",
|
||||
"sb_witness_cache": "ウィットネスを再構築中",
|
||||
"scale_effects": "スケールとエフェクト",
|
||||
@@ -1237,6 +1610,45 @@
|
||||
"screenshot_sweep_full": "UI全体スイープ",
|
||||
"search_icons": "アイコンを検索...",
|
||||
"search_placeholder": "検索...",
|
||||
"sec_changing_passphrase": "パスフレーズを変更しています...",
|
||||
"sec_changing_pin": "PINを変更しています...",
|
||||
"sec_couldnt_lock_wallet": "ウォレットをロックできませんでした。まだロック解除された状態です。デーモンの接続を確認してください。",
|
||||
"sec_encrypted_backup_suffix": "\n暗号化バックアップ: wallet.dat.encrypted.bak",
|
||||
"sec_encrypting_wallet": "ウォレットを暗号化しています...",
|
||||
"sec_encryption_did_not_complete": "ウォレットの暗号化が完了しませんでした。ウォレットは暗号化されていません。設定を開いて暗号化を完了してください。",
|
||||
"sec_encryption_failed_prefix": "暗号化に失敗しました: ",
|
||||
"sec_failed_prefix": "失敗しました: ",
|
||||
"sec_failed_to_create_vault": "ボールトの作成に失敗しました",
|
||||
"sec_importing_keys_rescanning": "鍵をインポートし、ブロックチェーンを再スキャンしています。処理中もウォレットは使用できます",
|
||||
"sec_incorrect_current_pin": "現在のPINが正しくありません",
|
||||
"sec_incorrect_passphrase_decrypt": "パスフレーズが正しくありません",
|
||||
"sec_incorrect_passphrase_pin_setup": "パスフレーズが正しくありません",
|
||||
"sec_incorrect_pin_remove": "PINが正しくありません",
|
||||
"sec_internal_error_change_pin": "内部エラー",
|
||||
"sec_internal_error_remove_pin": "内部エラー",
|
||||
"sec_mode_passphrase": " パスフレーズ",
|
||||
"sec_not_connected_to_daemon": "デーモンに接続されていません",
|
||||
"sec_not_connected_to_daemon_pin": "デーモンに接続されていません",
|
||||
"sec_passphrase_changed_successfully": "パスフレーズを変更しました",
|
||||
"sec_pin_changed_successfully": "PINを変更しました",
|
||||
"sec_pin_removed": "PINを削除しました",
|
||||
"sec_pin_set_successfully": "PINを設定しました",
|
||||
"sec_restart_daemon_for_encryption": "暗号化を有効にするには、デーモンを再起動してください。",
|
||||
"sec_too_many_attempts_wait": "試行回数が多すぎます。%.0f秒お待ちください...",
|
||||
"sec_total_elapsed_fmt": "合計経過時間: %d分 %02d秒",
|
||||
"sec_unlock_button": "ロック解除",
|
||||
"sec_unlock_failed_prefix": "ロック解除に失敗しました: ",
|
||||
"sec_unlocking_fmt": "ロック解除中%s",
|
||||
"sec_use_passphrase_instead": "代わりにパスフレーズを使用",
|
||||
"sec_use_pin_instead": "代わりにPINを使用",
|
||||
"sec_verifying_passphrase": "パスフレーズを確認しています...",
|
||||
"sec_verifying_pin": "PINを確認しています...",
|
||||
"sec_wallet_decrypted_all_keys_imported": "ウォレットの復号に成功しました。すべての鍵をインポートしました。",
|
||||
"sec_wallet_encrypted_and_pin_set": "ウォレットを暗号化し、PINを設定しました",
|
||||
"sec_wallet_encrypted_but_pin_vault_failed": "ウォレットは暗号化されましたが、PINボールトの作成に失敗しました",
|
||||
"sec_wallet_encrypted_restarting_daemon": "ウォレットを暗号化しました。デーモンを再起動しています...",
|
||||
"sec_wallet_encrypted_successfully": "ウォレットを暗号化しました",
|
||||
"sec_wallet_locked_title": "ウォレットがロックされています",
|
||||
"security": "セキュリティ",
|
||||
"seed_backup_button": "シードフレーズ",
|
||||
"seed_backup_close": "閉じる",
|
||||
@@ -1260,6 +1672,7 @@
|
||||
"send_amount": "金額",
|
||||
"send_amount_details": "金額の詳細",
|
||||
"send_amount_upper": "金額",
|
||||
"send_available_note": "利用可能",
|
||||
"send_cannot_send_now": "現在送信できません — 接続、同期、利用可能な残高を確認してください。",
|
||||
"send_clear_fields": "すべてのフォームフィールドをクリアしますか?",
|
||||
"send_contacts_button": "連絡先から選択",
|
||||
@@ -1447,17 +1860,34 @@
|
||||
"status": "ステータス",
|
||||
"stop_external": "外部デーモンを停止",
|
||||
"stop_mining": "マイニング停止",
|
||||
"stratum_allowip_hint": "マイナーの接続を許可するIPまたはCIDR(空欄=このコンピューターのみ)",
|
||||
"stratum_expose_warn": "許可したネットワークにマイニングポートを公開します — 信頼できるLANでのみ使用してください。",
|
||||
"stratum_host": "マイニングプールをホストする(stratum)",
|
||||
"stratum_host_hint": "マイナーはポート22769(RPCポート+1000)でこのコンピューターにRandomX stratumマイナーとして接続します。適用するにはデーモンを再起動してください。",
|
||||
"stratum_host_section": "マイニングプールのホスティング",
|
||||
"submitting_transaction": "取引を送信中...",
|
||||
"success": "成功",
|
||||
"summary": "概要",
|
||||
"sweep_button": "集約",
|
||||
"sweep_caveat": "鍵をインポートして、すべての資金をあなたのアドレスへ移す取引に署名します。鍵は残高ゼロでウォレットに残ります。",
|
||||
"sweep_current_theme_only": "現在のテーマのみ",
|
||||
"sweep_dest_label": "集約先",
|
||||
"sweep_dest_new": "新しいシールドアドレス(推奨)",
|
||||
"sweep_done": "完了 — 資金をあなたのアドレスに集約しました。",
|
||||
"sweep_to": "集約先:",
|
||||
"sweep_toggle": "ウォレットに集約(鍵は保持しない)",
|
||||
"sweep_tx": "取引:",
|
||||
"swin_connection_failed": "接続に失敗しました: ",
|
||||
"swin_connection_successful": "接続に成功しました!\ndragonxd バージョン: ",
|
||||
"swin_invalid_suffix": "(無効)",
|
||||
"swin_no_history_file_found": "履歴ファイルが見つかりません",
|
||||
"swin_rescan_failed": "再スキャンに失敗しました: ",
|
||||
"swin_rescan_started_from_block": "再スキャンを開始しました。開始ブロック ",
|
||||
"swin_rescan_to": " 〜 ",
|
||||
"swin_rpc_client_not_initialized": "RPCクライアントが初期化されていません",
|
||||
"swin_settings_saved": "設定を保存しました",
|
||||
"swin_theme_list_refreshed": "テーマ一覧を更新しました",
|
||||
"swin_ztx_history_cleared": "Zトランザクション履歴を消去しました",
|
||||
"switch_corrupt_body": "このウォレットは破損しているようです。ノードが開けませんでした。バックアップから復元するか、作り直すか、修復を試してください。",
|
||||
"switch_corrupt_repair": "修復を試す(salvage)",
|
||||
"switch_progress_background": "バックグラウンドで続行",
|
||||
@@ -1618,6 +2048,8 @@
|
||||
"tt_simple_bg": "背景にシンプルなグラデーションを使用\\nホットキー:Ctrl+Up",
|
||||
"tt_simple_bg_alt": "テーマ背景画像のグラデーション版を使用\\nホットキー:Ctrl+Up",
|
||||
"tt_stop_external": "このウォレット外で起動された\\nデーモンに接続する場合に適用",
|
||||
"tt_stratum_host": "このノード上でRandomX stratumプールサーバーを実行し、他のRandomXマイナーがこのコンピューターに接続できるようにします。v1.3.0以降のノードが必要で、適用にはデーモンの再起動が必要です。",
|
||||
"tt_sweep_current_theme_only": "全テーマを順に処理せず、有効なテーマのみをスイープします",
|
||||
"tt_test_conn": "デーモンへの RPC 接続を確認",
|
||||
"tt_theme_effects": "テーマごとのシマー、グロー、色相サイクル",
|
||||
"tt_theme_hotkey": "ホットキー:Ctrl+左/右でテーマを切り替え",
|
||||
@@ -1626,6 +2058,7 @@
|
||||
"tt_ui_opacity": "カードとサイドバーの不透明度(100% = 完全不透明、低い = より透過)",
|
||||
"tt_validate": "DragonX アドレスが有効かどうかを確認",
|
||||
"tt_verbose": "詳細な接続診断、デーモン状態、\\nポート所有者情報をコンソールタブに記録",
|
||||
"tt_wallet_size_warn": "シールドウォレットはノートごとのウィットネスデータによって肥大化します — 多数のノートを1つのアドレスに統合するとサイズを抑えられます。事前にバックアップしてください。",
|
||||
"tt_wallets_button": "ウォレットファイルを一覧表示して切り替えます",
|
||||
"tt_website": "DragonX ウェブサイトを開く",
|
||||
"tt_window_opacity": "背景の不透明度(低い = デスクトップがウィンドウ越しに見える)",
|
||||
@@ -1692,8 +2125,68 @@
|
||||
"view_on_explorer": "エクスプローラーで表示",
|
||||
"waiting_for_daemon": "デーモン接続を待機中...",
|
||||
"wallet": "ウォレット",
|
||||
"wallet_degraded_notify": "ウォレットは機能制限モードで開かれました。既存の資金は安全で使用可能ですが、新しいアドレスの作成とシールドは無効になっています。ウォレットを完全に修復するには、シードフレーズをバックアップしてそこから復元してください。",
|
||||
"wallet_diagnostics_hdr": "診断",
|
||||
"wallet_empty": "ウォレットは空です",
|
||||
"wallet_empty_hint": "受信に切り替えてアドレスを取得し、資金の受け取りを開始してください。",
|
||||
"wallet_options_hdr": "オプション",
|
||||
"wallet_rebuild_failed": "再構築で有効なウォレットが生成されなかったため、変更は行われませんでした。ウォレットはそのままです。",
|
||||
"wallet_rebuild_install_failed": "再構築したウォレットをインストールできませんでした。現在のウォレットはそのまま残されています。",
|
||||
"wallet_rebuild_no_helper": "このビルドではウォレット再構築ヘルパーを利用できません。「復元」を使うか、手動で再構築してください。",
|
||||
"wallet_rebuild_no_source": "再構築可能な読み取り可能なウォレットが見つかりませんでした。変更は行われませんでした。",
|
||||
"wallet_rebuild_ok": "ウォレットデータベースを再構築しました — ノードが読み込み、残高の再スキャンを行っています。",
|
||||
"wallet_rebuild_started": "ウォレットファイルを修復しています — このウィンドウを閉じないでください。",
|
||||
"wallet_recovered_body": "何も削除されていません。元のウォレットは日付付きのバックアップファイルとしてこのコンピューターに保存されたままで、以下のどの選択肢もファイルをコピーまたは名前変更するだけで、削除することはありません。鍵が再生成されることはなく、再読み込みされるだけです。\n\n現在読み込まれている修復済みコピーには、直近の一部の取引が含まれていない可能性があるため、以下を完了して再スキャンが終わるまでは残高が少し低く見えることがあります。",
|
||||
"wallet_recovered_dismiss": "今はしない — 修復済みコピーのまま使う",
|
||||
"wallet_recovered_dismiss_sub": "ファイルは変更されません。ステータスバーからいつでもこの画面を再度開けます。どちらを選んでも、オリジナルは安全にバックアップされたままです。",
|
||||
"wallet_recovered_notify": "ウォレットファイルの修復が必要でした — オリジナルは安全にバックアップされています。アプリを開いてオプションを確認してください。",
|
||||
"wallet_recovered_open_folder": "ファイルを表示",
|
||||
"wallet_recovered_open_folder_sub": "ウォレットデータフォルダを開き、バックアップファイルを自分で確認できます — 何も変更されません。",
|
||||
"wallet_recovered_rebuild": "自動修復する(推奨)",
|
||||
"wallet_recovered_rebuild_sub": "最も内容の多いウォレットファイルから復元可能なすべてのレコードを読み込み、新しいクリーンなファイルを作成してから再起動します。最も徹底したオプションです — いずれにせよ現在のファイルは日付付きバックアップとして保存されます。",
|
||||
"wallet_recovered_restore": "代わりにオリジナルファイルを復元する",
|
||||
"wallet_recovered_restore_sub": "手つかずの最も内容の多いバックアップをそのまま元に戻し、再スキャンします — やや高速ですが、そのファイルにあった範囲までしか復元されません。いずれにせよ現在のファイルは日付付きバックアップとして保存されます。",
|
||||
"wallet_recovered_safety": "コインは安全です。",
|
||||
"wallet_recovered_title": "ウォレットファイルの簡単な修復が必要です",
|
||||
"wallet_recovered_warn": "アプリの起動時に、ウォレットファイルが整合性チェックに合格しないことが判明しました — これはアプリの更新後や、正常終了しなかった場合によく起こります。アプリはすでにデータを保護しています。古いファイルは退避され、修復済みのコピーを読み込んだので、行き詰まることはありません。",
|
||||
"wallet_recovery_decide_later": "後で決める",
|
||||
"wallet_recovery_decide_later_tip": "この画面を閉じ、現在読み込まれているコピーをそのまま使用します。何も変更されず、いつでも修復できます — ステータスバーに「ウォレットの修復が可能」というリンクが表示され続けます。",
|
||||
"wallet_recovery_details_label": "技術的な詳細を表示",
|
||||
"wallet_recovery_done": "完了",
|
||||
"wallet_recovery_failure_body": "修復は実行されましたが、その結果が検証に合格しませんでした(読み込めなかった、またはアドレスがありませんでした)。そのため何かを置き換える前に自動的に破棄されました。ウォレットはクリックする前とまったく同じ状態です — ディスク上には何も変更されていません。",
|
||||
"wallet_recovery_failure_title": "修復は完了しませんでした",
|
||||
"wallet_recovery_files_detail": "オリジナルのウォレットはまだここにあります — アプリはそれをデータフォルダ内の日付付きバックアップ(wallet.<番号>.bak)にリネームしただけで、何も削除していません。「修復」は最も内容の多いウォレットからすべてのレコードを読み込み、まったく新しいクリーンなファイルを作成します。「復元」は最も内容の多いバックアップをそのままコピーして元に戻します。現在読み込まれているコピーもバックアップとして保存されます。",
|
||||
"wallet_recovery_files_label": "自分のファイルはどうなりますか?",
|
||||
"wallet_recovery_notnow_short": "後で",
|
||||
"wallet_recovery_other_options": "他のオプション",
|
||||
"wallet_recovery_rebuild_card": "自動修復する",
|
||||
"wallet_recovery_rebuild_card_desc": "復元可能なすべてのレコードをクリーンなファイルに読み込んでから再起動します。最も徹底したオプションです。",
|
||||
"wallet_recovery_recommended": "推奨",
|
||||
"wallet_recovery_repair_go": "修復",
|
||||
"wallet_recovery_rescan_body": "ブロックチェーンを再スキャンして残高と取引履歴を再構築しています。数分かかる場合があります — 何もする必要はありません。実行中はノードを再起動しないでください。",
|
||||
"wallet_recovery_rescan_elapsed": "%s 経過",
|
||||
"wallet_recovery_rescan_size": "再構築されたウォレットは現在 %s で、まだ増加中です",
|
||||
"wallet_recovery_rescan_slow": "通常より時間がかかっていますが、大きなウォレットでは正常です。実行したままにしておいて問題ありません — 進捗は「詳細設定 ▸ コンソール」で確認できます。",
|
||||
"wallet_recovery_rescan_title": "ウォレットの修復を完了しています",
|
||||
"wallet_recovery_restore_card": "オリジナルを復元",
|
||||
"wallet_recovery_restore_card_desc": "手つかずの最も内容の多いバックアップをそのまま元に戻します — そのファイルにあった範囲までしか復元されません。",
|
||||
"wallet_recovery_restore_go": "復元",
|
||||
"wallet_recovery_success_body": "アプリは修復済みのウォレットを読み込み、残高を集計するために再スキャンしています — 数分かかる場合があります。読み込める限りすべてのレコードを読み込みますが、まれに破損したファイルでは、以前よりわずかに少ない(または多い)アドレスが復元されることがあります。再スキャンが完了したら、残高と履歴が正しいか確認してください。",
|
||||
"wallet_recovery_success_restore": "最も内容の多いバックアップファイルが元の場所に戻され、現在読み込み中です。アプリは再スキャンしています。そのまま復元されているため、そのファイルにあった範囲まで正確です — 再スキャンが完了したら残高を確認してください。",
|
||||
"wallet_recovery_try_other": "別のオプションを試す",
|
||||
"wallet_recovery_whats_happened": "何が起きましたか?",
|
||||
"wallet_recovery_working_label": "処理中",
|
||||
"wallet_restore_bad_backup": "バックアップのウォレットファイルは読み込めないようです。そのため復元されませんでした — 現在のウォレットは変更されていません。ご自身のバックアップから復元してください。",
|
||||
"wallet_restore_busy": "ノードは再起動中でビジー状態です — しばらくしてからもう一度お試しください。",
|
||||
"wallet_restore_copy_failed": "バックアップウォレットをインストールできませんでした。現在のウォレットはそのまま残されました。",
|
||||
"wallet_restore_move_failed": "現在のウォレットを退避できませんでした — 何も変更されていません。",
|
||||
"wallet_restore_no_backup": "復元できる wallet.<timestamp>.bak が見つかりませんでした。何も変更されていません。",
|
||||
"wallet_restore_no_restart": "オリジナルのウォレットは復元されましたが、ノードは再起動しませんでした — 設定から起動してください。",
|
||||
"wallet_restore_ok": "オリジナルのウォレットを復元しました。ノードが読み込み中です。",
|
||||
"wallet_restore_started": "オリジナルファイルを復元しています — このウィンドウを閉じないでください。",
|
||||
"wallet_restore_stop_failed": "ノードが時間内に停止しなかったため、何も変更されていません。もう一度お試しください。",
|
||||
"wallet_size_consolidate": "ノートを統合…",
|
||||
"wallet_size_warn": "このウォレットファイルは大きくなっています。ノートを統合すると、さらなる増加を抑えられます。",
|
||||
"wallets_active": "アクティブ",
|
||||
"wallets_add": "フォルダを追加",
|
||||
"wallets_add_folder": "ウォレットファイルを検索する別のフォルダを追加:",
|
||||
@@ -1736,6 +2229,7 @@
|
||||
"wallets_open_inplace_tt": "このウォレットをその場で開きます — データディレクトリにリンク(コピーなし)",
|
||||
"wallets_remove_folder": "このフォルダーのスキャンを停止",
|
||||
"wallets_reveal": "フォルダを開く",
|
||||
"wallets_scan_folder": "別のフォルダをスキャンしてウォレットを検索…",
|
||||
"wallets_scanned_folders": "スキャン対象フォルダー:",
|
||||
"wallets_sort_addresses": "アドレス",
|
||||
"wallets_sort_asc": "昇順(古い/少ない/小さい順)",
|
||||
@@ -1805,19 +2299,30 @@
|
||||
"wiz_welcome_sub": "いくつか選ぶだけでフルノードの準備が整います。",
|
||||
"wiz_welcome_title": "ObsidianDragon へようこそ!",
|
||||
"wizard_daemon_start_failed": "デーモンの起動に失敗しました — 自動的に再試行されます",
|
||||
"xmrig_active_tt": "現在インストールされている(有効な)マイナー",
|
||||
"xmrig_browse_releases": "すべてのリリースを表示…",
|
||||
"xmrig_checking": "最新のマイナーを確認中…",
|
||||
"xmrig_current": "現在:",
|
||||
"xmrig_downgrade_note": "別のバージョンのマイナーをインストールすると、現在のものが置き換わります。まずマイニングを停止してください。変更は次回の起動時に反映されます。",
|
||||
"xmrig_download_install": "ダウンロードしてインストール",
|
||||
"xmrig_downloading": "ダウンロード中…",
|
||||
"xmrig_install_this": "このバージョンをインストール",
|
||||
"xmrig_installed": "インストール済み:",
|
||||
"xmrig_installed_ok": "マイナーをインストールしました",
|
||||
"xmrig_installing": "インストール中…",
|
||||
"xmrig_latest": "最新:",
|
||||
"xmrig_latest_badge": "最新",
|
||||
"xmrig_latest_tt": "利用可能な最新バージョン",
|
||||
"xmrig_loading_releases": "リリースを読み込み中…",
|
||||
"xmrig_no_notes": "このバージョンのリリースノートはありません。",
|
||||
"xmrig_none": "なし",
|
||||
"xmrig_prerelease_tt": "プレリリース/テストビルド",
|
||||
"xmrig_reinstall": "再インストール",
|
||||
"xmrig_released": "リリース日:",
|
||||
"xmrig_releases": "xmrig リリース",
|
||||
"xmrig_status_current": "このバージョンはインストール済みです",
|
||||
"xmrig_status_latest": "最新バージョン",
|
||||
"xmrig_status_older": "インストール済みのバージョンより古い",
|
||||
"xmrig_stop_mining_first": "マイナーを更新する前にマイニングを停止してください。",
|
||||
"xmrig_unavailable_body": "このプラットフォーム向けのマイナービルドは利用できません。",
|
||||
"xmrig_unavailable_title": "マイナーの更新は利用できません",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"about_license_text": "본 소프트웨어는 GNU General Public License v3 (GPLv3) 하에 배포됩니다. 라이선스 조건에 따라 자유롭게 사용, 수정 및 배포할 수 있습니다.",
|
||||
"about_peers_count": "%zu 피어",
|
||||
"about_release": "릴리스",
|
||||
"about_source": "소스",
|
||||
"about_title": "ObsidianDragon 정보",
|
||||
"about_version": "버전:",
|
||||
"about_website": "웹사이트",
|
||||
@@ -60,7 +61,84 @@
|
||||
"amount_label": "금액:",
|
||||
"animate_avatars": "아바타 애니메이션",
|
||||
"appearance": "외관",
|
||||
"appx_back": "뒤로",
|
||||
"appx_back_up_seed_phrase_title": "시드 문구 백업",
|
||||
"appx_birthday_block_height": "생성 시점(블록 높이): %llu — 이것도 함께 백업하세요.",
|
||||
"appx_blockchain_data_deleted": "블록체인 데이터를 삭제했습니다(%d개 항목). 네트워크에서 다시 동기화하기 위해 데몬을 재시작합니다.",
|
||||
"appx_blockchain_maintenance_in_progress": "블록체인 유지 관리 작업이 이미 진행 중입니다.",
|
||||
"appx_blockchain_rescan_complete": "블록체인 다시 스캔 완료",
|
||||
"appx_bootstrap_complete_reconciling": "부트스트랩 완료 — 새 체인 데이터와 지갑을 대조하는 중입니다.",
|
||||
"appx_cancel": "취소",
|
||||
"appx_cleaning_up": "정리하는 중...",
|
||||
"appx_confirm_your_backup": "백업 확인",
|
||||
"appx_copied_clipboard_autoclears": "복사됨 — 클립보드가 45초 후 자동으로 지워집니다",
|
||||
"appx_copy": "복사",
|
||||
"appx_could_not_start_restore": "복구를 시작할 수 없습니다",
|
||||
"appx_create_failed_prefix": "생성 실패: ",
|
||||
"appx_creating_your_wallet": "지갑을 생성하는 중…",
|
||||
"appx_daemon_error": "데몬 오류",
|
||||
"appx_daemon_reinstall_in_progress": "데몬 재설치가 이미 진행 중입니다.",
|
||||
"appx_disconnecting": "연결 해제 중...",
|
||||
"appx_done": "완료",
|
||||
"appx_dragonxd_output": "dragonxd 출력",
|
||||
"appx_encrypting_wallet": "지갑 암호화 중...",
|
||||
"appx_fullnode_lifecycle_unavailable_lite": "풀노드 수명 주기 작업은 라이트 빌드에서 사용할 수 없습니다",
|
||||
"appx_installing_bundled_daemon": "번들 데몬을 설치하는 중 — 노드가 중지, 업데이트 후 다시 시작됩니다...",
|
||||
"appx_invalid_payment_uri_prefix": "잘못된 결제 URI: ",
|
||||
"appx_ive_written_it_down": "적어 두었습니다",
|
||||
"appx_keep_node_running_and_quit": "노드 유지하고 종료",
|
||||
"appx_last_block_n": "마지막 블록: %d",
|
||||
"appx_last_used_wallet_not_found_prefix": "마지막으로 사용한 지갑 파일(",
|
||||
"appx_last_used_wallet_not_found_suffix": ")을 찾을 수 없어 기본 지갑을 대신 열었습니다. 옮기셨다면 복원한 후 지갑 목록에서 다시 전환하세요.",
|
||||
"appx_low_spec_mode_disabled": "저사양 모드 비활성화됨",
|
||||
"appx_low_spec_mode_enabled": "저사양 모드 활성화됨",
|
||||
"appx_miner_stopped_prefix": "채굴기 중지됨: ",
|
||||
"appx_miner_stopped_unexpectedly": "채굴기가 예기치 않게 중지되었습니다.",
|
||||
"appx_n_min_n_sec": "%d분 %d초",
|
||||
"appx_n_seconds": "%d초",
|
||||
"appx_no_bundled_daemon_to_install": "이 빌드에는 설치할 번들 데몬이 없습니다",
|
||||
"appx_no_embedded_daemon_to_install": "이 빌드에는 설치할 내장 데몬이 없습니다",
|
||||
"appx_node_busy_restarting": "노드가 재시작 중입니다 — 잠시 후 다시 시도하세요.",
|
||||
"appx_node_rebuilding_witness_cache": "노드가 위트니스 캐시를 재구성하는 중입니다",
|
||||
"appx_not_next_word": " — 다음 단어가 아닙니다",
|
||||
"appx_payment_request_loaded": "결제 요청을 불러왔습니다",
|
||||
"appx_pool_miner_connected_and_hashing": "풀 채굴기가 연결되어 해싱 중입니다.",
|
||||
"appx_progress_n_of_n": "진행 상황: %d / %d",
|
||||
"appx_rebuilding_sapling_note_witnesses": "Sapling 노트 위트니스 재구성 중…",
|
||||
"appx_rebuilding_witness_cache_blocks_left": "위트니스 캐시 재구성 중 %.0f%% — %d개 블록 남음",
|
||||
"appx_rebuilding_witness_cache_pct": "위트니스 캐시 재구성 중 %.0f%%",
|
||||
"appx_recovery_phrase_word_count": "복구 문구는 24개 단어여야 합니다 — 현재 %d개입니다.",
|
||||
"appx_restarting_daemon_rescan_flag": "-rescan 플래그로 데몬을 다시 시작하는 중...",
|
||||
"appx_restarting_daemon_zapwallettxes": "-zapwallettxes=2로 데몬을 다시 시작하는 중(지갑 복구)...",
|
||||
"appx_restoring_your_wallet": "지갑을 복구하는 중…",
|
||||
"appx_seed_backup_warning": "이 24개 단어는 지갑을 복구할 수 있는 유일한 방법입니다. 순서대로 적어 오프라인에 보관하고 절대 공유하지 마세요. 잃어버리면 자금을 영원히 되찾을 수 없습니다.",
|
||||
"appx_seed_not_backed_up_warning": "시드를 아직 백업하지 않았습니다 — 자금을 잃을 수 있습니다. 그래도 건너뛰시겠습니까?",
|
||||
"appx_sending_stop_command_to_daemon": "데몬에 중지 명령을 보내는 중...",
|
||||
"appx_setting_initial_sapling_witnesses": "초기 Sapling 위트니스 설정 중 %.0f%%",
|
||||
"appx_shutdown_complete": "종료 완료",
|
||||
"appx_simple_background_disabled": "단순 배경 비활성화됨",
|
||||
"appx_simple_background_enabled": "단순 배경 활성화됨",
|
||||
"appx_skip": "건너뛰기",
|
||||
"appx_skip_anyway": "그래도 건너뛰기",
|
||||
"appx_still_status_prefix": "아직 \"",
|
||||
"appx_still_status_suffix": "\" 상태입니다 — 지금 강제 종료하면 체인 데이터가 손상될 수 있습니다.",
|
||||
"appx_stop_anyway_and_quit": "그래도 중지하고 종료",
|
||||
"appx_stopping_daemon_deleting_blockchain": "데몬을 중지하고 블록체인 데이터를 삭제하는 중...",
|
||||
"appx_stopping_node_discards_rebuild": "지금 노드를 중지하면 진행 중인 재구성이 취소되며, 다음에 지갑을 열 때 다시 시작됩니다(몇 분 소요). 대신 노드를 계속 실행할 수 있습니다.",
|
||||
"appx_stopping_pool_miner": "풀 채굴기를 중지하는 중...",
|
||||
"appx_syncing_pct_block_n_of_n": "동기화 중 %.1f%% — 블록 %d / %d",
|
||||
"appx_tap_words_in_order": "저장한 내용을 확인하도록 단어를 올바른 순서대로 탭하세요.",
|
||||
"appx_theme_effects_disabled": "테마 효과 비활성화됨",
|
||||
"appx_theme_effects_enabled": "테마 효과 활성화됨",
|
||||
"appx_theme_prefix": "테마: ",
|
||||
"appx_use_settings_restart_daemon_hint": "설정 > 데몬 다시 시작을 사용해 다시 시도하세요",
|
||||
"appx_waiting_for_daemon_to_encrypt_wallet": "데몬이 지갑을 암호화하기를 기다리는 중...",
|
||||
"appx_wallet_created_and_backed_up": "지갑이 생성되고 백업되었습니다.",
|
||||
"appx_wallet_open_failed_prefix": "지갑 열기 실패: ",
|
||||
"auto_shield": "채굴 자동 차폐",
|
||||
"autoshield_by_node": "노드가 채굴한 코인베이스를 자동으로 보호 처리합니다.",
|
||||
"autoshield_off_backup_seed": "채굴한 코인베이스가 아직 자동으로 보호 처리되지 않고 있습니다. 노드가 안전하게 이를 활성화할 수 있도록 시드 문구를 백업하세요 (설정 → 백업 & 데이터).",
|
||||
"autoshield_off_generic": "현재 노드가 채굴한 코인베이스를 자동으로 보호 처리하지 않고 있습니다.",
|
||||
"av_intro": "채굴 소프트웨어는 종종 잠재적으로 원치 않는 항목으로 표시됩니다. 풀 채굴을 활성화하려면 다음 단계를 따르세요:",
|
||||
"av_open_security": "Windows 보안 열기",
|
||||
"av_step1": "1단계: 제외 추가",
|
||||
@@ -100,6 +178,17 @@
|
||||
"balance_shielded_fmt": "차폐: %.8f",
|
||||
"balance_syncing_pct": "동기화 중 %.1f%%",
|
||||
"balance_transparent_fmt": "투명: %.8f",
|
||||
"baltab_market": "시세",
|
||||
"baltab_market_price_4dp": "시세: $%.4f",
|
||||
"baltab_market_price_8dp": "시세: $%.8f",
|
||||
"baltab_pct_change_24h": "%s%.1f%% 24시간",
|
||||
"baltab_pct_of_total_zaddr": "전체의 %.0f%% · Z-addr %d개",
|
||||
"baltab_shielded": "실드됨",
|
||||
"baltab_shielded_amount": "실드됨 %.8f",
|
||||
"baltab_t_addresses_count": "T-address %d개",
|
||||
"baltab_total_balance": "총 잔액",
|
||||
"baltab_transparent": "투명",
|
||||
"baltab_transparent_amount": "투명 %.8f",
|
||||
"ban": "차단",
|
||||
"banned_peers": "차단된 피어",
|
||||
"block": "블록",
|
||||
@@ -107,6 +196,12 @@
|
||||
"block_click_copy": "복사하려면 클릭",
|
||||
"block_click_next": "클릭하여 다음 블록 보기",
|
||||
"block_click_prev": "클릭하여 이전 블록 보기",
|
||||
"block_db_reindex_body": "이 현상은 보통 데몬 업데이트로 디스크 저장 형식이 바뀌었거나, 블록 색인이 손상된 경우에 발생합니다. 지갑과 코인은 안전합니다 — 노드가 체인을 불러오지 못할 뿐이라 잔액이 0으로 표시됩니다.\n\n재구축은 기존 블록 파일을 다시 읽어 들이며 시간이 다소 걸릴 수 있습니다(지갑도 다시 스캔합니다). 아무것도 다운로드하지 않습니다.",
|
||||
"block_db_reindex_confirm": "블록 데이터베이스 재구축",
|
||||
"block_db_reindex_notify": "노드가 블록 데이터베이스를 읽을 수 없습니다(흔히 데몬 업데이트 후 발생). 잔액을 복구하려면 재구축하세요 — 안내창을 확인하거나 설정 › 노드로 이동하세요.",
|
||||
"block_db_reindex_started": "블록에서 블록 데이터베이스를 재구축하는 중입니다 — 시간이 다소 걸릴 수 있습니다.",
|
||||
"block_db_reindex_title": "블록 데이터베이스를 재구축할까요?",
|
||||
"block_db_reindex_warn": "노드가 블록 데이터베이스를 읽을 수 없습니다.",
|
||||
"block_explorer": "블록 탐색기",
|
||||
"block_get_info": "블록 정보 조회",
|
||||
"block_hash": "블록 해시:",
|
||||
@@ -154,6 +249,10 @@
|
||||
"chat_accent_theme": "테마",
|
||||
"chat_add_contact": "연락처 추가",
|
||||
"chat_awaiting_key": "답장 대기 중",
|
||||
"chat_blocked_desc": "차단된 대화는 삭제되며, 차단을 해제하기 전까지 기존 및 새 메시지가 모두 무시됩니다. 차단을 해제하면 체인에서 대화를 다시 가져옵니다.",
|
||||
"chat_blocked_manage": "차단됨",
|
||||
"chat_blocked_title": "차단된 대화",
|
||||
"chat_blocked_toast": "대화를 삭제하고 차단했습니다",
|
||||
"chat_bubble_minimal": "미니멀",
|
||||
"chat_bubble_rounded": "둥근",
|
||||
"chat_bubble_square": "각진",
|
||||
@@ -166,6 +265,16 @@
|
||||
"chat_contact_added": "연락처 추가됨 — 연락처에서 이름을 변경하세요",
|
||||
"chat_contact_request": "연락 요청",
|
||||
"chat_copy_address_tip": "클릭하여 주소 복사",
|
||||
"chat_delete": "대화 삭제",
|
||||
"chat_delete_block": "삭제 및 차단",
|
||||
"chat_delete_body_prefix": "다음 상대와의 대화 로컬 사본을 삭제할까요: ",
|
||||
"chat_delete_body_suffix": "?",
|
||||
"chat_delete_confirm": "삭제",
|
||||
"chat_delete_failed": "대화를 삭제하지 못했습니다 — 변경된 내용이 없습니다.",
|
||||
"chat_delete_local_note": "이 기기에만 영향을 줍니다 — 메시지는 블록체인에 남아 있고 상대방도 자신의 사본을 계속 보관합니다.",
|
||||
"chat_delete_revive_note": "\"삭제\"는 이 기기의 기록만 지웁니다. 상대방이 다시 메시지를 보내면 대화가 되살아납니다.",
|
||||
"chat_delete_title": "대화를 삭제할까요?",
|
||||
"chat_deleted_toast": "대화를 삭제했습니다",
|
||||
"chat_density_comfortable": "편안하게",
|
||||
"chat_density_compact": "촘촘하게",
|
||||
"chat_emoji_color": "컬러",
|
||||
@@ -189,6 +298,7 @@
|
||||
"chat_new_button": "새 채팅",
|
||||
"chat_new_message": "메시지",
|
||||
"chat_new_message_toast": "새 암호화 채팅 메시지",
|
||||
"chat_new_needs_zaddr": "채팅에는 보호 (z) 주소가 필요합니다 — 투명 (t) 주소는 암호화된 메시지를 받을 수 없습니다.",
|
||||
"chat_new_send": "요청 보내기",
|
||||
"chat_new_title": "새 채팅",
|
||||
"chat_new_zaddr": "받는 사람 z-주소",
|
||||
@@ -234,6 +344,8 @@
|
||||
"chat_ts_24h": "24시간",
|
||||
"chat_ts_global": "전역 설정 따르기",
|
||||
"chat_ts_global_short": "전역",
|
||||
"chat_unblock": "차단 해제",
|
||||
"chat_unblocked_toast": "대화 차단을 해제했습니다",
|
||||
"chat_unhide": "다시 표시",
|
||||
"chat_unmute": "음소거 해제",
|
||||
"chat_verify_key": "신원 키 — 비교하여 확인",
|
||||
@@ -286,6 +398,7 @@
|
||||
"console": "콘솔",
|
||||
"console_accents": "색상 강조",
|
||||
"console_app": "앱",
|
||||
"console_auto_focus": "열 때 입력창에 포커스",
|
||||
"console_auto_scroll": "자동 스크롤",
|
||||
"console_available_commands": "사용 가능한 명령어:",
|
||||
"console_backend_reference": "백엔드 명령어 참조",
|
||||
@@ -380,11 +493,15 @@
|
||||
"console_tab_completion": "Tab으로 자동 완성",
|
||||
"console_text_colors": "텍스트 색상",
|
||||
"console_toggle_accents": "줄 색상 강조 전환",
|
||||
"console_toggle_auto_focus": "콘솔 탭을 열 때 명령 입력창에 커서를 놓습니다",
|
||||
"console_toggle_text_color": "줄 텍스트 색상 전환",
|
||||
"console_type_help": "'help'를 입력하여 사용 가능한 명령어 보기",
|
||||
"console_welcome": "ObsidianDragon 콘솔에 오신 것을 환영합니다",
|
||||
"console_zoom_in": "확대",
|
||||
"console_zoom_out": "축소",
|
||||
"consolidate_desc": "여러 개의 작은 입력을 하나의 보호된 노트로 합칩니다. 노트 수가 줄면 지갑 파일이 작아지고 프라이버시도 향상됩니다.",
|
||||
"consolidate_funds_btn": "통합",
|
||||
"consolidate_title": "자금 통합",
|
||||
"contact_avatar": "아바타",
|
||||
"contact_avatar_bad_image": "그 이미지를 불러올 수 없습니다.",
|
||||
"contact_avatar_badge": "배지",
|
||||
@@ -414,6 +531,7 @@
|
||||
"contacts_shape_square": "사각형",
|
||||
"contacts_shape_tab": "왼쪽 탭",
|
||||
"copied": "복사됨!",
|
||||
"copied_to_clipboard": "클립보드에 복사됨",
|
||||
"copy": "복사",
|
||||
"copy_address": "전체 주소 복사",
|
||||
"copy_error": "오류 복사",
|
||||
@@ -429,9 +547,13 @@
|
||||
"daemon_maintenance_label": "유지 관리",
|
||||
"daemon_none_bundled": "이 빌드에 없음",
|
||||
"daemon_not_installed": "설치되지 않음",
|
||||
"daemon_status_diff": "버전이 다름",
|
||||
"daemon_status_differ": "설치된 바이너리가 번들 버전과 다릅니다.",
|
||||
"daemon_status_match": "설치된 바이너리가 번들 버전과 일치합니다.",
|
||||
"daemon_status_missing": "설치된 데몬 없음 — 번들 버전을 설치하세요.",
|
||||
"daemon_status_none": "설치되지 않음",
|
||||
"daemon_status_ok": "최신 상태",
|
||||
"daemon_update_active_tt": "현재 설치되어(활성화되어) 있는 노드",
|
||||
"daemon_update_available": "새 노드 버전이 있습니다",
|
||||
"daemon_update_body": "이 지갑 빌드에는 현재 설치된 것보다 새로운 DragonX 노드가 번들로 포함되어 있습니다. 업데이트하면 설치된 dragonxd(및 dragonx-cli/dragonx-tx)가 교체된 후, 새 버전이 적용되도록 노드를 중지했다가 다시 시작합니다. 새 노드는 기존 노드에 없는 기능(예: 시드 문구 지원)을 추가할 수 있으므로 권장합니다.",
|
||||
"daemon_update_browse": "모든 릴리스 보기…",
|
||||
@@ -441,19 +563,28 @@
|
||||
"daemon_update_download_install": "다운로드 및 설치",
|
||||
"daemon_update_downloading": "다운로드 중…",
|
||||
"daemon_update_failed": "업데이트 실패",
|
||||
"daemon_update_install_this": "이 버전 설치",
|
||||
"daemon_update_installed": "설치됨:",
|
||||
"daemon_update_installed_ok": "노드가 설치되었습니다",
|
||||
"daemon_update_installing": "설치 중…",
|
||||
"daemon_update_keep": "현재 버전 유지",
|
||||
"daemon_update_later": "나중에",
|
||||
"daemon_update_latest": "최신:",
|
||||
"daemon_update_latest_badge": "최신",
|
||||
"daemon_update_latest_tt": "사용 가능한 최신 버전",
|
||||
"daemon_update_loading": "릴리스 불러오는 중…",
|
||||
"daemon_update_no_notes": "이 버전에는 릴리스 노트가 없습니다.",
|
||||
"daemon_update_now": "지금 업데이트",
|
||||
"daemon_update_prerelease_tt": "사전 릴리스 / 테스트 빌드",
|
||||
"daemon_update_prompt_title": "노드 데몬을 업데이트하시겠습니까?",
|
||||
"daemon_update_reinstall": "다시 설치",
|
||||
"daemon_update_released": "출시일:",
|
||||
"daemon_update_restart_note": "새 버전을 실행하려면 데몬을 재시작하세요.",
|
||||
"daemon_update_restart_now": "지금 데몬 재시작",
|
||||
"daemon_update_safe": "지갑, 키, 블록체인 데이터는 그대로이며 — 데몬 프로그램 파일만 교체됩니다. 커스텀 노드를 일부러 실행 중이라면 현재 버전 유지를 선택하세요.",
|
||||
"daemon_update_status_current": "이 버전이 설치되어 있습니다",
|
||||
"daemon_update_status_latest": "최신 버전",
|
||||
"daemon_update_status_older": "설치된 버전보다 이전 버전입니다",
|
||||
"daemon_update_title": "노드 데몬을 업데이트할까요?",
|
||||
"daemon_update_unavailable_body": "이 플랫폼에 사용할 수 있는 노드 빌드가 없습니다.",
|
||||
"daemon_update_unavailable_title": "노드 업데이트를 사용할 수 없음",
|
||||
@@ -469,6 +600,13 @@
|
||||
"data_stale_tooltip": "잔액이 오래되었을 수 있습니다 — 지갑이 최근에 업데이트를 받지 못했습니다. 노드 연결을 확인하세요.",
|
||||
"date": "날짜",
|
||||
"date_label": "날짜:",
|
||||
"debug_gate_confirm": "디버그 옵션 표시",
|
||||
"debug_gate_incorrect": "PIN 또는 암호가 올바르지 않습니다.",
|
||||
"debug_gate_pass_prompt": "계속하려면 지갑 암호를 입력하세요:",
|
||||
"debug_gate_pin_prompt": "계속하려면 잠금 해제 PIN을 입력하세요:",
|
||||
"debug_gate_title": "디버그 옵션을 표시할까요?",
|
||||
"debug_gate_verifying": "확인 중…",
|
||||
"debug_gate_warning": "이 옵션들은 고급 진단 및 노드 튜닝 옵션입니다. 민감한 정보를 노출하거나 노드 동작 방식을 변경할 수 있습니다. 무엇을 하는지 정확히 알고 있을 때만 계속하세요.",
|
||||
"debug_logging": "디버그 로깅",
|
||||
"decrypt_desc": "지갑을 내보내고 데몬을 새 암호화되지 않은 지갑으로 다시 시작한 후 모든 키를 다시 가져옵니다. 지갑 크기에 따라 몇 분 정도 걸릴 수 있습니다.",
|
||||
"decrypt_error_title": "복호화 실패",
|
||||
@@ -491,6 +629,7 @@
|
||||
"disconnected": "연결 끊김",
|
||||
"dismiss": "닫기",
|
||||
"display": "디스플레이",
|
||||
"done": "완료",
|
||||
"download": "다운로드",
|
||||
"download_bootstrap": "부트스트랩 다운로드",
|
||||
"dragonx_green": "DragonX(그린)",
|
||||
@@ -544,6 +683,7 @@
|
||||
"explorer_tx_outputs": "출력",
|
||||
"explorer_tx_size": "크기",
|
||||
"explorer_txid": "TxID:",
|
||||
"explorer_urls_hdr": "URL",
|
||||
"export": "내보내기",
|
||||
"export_csv": "CSV 내보내기",
|
||||
"export_keys_btn": "키 내보내기",
|
||||
@@ -571,6 +711,151 @@
|
||||
"export_viewing_key": "조회 키 내보내기",
|
||||
"failed_create_shielded": "차폐 주소 생성 실패",
|
||||
"failed_create_transparent": "투명 주소 생성 실패",
|
||||
"faq": "FAQ",
|
||||
"faq_d_mgmt_1_a": "내장 노드는 지갑과 함께 자동으로 시작되고 적절한 시점에 중지되므로, 보통 직접 관리할 필요가 없습니다. 고급 제어 기능(데몬 재시작 포함)은 설정 > 노드 및 보안에 있습니다.",
|
||||
"faq_d_mgmt_1_q": "노드를 시작하거나 중지하려면 어떻게 하나요?",
|
||||
"faq_d_mgmt_2_a": "직접 실행한 노드에 지갑을 연결한 경우, 이 옵션은 지갑을 닫을 때 해당 노드도 함께 종료할지를 결정합니다. 지갑을 닫은 후에도 노드를 계속 실행하고 싶다면 꺼두세요.",
|
||||
"faq_d_mgmt_2_q": "'종료 시 외부 데몬 닫기'는 무엇을 하나요?",
|
||||
"faq_d_mgmt_3_a": "노드와 지갑 데이터는 사용자 프로필 아래 DragonX 데이터 디렉터리에 저장됩니다(예: Windows에서는 AppData, Linux/macOS에서는 홈 폴더). 블록체인이 가장 많은 용량을 차지하므로, 성장할 만큼 충분한 여유 디스크 공간을 확보하세요.",
|
||||
"faq_d_mgmt_3_q": "블록체인과 지갑 데이터는 어디에 저장되나요?",
|
||||
"faq_d_mgmt_title": "노드 관리",
|
||||
"faq_d_mine_1_a": "마이닝 탭에서 CPU로 채굴할 수 있습니다. DragonX는 일반 프로세서용으로 설계된 RandomX 알고리즘을 사용합니다. 스레드 수를 설정하고 시작하면 보상이 지갑에 쌓입니다.",
|
||||
"faq_d_mine_1_q": "DragonX는 어떻게 채굴하나요?",
|
||||
"faq_d_mine_2_a": "솔로 채굴은 보상을 바로 받지만 해시레이트가 크지 않으면 드뭅니다. 풀 채굴은 다른 사람들과 작업을 나누어 더 작지만 꾸준한 보상을 받습니다. 마이닝 탭은 두 방식 모두 지원합니다.",
|
||||
"faq_d_mine_2_q": "솔로 채굴과 풀 채굴, 무엇을 선택해야 하나요?",
|
||||
"faq_d_mine_3_a": "마이닝 탭의 풀 섹션에 있는 '마이너 업데이트…' 버튼은 최신 최적화된 DragonX 마이너 빌드를 다운로드, 검증, 설치합니다. 노드 업데이트와 마찬가지로 각 다운로드는 설치 전 체크섬과 서명 검증을 거칩니다.",
|
||||
"faq_d_mine_3_q": "마이너는 어떻게 업데이트하나요?",
|
||||
"faq_d_mine_4_a": "현재 노드는 내장 stratum 서버를 실행할 수 있어 다른 채굴자가 이 컴퓨터를 가리킬 수 있습니다. 설정 > 노드 및 보안에 관련 토글이 있습니다. 기본적으로 로컬에서만 대기하며, 다른 컴퓨터에 노출하려면 직접 열어야 합니다.",
|
||||
"faq_d_mine_4_q": "마이닝 풀을 호스팅할 수 있나요?",
|
||||
"faq_d_mine_title": "채굴 및 풀",
|
||||
"faq_d_net_1_a": "실행 직후에는 노드가 피어를 찾는 데 잠시 시간이 걸리므로 '피어 0'이 잠깐 나타나는 것은 정상입니다. 계속되면 인터넷 연결과 방화벽이 노드를 차단하고 있지 않은지 확인하세요. 노드는 내장된 시드를 통해 자동으로 피어를 찾습니다.",
|
||||
"faq_d_net_1_q": "지갑에 피어나 연결이 표시되지 않습니다 — 무엇이 문제인가요?",
|
||||
"faq_d_net_2_a": "예 — DragonX 노드는 TLS를 통해 서로 통신하므로 피어 연결이 암호화됩니다. 지갑도 자신의 노드와 안전한 로컬 채널로 통신합니다.",
|
||||
"faq_d_net_2_q": "네트워크 연결이 암호화되어 있나요?",
|
||||
"faq_d_net_title": "피어 및 네트워크",
|
||||
"faq_d_node_1_a": "데몬 dragonxd는 DragonX 풀 노드입니다. 전체 블록체인을 다운로드하고 검증하며, 트랜잭션을 네트워크에 중계하고, 지갑의 요청에 응답합니다. 자신의 노드를 운영하면 다른 서버를 신뢰할 필요가 없습니다.",
|
||||
"faq_d_node_1_q": "데몬(dragonxd)이란 무엇인가요?",
|
||||
"faq_d_node_2_a": "기본적으로 지갑은 번들된 자체 노드를 실행하고 관리합니다(내장형) — 별도로 할 일은 없습니다. 이미 직접 dragonxd를 실행 중이라면 지갑이 그 외부 노드에 연결할 수도 있습니다. 지갑은 현재 상황을 감지해 그에 맞게 동작합니다.",
|
||||
"faq_d_node_2_q": "내장 노드와 외부 노드의 차이는 무엇인가요?",
|
||||
"faq_d_node_3_a": "풀 노드는 블록체인의 모든 규칙을 독립적으로 검증하므로 지갑이 제3자가 아닌 수학을 신뢰하게 됩니다. 네트워크도 강화합니다. 대신 디스크 공간과 초기 동기화가 필요합니다.",
|
||||
"faq_d_node_3_q": "왜 굳이 풀 노드를 운영해야 하나요?",
|
||||
"faq_d_node_title": "풀 노드",
|
||||
"faq_d_perf_1_a": "블록체인이 가장 큰 부분이며 시간이 지나면서 커지므로 여유 공간을 몇 기가바이트 남겨두세요. 노드는 속도를 위해 데이터베이스 캐시를 사용하며, 번들된 기본값은 일반적인 기기에 맞춰져 있습니다. 고급 사용자는 설정 파일(DRAGONX.conf)에서 dbcache를 수정해 노드의 데이터베이스 캐시를 조정할 수 있습니다.",
|
||||
"faq_d_perf_1_q": "노드가 디스크를 얼마나 사용하나요? 메모리를 조정할 수 있나요?",
|
||||
"faq_d_perf_2_a": "모든 블록을 처음부터 검증하는 대신 설정 마법사에서 제공되는 부트스트랩 다운로드를 활성화하면 최근의 검증된 체인 사본을 받아올 수 있습니다. 빠른 디스크(SSD)도 큰 도움이 됩니다.",
|
||||
"faq_d_perf_2_q": "초기 동기화를 더 빠르게 하려면 어떻게 하나요?",
|
||||
"faq_d_perf_title": "저장 공간 및 성능",
|
||||
"faq_d_sync_1_a": "실행 시 노드는 지갑에 서비스를 제공하기 전에 블록을 불러오고 검증합니다. '블록 처리 중 / 현재 체인 상태를 구성하기 위해 블록 적용 중' 화면이 바로 이 준비 과정입니다. 정상적인 동작이며, 노드가 완료되고 높이를 보고하면 지갑을 사용할 수 있게 됩니다.",
|
||||
"faq_d_sync_1_q": "시작 시 나타나는 '블록 처리 중'은 무슨 의미인가요?",
|
||||
"faq_d_sync_2_a": "보호 자금을 위해 노드는 비공개로 송금할 수 있게 해주는 암호학적 '증인(witness)'을 유지합니다. 일부 재시작이나 업데이트 후에는 최근 블록을 다시 스캔하여 이 캐시를 재구축합니다. 트랜잭션이 많은 지갑에서는 몇 분이 걸리고 추가 메모리를 사용할 수 있습니다.\n\n멈춘 것이 아니니 완료될 때까지 기다리세요. 도중에 강제 종료하면 다음 실행 시 재구축이 처음부터 다시 시작될 뿐입니다.",
|
||||
"faq_d_sync_2_q": "'증인 캐시 재구축 중'이란 무엇인가요?",
|
||||
"faq_d_sync_3_a": "체인 끝부분에 가까워지면 지갑을 스캔하는 작업(잔액과 노트 확인)이 노드와 동일한 내부 잠금을 두고 경쟁하게 되어, 대형 지갑에서는 마지막 블록 연결 속도가 느려질 수 있습니다. 최근 버전은 이 스캔을 제한해 노드가 따라잡을 수 있게 합니다. 가벼운 탭에 머물거나 그냥 기다리면 더 빨리 끝납니다.",
|
||||
"faq_d_sync_3_q": "거의 다 따라잡았는데 왜 동기화가 느린가요?",
|
||||
"faq_d_sync_4_a": "첫 동기화는 전체 체인을 다운로드하고 검증하며, 연결 상태와 디스크에 따라 시간이 걸릴 수 있습니다. 설정 마법사에서 부트스트랩 다운로드를 활성화하면 최근의 검증된 체인 데이터 사본을 받아와 크게 단축할 수 있습니다.",
|
||||
"faq_d_sync_4_q": "초기 동기화는 얼마나 걸리나요?",
|
||||
"faq_d_sync_title": "동기화 및 블록체인",
|
||||
"faq_d_trbl_1_a": "대개는 아닙니다. 대형 지갑에서는 노드가 보호 증인을 재구축하거나 블록을 적용하는 데 몇 분이 걸릴 수 있으며, 이 동안 멈춘 것처럼 보이거나 응답이 느릴 수 있습니다. 시간을 두고 기다려 주세요 — 디버그 로그(설정)를 보면 정상 작동 중이라면 꾸준한 진행이 보입니다.\n\n이 단계에서 강제 종료하지 마세요. 다음 실행 시 작업이 처음부터 다시 시작됩니다.",
|
||||
"faq_d_trbl_1_q": "노드가 '최적 체인 활성화 중'에서 멈춘 것 같습니다 — 멈춘 건가요?",
|
||||
"faq_d_trbl_2_a": "지갑 파일이 복구되었지만 키 데이터 일부가 없으면 노드가 새 주소를 생성하거나 자금을 보호할 수 없는 제한된 '저하 모드'로 열 수 있습니다. 가장 안전한 해결책은 시드 복구 문구로 새 지갑을 복원하는 것입니다.",
|
||||
"faq_d_trbl_2_q": "'저하 모드'란 무엇인가요?",
|
||||
"faq_d_trbl_3_a": "재스캔은 지갑이 체인을 다시 읽어 트랜잭션을 재발견하게 합니다(키를 가져온 후 유용). 재색인은 노드의 블록체인 데이터베이스를 재구축하며, 해당 데이터베이스가 손상되었거나 버전 변경 후 호환되지 않을 때만 필요합니다. 둘 다 시간이 걸릴 수 있으며, 설정 > 노드 및 보안에서 시작할 수 있습니다.",
|
||||
"faq_d_trbl_3_q": "언제 재색인이나 재스캔을 해야 하나요?",
|
||||
"faq_d_trbl_4_a": "증인 재구축이나 초기 동기화 같은 무거운 작업 중에는 메모리 사용량이 급증했다가 완료되면 다시 낮아집니다. 유휴 상태에서 계속 높은 메모리 사용은 이례적이며, 지갑을 재시작하면 해소됩니다. 메모리가 적은 기기에서는 다른 메모리 사용량이 많은 앱과 경쟁하지 않는지 확인하세요.",
|
||||
"faq_d_trbl_4_q": "노드가 메모리를 많이 사용하고 있습니다 — 정상인가요?",
|
||||
"faq_d_trbl_title": "문제 해결",
|
||||
"faq_d_upd_1_a": "설정 > 노드 및 보안 > 데몬 바이너리를 열고 '업데이트 확인…'을 사용하세요. 지갑이 최신 검증된 노드 빌드를 다운로드해 설치하며, 새 버전은 데몬이 다음에 시작될 때 적용됩니다.",
|
||||
"faq_d_upd_1_q": "노드는 어떻게 업데이트하나요?",
|
||||
"faq_d_upd_2_a": "예 — 업데이트 대화상자에 모든 릴리스가 나열되어 특정 버전이나 이전 빌드를 선택할 수 있습니다. 다운그레이드는 주의하세요. 이전 노드는 더 새로운 노드가 작성한 블록체인 데이터를 받아들이지 못해 재색인이 필요할 수 있습니다.",
|
||||
"faq_d_upd_2_q": "특정 노드 버전을 설치할 수 있나요?",
|
||||
"faq_d_upd_3_a": "다운로드된 모든 노드 아카이브는 설치 전에 체크섬과 암호화 서명으로 검증됩니다. 검증에 실패하면 지갑이 업데이트를 거부합니다.",
|
||||
"faq_d_upd_3_q": "업데이트는 안전한가요?",
|
||||
"faq_d_upd_title": "노드 업데이트",
|
||||
"faq_group_daemon": "데몬",
|
||||
"faq_group_wallet": "지갑",
|
||||
"faq_intro": "지갑과 DragonX 노드에 관한 답변입니다. 검색하거나 아래 주제별로 살펴보세요.",
|
||||
"faq_intro_lite": "Lite 지갑에 관한 답변입니다. 검색하거나 아래 주제별로 살펴보세요.",
|
||||
"faq_l_gs_1_a": "ObsidianDragonLite는 경량 DragonX 지갑입니다. 풀 노드를 실행하는 대신 DragonX lite 서버에 연결하므로 빠르게 시작되고 전체 블록체인을 다운로드하지 않습니다.\n\n일반 지갑과 마찬가지로 키, 잔액, 송금을 관리합니다 — 서버 연결 방식과 프라이버시 절충에 대해서는 아래의 'Lite 지갑' 섹션을 참고하세요.",
|
||||
"faq_l_gs_1_q": "ObsidianDragonLite란 무엇인가요?",
|
||||
"faq_l_lite_1_a": "Lite 지갑은 자체 풀 노드를 실행하는 대신 DragonX lite 서버와 통신합니다. 전체 블록체인을 직접 다운로드하거나 검증하지 않으므로 빠르게 시작되고 디스크와 대역폭 사용이 적습니다. 개인 키는 항상 기기에 남아 있으며 서버로 전송되지 않습니다.",
|
||||
"faq_l_lite_1_q": "Lite 지갑은 풀 지갑과 어떻게 다른가요?",
|
||||
"faq_l_lite_2_a": "설정을 열고 서버를 선택하세요. 언제든 서버를 전환할 수 있으며 지갑이 새 서버에서 다시 동기화합니다. 서버가 느리거나 연결되지 않으면 다른 서버를 시도해 보세요.",
|
||||
"faq_l_lite_2_q": "서버는 어떻게 선택하거나 변경하나요?",
|
||||
"faq_l_lite_3_a": "키는 절대 기기를 벗어나지 않으며 보호된 금액은 암호화된 상태로 유지됩니다. 다만 lite 서버는 지갑이 조회하는 주소와 IP 주소를 볼 수 있어 주소들을 서로 연결 지을 수 있습니다 — 편의성과 프라이버시를 맞바꾸는 셈입니다. 최고 수준의 프라이버시를 원한다면 체인을 직접 검증하고 서버에 주소를 노출하지 않는 풀 노드 지갑을 사용하세요.",
|
||||
"faq_l_lite_3_q": "Lite 지갑은 풀 노드만큼 프라이버시가 보장되나요?",
|
||||
"faq_l_lite_title": "라이트 지갑",
|
||||
"faq_l_sec_1_a": "설정 > 지갑으로 이동해 보안 섹션에서 암호를 설정하세요. 암호화는 디스크에 저장된 개인 키를 보호하므로, 파일에 접근할 수 있는 사람이라도 코인을 사용할 수 없습니다.\n\n강력한 암호를 선택하고 잊어버리지 마세요 — 암호 없이는 암호화된 지갑을 복구할 방법이 없습니다.",
|
||||
"faq_no_results": "결과가 없습니다. 다른 검색어를 시도해 보세요.",
|
||||
"faq_open_tooltip": "도움말 및 FAQ",
|
||||
"faq_search_hint": "FAQ 검색…",
|
||||
"faq_title": "도움말 및 FAQ",
|
||||
"faq_w_addr_1_a": "투명 주소(R 또는 t로 시작)는 대부분의 코인처럼 작동합니다. 금액과 주소가 블록체인에 공개됩니다.\n\n보호 주소(zs로 시작하는 z-주소)는 영지식 암호화를 사용합니다 — 금액, 발신자, 수신자가 체인상에서 암호화됩니다. 보호 주소에 보관된 잔액은 비공개입니다.",
|
||||
"faq_w_addr_1_q": "투명 주소와 보호 주소의 차이는 무엇인가요?",
|
||||
"faq_w_addr_2_a": "가능하면 항상 보호(z) 주소를 사용하세요 — 잔액과 결제 내역이 비공개로 유지됩니다. DragonX는 프라이버시 코인이며, 수신 자금의 기본값은 보호 주소입니다.\n\n상대방 서비스가 보호 주소를 처리할 수 없는 경우에만 투명 주소를 사용하세요.",
|
||||
"faq_w_addr_2_q": "어떤 주소 유형을 사용해야 하나요?",
|
||||
"faq_w_addr_3_a": "보호 주소는 재사용해도 괜찮으며 내역이 노출되지 않습니다. 투명 주소는 결제마다 새 주소를 사용하는 것이 프라이버시에 좋습니다. 수신 탭에서 새 주소를 만들 수 있습니다.",
|
||||
"faq_w_addr_3_q": "주소를 재사용해야 하나요, 아니면 새로 만들어야 하나요?",
|
||||
"faq_w_addr_4_a": "보호(shielding)는 코인을 투명 주소에서 보호 주소로 옮겨 비공개로 만드는 것입니다. 새로 채굴된 코인은 투명 상태로 도착하며 자동으로 보호 처리됩니다.\n\n지갑에서 수동으로 보호 처리할 수도 있습니다. 보호는 일반 트랜잭션의 일종이므로 완료까지 일반적인 확인 시간이 걸립니다.",
|
||||
"faq_w_addr_4_q": "'보호(shielding)'란 무엇인가요?",
|
||||
"faq_w_addr_title": "주소 및 프라이버시",
|
||||
"faq_w_bal_1_a": "지갑이 아직 동기화 중일 때는 잔액이 불완전하며 0으로 표시될 수 있습니다 — 아직 모든 트랜잭션을 스캔하지 않았기 때문입니다. 동기화가 끝나면 채워집니다.\n\n하단 상태 표시줄을 확인하세요. 연결 상태와 블록 높이가 표시됩니다. 동기화가 완료되면 잔액이 정확해집니다.",
|
||||
"faq_w_bal_1_q": "잔액이 0이거나 업데이트되지 않는 이유는 무엇인가요?",
|
||||
"faq_w_bal_2_a": "트랜잭션이 포함된 블록 위에 새 블록이 채굴될 때마다 확인이 하나씩 늘어납니다. 확인 수가 많을수록 결제가 더 확실하게 확정된 것입니다. 수신 자금은 첫 확인 후 사용할 수 있게 됩니다.",
|
||||
"faq_w_bal_2_q": "확인이란 무엇인가요?",
|
||||
"faq_w_bal_3_a": "총 잔액에는 아직 확인 중이거나 일시적으로 잠긴 자금(예: 보호 처리 중인 코인)이 포함됩니다. 사용 가능 잔액은 지금 바로 보낼 수 있는 금액입니다. 트랜잭션이 확인되면 두 값이 같아집니다.",
|
||||
"faq_w_bal_3_q": "총 잔액과 사용 가능 잔액의 차이는 무엇인가요?",
|
||||
"faq_w_bal_title": "잔액 및 동기화",
|
||||
"faq_w_chat_1_a": "채팅은 지갑에 내장된 암호화 온체인 메신저입니다. 메시지는 비공개 보호 메모 형태로 전송되므로 본인과 상대방만 읽을 수 있습니다.",
|
||||
"faq_w_chat_1_q": "채팅 기능이란 무엇인가요?",
|
||||
"faq_w_chat_2_a": "채팅 아이디는 지갑의 복구 문구에서 파생되므로 지갑과 함께 이동합니다 — 지갑을 복원하면 아이디도 함께 돌아옵니다. 별도의 계정이나 비밀번호를 만들 필요가 없습니다.",
|
||||
"faq_w_chat_2_q": "채팅 아이디는 어떻게 작동하나요?",
|
||||
"faq_w_chat_3_a": "연락처 탭에서 주소로 연락처를 추가할 수 있으며, 선택적으로 이름과 아바타를 지정할 수 있습니다. 연락처는 현재 지갑에만 비공개로 유지하거나 이 컴퓨터에서 여는 모든 지갑과 공유할 수 있습니다.",
|
||||
"faq_w_chat_3_q": "연락처는 어떻게 작동하나요?",
|
||||
"faq_w_chat_4_a": "대화를 열고 헤더의 아이콘을 사용하세요. 숨기기(눈 모양)는 목록에서 제거하지만 모든 메시지는 유지됩니다 — 새 메시지가 오면 다시 표시됩니다. 삭제(휴지통)는 대화상자를 엽니다. '삭제'는 이 기기의 내역만 지웁니다(상대방이 다시 메시지를 보내면 대화가 다시 나타납니다). '삭제 및 차단'은 상대방 목록 위의 '차단됨' 목록에서 차단을 해제할 때까지 향후 메시지도 막습니다. 이 모든 작업은 이 기기에서만 이루어집니다 — 메시지는 블록체인에 그대로 남아 있고 상대방은 자신의 사본을 유지하므로 '전송 취소'는 아닙니다.",
|
||||
"faq_w_chat_4_q": "대화를 숨기거나 삭제하거나 차단하려면 어떻게 하나요?",
|
||||
"faq_w_chat_title": "채팅 및 연락처",
|
||||
"faq_w_gs_1_a": "ObsidianDragon은 DragonX(DRGX)용 풀노드 지갑입니다. 코인을 관리하며, 데스크톱 빌드에서는 백그라운드에서 DragonX 풀노드(데몬)를 실행하여 지갑이 제3자 서버를 신뢰하는 대신 블록체인을 직접 검증합니다.\n\n지갑(키, 잔액, 전송)과 노드(블록체인, 네트워크)라는 두 부분이 함께 작동한다고 생각하면 됩니다. 여기 질문들도 같은 방식으로 나뉘어 있습니다 — 위의 지갑 및 데몬 탭을 참고하세요.",
|
||||
"faq_w_gs_1_q": "ObsidianDragon이란 무엇인가요?",
|
||||
"faq_w_gs_2_a": "처음 실행하면 설정 마법사가 자동으로 지갑을 만들어 줍니다. 새 지갑은 비밀 복구 문구(단어 목록)로 보호됩니다. 이 문구를 적어 오프라인에 안전하게 보관하세요 — 이 컴퓨터를 잃어버렸을 때 자금을 복원할 수 있는 유일한 방법입니다.\n\n설정 > 백업 및 데이터 > 시드 문구에서 언제든지 문구를 확인하거나 백업할 수 있습니다.",
|
||||
"faq_w_gs_2_q": "새 지갑은 어떻게 만드나요?",
|
||||
"faq_w_gs_3_a": "복구 문구가 있다면 시드 문구 복원 절차를 사용하세요. (풀노드 빌드에서는 실행 전에 기존 wallet.dat 파일을 지갑의 데이터 디렉터리에 복사할 수도 있습니다.)\n\n복원 후 지갑이 과거 트랜잭션을 찾기 위해 다시 스캔하므로, 처음에는 잔액과 내역이 표시되기까지 시간이 걸릴 수 있습니다.",
|
||||
"faq_w_gs_3_q": "이미 가지고 있는 지갑은 어떻게 복원하나요?",
|
||||
"faq_w_gs_4_a": "마법사에서는 모양/테마를 선택하고, 암호화와 PIN을 설정하며, 풀노드 빌드에서는 선택적으로 부트스트랩을 다운로드해 첫 동기화 속도를 높일 수 있습니다. 어떤 단계든 건너뛸 수 있으며 나중에 설정에서 모두 변경할 수 있습니다.",
|
||||
"faq_w_gs_4_q": "최초 실행 설정은 무엇을 하나요?",
|
||||
"faq_w_gs_title": "시작하기",
|
||||
"faq_w_sec_1_a": "설정 > 노드 및 보안으로 이동해 암호를 설정하세요(보안 섹션). 암호화는 디스크에 저장된 개인 키를 보호하므로, 파일에 접근할 수 있는 사람이라도 코인을 사용할 수 없습니다.\n\n강력한 암호를 선택하고 잊어버리지 마세요 — 암호 없이는 암호화된 지갑을 복구할 방법이 없습니다.",
|
||||
"faq_w_sec_1_q": "지갑을 어떻게 암호화하나요?",
|
||||
"faq_w_sec_2_a": "PIN은 자리를 비웠을 때 잔액과 작업이 노출되지 않도록 앱 화면을 잠급니다. 암호화 위에 추가된 편의용 잠금이며, 마법사 중이나 지갑을 암호화한 후 설정에서 설정할 수 있습니다.",
|
||||
"faq_w_sec_2_q": "PIN/잠금 화면이란 무엇인가요?",
|
||||
"faq_w_sec_3_a": "아니요. 지갑 암호화는 우회할 수 없습니다. 시드 복구 문구가 있다면 새 지갑으로 복원하고 새 암호를 설정할 수 있습니다. 암호와 시드 문구 둘 다 없으면 자금을 복구할 수 없습니다.",
|
||||
"faq_w_sec_3_q": "암호를 잊어버렸어요 — 복구할 수 있나요?",
|
||||
"faq_w_sec_title": "보안 및 암호화",
|
||||
"faq_w_seed_1_a": "설정 > 백업 및 데이터 > 시드 문구로 이동하세요. 단어를 순서대로 종이에 적어 안전한 오프라인 장소에 보관하세요. 이 문구를 가진 사람은 누구든 코인을 사용할 수 있으므로 사진, 이메일, 클라우드 메모에는 절대 저장하지 마세요.",
|
||||
"faq_w_seed_1_q": "복구 문구는 어떻게 백업하나요?",
|
||||
"faq_w_seed_2_a": "이전(레거시) 지갑은 시드 기반이 아니었습니다. 지갑은 레거시 지갑을 최신 시드 기반 지갑으로 이전할 수 있습니다. 새 시드 지갑을 만들고 자금을 그곳으로 쓸어 담습니다. 설정 > 백업 및 데이터에서 '시드 지갑으로 이전…'을 찾으세요. 이 기능은 현재 버전의 데몬이 필요합니다.",
|
||||
"faq_w_seed_2_q": "제 지갑에는 복구 문구가 없어요 — 추가할 수 있나요?",
|
||||
"faq_w_seed_3_a": "지갑을 설정할 때 시드 복원 절차를 사용해 단어를 순서대로 입력하세요. 그러면 지갑이 다시 스캔해 잔액과 내역을 재구성하며, 처음에는 시간이 걸릴 수 있습니다.",
|
||||
"faq_w_seed_3_q": "복구 문구로 어떻게 복원하나요?",
|
||||
"faq_w_seed_4_a": "시드 문구는 기본 백업이며 모든 것을 복원하기에 충분합니다. 지갑 파일 사본은 라벨과 설정까지 보존하는 편리한 보조 백업입니다. 모든 백업은 오프라인에 비공개로 보관하세요.",
|
||||
"faq_w_seed_4_q": "wallet.dat도 백업해야 하나요?",
|
||||
"faq_w_seed_title": "시드 문구 및 백업",
|
||||
"faq_w_send_1_a": "전송 탭을 열고 받는 사람의 주소를 붙여넣은 다음 금액을 입력하고 확인하세요. 지갑이 암호화되어 있다면 트랜잭션을 위해 잠금 해제를 요청받습니다.\n\n보호 주소로의 전송은 비공개이며, 투명 주소로의 전송은 공개됩니다.",
|
||||
"faq_w_send_1_q": "자금은 어떻게 보내나요?",
|
||||
"faq_w_send_2_a": "DragonX 수수료는 매우 낮으며 자동으로 설정됩니다. 확인 전에 수수료가 표시되며, 보통 변경할 필요는 없습니다.",
|
||||
"faq_w_send_2_q": "어떤 수수료를 내나요?",
|
||||
"faq_w_send_3_a": "네 — 보호(z) 주소로 전송할 때 암호화된 메모를 첨부할 수 있습니다. 받는 사람만 읽을 수 있습니다. 투명 주소는 메모를 지원하지 않습니다.",
|
||||
"faq_w_send_3_q": "결제에 메시지를 첨부할 수 있나요?",
|
||||
"faq_w_send_4_a": "트랜잭션은 블록에 채굴되어 확인을 얻기 전까지 '대기 중'입니다. 보통 1~2분 정도 걸립니다. 보호 트랜잭션은 증명을 생성하기 위해 지갑이 동기화되어 있어야 합니다.\n\n전송이 유난히 오래 대기 중이라면 지갑이 연결되고 동기화되어 있는지 확인하세요(창 하단의 상태 표시줄 참고).",
|
||||
"faq_w_send_4_q": "결제를 보냈는데 대기 중이라고 나와요 — 왜 그런가요?",
|
||||
"faq_w_send_title": "송금 및 수신",
|
||||
"faq_w_set_1_a": "설정 > 화면에서 테마와 강조 색상을 선택하세요. 여러 밝은/어두운 스킨이 포함되어 있으며, 저사양 기기에서는 블러 같은 효과를 끌 수 있습니다.",
|
||||
"faq_w_set_1_q": "테마를 어떻게 바꾸나요?",
|
||||
"faq_w_set_2_a": "네 — 설정 > 화면에 언어 선택 항목이 있습니다. 지갑은 여러 언어로 번역되어 있으며, 아직 번역되지 않은 항목은 영어로 표시됩니다.",
|
||||
"faq_w_set_2_q": "언어를 바꿀 수 있나요?",
|
||||
"faq_w_set_3_a": "설정 > 화면의 글꼴 배율 옵션을 사용하세요. 전체 인터페이스 크기를 조절하며, 고해상도(HiDPI) 디스플레이에서 앱이 작게 표시될 때 유용합니다.",
|
||||
"faq_w_set_3_q": "글자가 너무 작거나(또는 커요) — 크기를 조절할 수 있나요?",
|
||||
"faq_w_set_4_a": "설정에서 가격 조회를 활성화하세요. 그러면 잔액과 함께 대략적인 법정화폐 가치가 표시되고, 작은 시세 차트도 볼 수 있습니다. 가격은 참고용일 뿐입니다.",
|
||||
"faq_w_set_4_q": "법정화폐 가격을 어떻게 표시하나요?",
|
||||
"faq_w_set_title": "설정 및 화면",
|
||||
"favorite_address": "즐겨찾기에 추가",
|
||||
"fee": "수수료",
|
||||
"fee_high": "높음",
|
||||
@@ -593,6 +878,63 @@
|
||||
"general": "일반",
|
||||
"generating": "생성 중",
|
||||
"go_to_receive": "수신으로 이동",
|
||||
"grpa_current_block_paren": "(현재: %d)",
|
||||
"grpa_days_ago": "%lld일 전",
|
||||
"grpa_dbg_addrman": "피어 주소 추적 및 관리",
|
||||
"grpa_dbg_alert": "경고 시스템 메시지",
|
||||
"grpa_dbg_bench": "작업 벤치마크 시간 측정",
|
||||
"grpa_dbg_coindb": "코인 데이터베이스 읽기/쓰기 작업",
|
||||
"grpa_dbg_db": "Berkeley DB 작업",
|
||||
"grpa_dbg_estimatefee": "수수료 추정 알고리즘",
|
||||
"grpa_dbg_http": "HTTP RPC 서버 활동",
|
||||
"grpa_dbg_libevent": "Libevent 네트워킹 라이브러리",
|
||||
"grpa_dbg_lock": "락 경합 디버깅",
|
||||
"grpa_dbg_mempool": "트랜잭션 메모리 풀 활동",
|
||||
"grpa_dbg_net": "네트워크 연결 및 메시지",
|
||||
"grpa_dbg_paymentdisclosure": "결제 공개 프로토콜",
|
||||
"grpa_dbg_pow": "작업 증명 채굴 활동",
|
||||
"grpa_dbg_proxy": "SOCKS5 프록시 연결",
|
||||
"grpa_dbg_prune": "블록 정리 작업",
|
||||
"grpa_dbg_rand": "난수 생성",
|
||||
"grpa_dbg_reindex": "블록체인 재색인 진행 상황",
|
||||
"grpa_dbg_rpc": "RPC 명령 처리",
|
||||
"grpa_dbg_selectcoins": "트랜잭션용 코인 선택",
|
||||
"grpa_dbg_tor": "Tor 연동 및 회로 정보",
|
||||
"grpa_dbg_zmq": "ZeroMQ 알림 시스템",
|
||||
"grpa_dbg_zrpc": "실드(z-addr) RPC 작업",
|
||||
"grpa_enter_private_key_to_import": "가져올 개인 키를 입력하세요.",
|
||||
"grpa_error_prefix": "오류: ",
|
||||
"grpa_hr_ago": "%lld시간 전",
|
||||
"grpa_invalid_response_from_daemon": "데몬으로부터 잘못된 응답",
|
||||
"grpa_invalid_suffix": " (유효하지 않음)",
|
||||
"grpa_min_ago": "%lld분 전",
|
||||
"grpa_sec_ago": "%lld초 전",
|
||||
"grpa_seed_demo_chat": "시드 데모 채팅",
|
||||
"grpa_showing_first_100_of": "... %d개 중 처음 100개 표시",
|
||||
"grpa_tab_about": "정보",
|
||||
"grpa_tab_appearance": "화면 표시",
|
||||
"grpa_tab_backup_data": "백업 및 데이터",
|
||||
"grpa_tab_chat": "채팅",
|
||||
"grpa_tab_explorer": "탐색기",
|
||||
"grpa_tab_node_security": "노드 및 보안",
|
||||
"grpa_tab_wallet": "지갑",
|
||||
"grpa_unexpected_getblockhash_result": "예기치 않은 getblockhash 결과",
|
||||
"grpb_copy": "복사",
|
||||
"grpb_max": "최대",
|
||||
"grpb_new_badge_suffix": " [신규]",
|
||||
"grpb_preview_msg_payment_through": "결제가 완료됐나요? 🙂",
|
||||
"grpb_preview_msg_sending_rest": "지금 나머지를 보낼게요 👍",
|
||||
"grpb_preview_msg_yep_confirmed": "네 — 방금 확인했어요 ✅",
|
||||
"grpb_selected_suffix": "\n(선택됨)",
|
||||
"grpb_tooltip_address_balance": "%s\n잔액: %.8f %s%s",
|
||||
"grpb_undo_clear": "지우기 취소",
|
||||
"grpc_benchmark_inconclusive": "벤치마크 결과가 불확실합니다: 해시레이트 샘플이 기록되지 않았습니다. 풀 연결을 확인한 후 다시 시도하세요.",
|
||||
"grpc_benchmark_takes_secs": "벤치마크는 약 %d초가 걸리며 채굴을 중단합니다. 시작하려면 다시 클릭하세요.",
|
||||
"grpc_bootstrap_failed": "부트스트랩 실패",
|
||||
"grpc_bootstrap_not_initialized": "부트스트랩이 초기화되지 않았습니다",
|
||||
"grpc_hashrate_fee": "%s 수수료 %s%%",
|
||||
"grpc_key_not_available": "이 주소에 대한 키를 사용할 수 없습니다",
|
||||
"grpc_na": "해당 없음",
|
||||
"height": "높이",
|
||||
"help": "도움말",
|
||||
"hidden_tag": " (숨김)",
|
||||
@@ -766,6 +1108,7 @@
|
||||
"loading": "로딩 중...",
|
||||
"loading_addresses": "주소 로딩 중...",
|
||||
"loading_stall_body": "데몬이 %.0f초 동안 초기화 중입니다. 업데이트 후나 첫 실행 시(블록 인덱스 로드 또는 재스캔)에는 정상일 수 있습니다. 준비되면 자동으로 연결됩니다.",
|
||||
"loading_stall_hint": "멈췄나요? 설정 → 데몬 재시작을 시도하거나 콘솔을 확인하세요.",
|
||||
"loading_stall_title": "예상보다 오래 걸리고 있습니다",
|
||||
"loading_transactions": "거래를 불러오는 중",
|
||||
"local_hashrate": "로컬 해시레이트",
|
||||
@@ -816,9 +1159,27 @@
|
||||
"memo_optional": "메모 (선택)",
|
||||
"memo_upper": "메모",
|
||||
"memo_z_only": "참고: 메모는 차폐 (z) 주소로 전송할 때만 사용할 수 있습니다",
|
||||
"merge_addr_created": "보호 주소가 생성되었습니다.",
|
||||
"merge_advanced": "고급",
|
||||
"merge_back": "뒤로",
|
||||
"merge_batch_fmt": "실행당 최대 %d개의 입력을 병합합니다 — 나머지를 마치려면 반복하세요.",
|
||||
"merge_confirm_btn": "확인",
|
||||
"merge_confirm_fmt": "~%s DRGX를 입력 %d개에서 %s(으)로 통합할까요?",
|
||||
"merge_create_zaddr": "보호 주소 생성",
|
||||
"merge_creating": "주소 생성 중…",
|
||||
"merge_description": "여러 UTXO를 단일 차폐 주소로 통합합니다. 지갑 크기를 줄이고 프라이버시를 향상시킵니다.",
|
||||
"merge_fee_hint": "이 거래에 대한 네트워크 수수료입니다.",
|
||||
"merge_funds": "자금 통합",
|
||||
"merge_max_inputs": "배치당 최대 입력 수",
|
||||
"merge_no_spendable": "아직 통합할 사용 가능한 입력이 없습니다.",
|
||||
"merge_progress": "통합 중… 몇 분 정도 걸릴 수 있습니다. 이 창을 닫아도 됩니다.",
|
||||
"merge_scope_fmt": "투명 입력 %d개 + 보호 입력 %d개 · 사용 가능 약 %s DRGX",
|
||||
"merge_scope_loading": "입력을 확인하는 중…",
|
||||
"merge_send_failed": "통합 실패: ",
|
||||
"merge_source": "통합",
|
||||
"merge_src_both": "둘 다",
|
||||
"merge_src_shielded": "보호",
|
||||
"merge_src_transparent": "투명",
|
||||
"merge_started": "통합 작업이 시작되었습니다",
|
||||
"merge_title": "주소로 통합",
|
||||
"mig_adopt": "이 지갑을 내 지갑으로 사용",
|
||||
@@ -1058,6 +1419,13 @@
|
||||
"peers_upper": "피어",
|
||||
"peers_version": "버전",
|
||||
"pending": "대기 중",
|
||||
"picker_dat_count": "이 폴더에 지갑 파일 %d개",
|
||||
"picker_dat_none": "이 폴더에 직접 있는 지갑 파일이 없습니다",
|
||||
"picker_empty": "이 폴더에는 하위 폴더나 지갑 파일이 없습니다.",
|
||||
"picker_home": "홈 폴더",
|
||||
"picker_select": "이 폴더 검색",
|
||||
"picker_title": "검색할 폴더 선택",
|
||||
"picker_up": "한 단계 위로",
|
||||
"pin_change_desc": "잠금 해제 PIN을 변경합니다. 현재 PIN과 새 PIN이 필요합니다.",
|
||||
"pin_confirm_new_label": "새 PIN 확인:",
|
||||
"pin_current_label": "현재 PIN:",
|
||||
@@ -1194,6 +1562,7 @@
|
||||
"save_z_transactions": "Z 거래를 거래 목록에 저장",
|
||||
"sb_auth_failed": "인증 실패 — rpcuser/rpcpassword를 확인하세요",
|
||||
"sb_block": "블록: %d",
|
||||
"sb_block_db_unreadable": "블록 데이터베이스를 읽을 수 없음 — 재구축 필요",
|
||||
"sb_building_witnesses": "증인 설정 중",
|
||||
"sb_building_witnesses_pct": "위트니스 재생성 중 %.0f%%",
|
||||
"sb_connecting_daemon": "dragonxd에 연결 중...",
|
||||
@@ -1209,6 +1578,7 @@
|
||||
"sb_dragonxd_stopped": "dragonxd 중지됨",
|
||||
"sb_dragonxd_stopping": "dragonxd 중지 중...",
|
||||
"sb_extracting_sapling": "Sapling 매개변수 추출 중...",
|
||||
"sb_finishing_repair": "지갑 복구 마무리 중 — 다시 스캔하는 중…",
|
||||
"sb_importing_keys": "키 가져오기 중",
|
||||
"sb_loading_config": "설정 불러오는 중...",
|
||||
"sb_mining_hs": "%.1f H/s",
|
||||
@@ -1230,6 +1600,7 @@
|
||||
"sb_waiting_config": "데몬 설정 대기 중...",
|
||||
"sb_waiting_daemon": "dragonxd 대기 중...",
|
||||
"sb_waiting_daemon_err": "dragonxd 대기 중 — %s",
|
||||
"sb_wallet_needs_recovery": "지갑 복구 가능",
|
||||
"sb_warming_up": "워밍업 중...",
|
||||
"sb_witness_cache": "증인 재구축 중",
|
||||
"scale_effects": "배율 및 효과",
|
||||
@@ -1239,6 +1610,45 @@
|
||||
"screenshot_sweep_full": "전체 UI 스윕",
|
||||
"search_icons": "아이콘 검색...",
|
||||
"search_placeholder": "검색...",
|
||||
"sec_changing_passphrase": "암호문 변경 중...",
|
||||
"sec_changing_pin": "PIN 변경 중...",
|
||||
"sec_couldnt_lock_wallet": "지갑을 잠글 수 없습니다 — 아직 잠금 해제 상태입니다. 데몬 연결을 확인하세요.",
|
||||
"sec_encrypted_backup_suffix": "\n암호화된 백업: wallet.dat.encrypted.bak",
|
||||
"sec_encrypting_wallet": "지갑 암호화 중...",
|
||||
"sec_encryption_did_not_complete": "지갑 암호화가 완료되지 않았습니다 — 지갑이 암호화되지 않은 상태입니다. 설정을 열어 암호화를 완료하세요.",
|
||||
"sec_encryption_failed_prefix": "암호화 실패: ",
|
||||
"sec_failed_prefix": "실패: ",
|
||||
"sec_failed_to_create_vault": "볼트 생성에 실패했습니다",
|
||||
"sec_importing_keys_rescanning": "키를 가져오고 블록체인을 다시 스캔하는 중 — 진행 중에도 지갑을 사용할 수 있습니다",
|
||||
"sec_incorrect_current_pin": "현재 PIN이 올바르지 않습니다",
|
||||
"sec_incorrect_passphrase_decrypt": "잘못된 암호문",
|
||||
"sec_incorrect_passphrase_pin_setup": "잘못된 암호문",
|
||||
"sec_incorrect_pin_remove": "잘못된 PIN",
|
||||
"sec_internal_error_change_pin": "내부 오류",
|
||||
"sec_internal_error_remove_pin": "내부 오류",
|
||||
"sec_mode_passphrase": " 암호문",
|
||||
"sec_not_connected_to_daemon": "데몬에 연결되지 않음",
|
||||
"sec_not_connected_to_daemon_pin": "데몬에 연결되지 않음",
|
||||
"sec_passphrase_changed_successfully": "암호문이 성공적으로 변경되었습니다",
|
||||
"sec_pin_changed_successfully": "PIN이 성공적으로 변경되었습니다",
|
||||
"sec_pin_removed": "PIN이 제거되었습니다",
|
||||
"sec_pin_set_successfully": "PIN이 성공적으로 설정되었습니다",
|
||||
"sec_restart_daemon_for_encryption": "암호화를 적용하려면 데몬을 다시 시작하세요.",
|
||||
"sec_too_many_attempts_wait": "시도 횟수가 너무 많습니다. %.0f초 기다리세요...",
|
||||
"sec_total_elapsed_fmt": "총 경과 시간: %d분 %02d초",
|
||||
"sec_unlock_button": "잠금 해제",
|
||||
"sec_unlock_failed_prefix": "잠금 해제 실패: ",
|
||||
"sec_unlocking_fmt": "잠금 해제 중%s",
|
||||
"sec_use_passphrase_instead": "대신 암호문 사용",
|
||||
"sec_use_pin_instead": "대신 PIN 사용",
|
||||
"sec_verifying_passphrase": "암호문 확인 중...",
|
||||
"sec_verifying_pin": "PIN 확인 중...",
|
||||
"sec_wallet_decrypted_all_keys_imported": "지갑 복호화 완료! 모든 키를 가져왔습니다.",
|
||||
"sec_wallet_encrypted_and_pin_set": "지갑 암호화 및 PIN 설정 완료",
|
||||
"sec_wallet_encrypted_but_pin_vault_failed": "지갑은 암호화되었으나 PIN 볼트 설정에 실패했습니다",
|
||||
"sec_wallet_encrypted_restarting_daemon": "지갑이 암호화되었습니다. 데몬을 다시 시작하는 중...",
|
||||
"sec_wallet_encrypted_successfully": "지갑이 성공적으로 암호화되었습니다",
|
||||
"sec_wallet_locked_title": "지갑 잠김",
|
||||
"security": "보안",
|
||||
"seed_backup_button": "시드 문구",
|
||||
"seed_backup_close": "닫기",
|
||||
@@ -1262,6 +1672,7 @@
|
||||
"send_amount": "금액",
|
||||
"send_amount_details": "금액 상세",
|
||||
"send_amount_upper": "금액",
|
||||
"send_available_note": "사용 가능",
|
||||
"send_cannot_send_now": "지금 보낼 수 없습니다 — 연결, 동기화, 사용 가능한 잔액을 확인하세요.",
|
||||
"send_clear_fields": "모든 양식 필드를 지우시겠습니까?",
|
||||
"send_contacts_button": "연락처에서 선택",
|
||||
@@ -1449,17 +1860,34 @@
|
||||
"status": "상태",
|
||||
"stop_external": "외부 데몬 중지",
|
||||
"stop_mining": "채굴 중지",
|
||||
"stratum_allowip_hint": "채굴자를 허용할 IP 또는 CIDR (비워두면 이 컴퓨터만 허용)",
|
||||
"stratum_expose_warn": "허용한 네트워크로 채굴 포트를 엽니다 — 신뢰할 수 있는 LAN에서만 사용하세요.",
|
||||
"stratum_host": "마이닝 풀 호스팅 (stratum)",
|
||||
"stratum_host_hint": "채굴자는 RandomX stratum 마이너로 포트 22769(RPC 포트 + 1000)에서 이 컴퓨터에 연결합니다. 적용하려면 데몬을 재시작하세요.",
|
||||
"stratum_host_section": "마이닝 풀 호스팅",
|
||||
"submitting_transaction": "거래 제출 중...",
|
||||
"success": "성공",
|
||||
"summary": "요약",
|
||||
"sweep_button": "쓸어담기",
|
||||
"sweep_caveat": "키를 가져와 모든 자금을 내 주소로 옮기는 거래에 서명합니다. 키는 잔액이 0인 상태로 지갑에 남습니다.",
|
||||
"sweep_current_theme_only": "현재 테마만",
|
||||
"sweep_dest_label": "쓸어담은 자금을 보낼 주소",
|
||||
"sweep_dest_new": "새 차폐 주소 (권장)",
|
||||
"sweep_done": "완료 — 자금을 내 주소로 쓸어담았습니다.",
|
||||
"sweep_to": "쓸어담은 주소:",
|
||||
"sweep_toggle": "내 지갑으로 쓸어담기 (키 보관 안 함)",
|
||||
"sweep_tx": "거래:",
|
||||
"swin_connection_failed": "연결 실패: ",
|
||||
"swin_connection_successful": "연결에 성공했습니다!\ndragonxd 버전: ",
|
||||
"swin_invalid_suffix": " (유효하지 않음)",
|
||||
"swin_no_history_file_found": "기록 파일을 찾을 수 없습니다",
|
||||
"swin_rescan_failed": "다시 스캔 실패: ",
|
||||
"swin_rescan_started_from_block": "다시 스캔이 시작된 블록 ",
|
||||
"swin_rescan_to": " ~ ",
|
||||
"swin_rpc_client_not_initialized": "RPC 클라이언트가 초기화되지 않았습니다",
|
||||
"swin_settings_saved": "설정이 저장되었습니다",
|
||||
"swin_theme_list_refreshed": "테마 목록을 새로고침했습니다",
|
||||
"swin_ztx_history_cleared": "Z-트랜잭션 기록이 삭제되었습니다",
|
||||
"switch_corrupt_body": "이 지갑이 손상된 것 같습니다. 노드가 열 수 없습니다. 백업에서 복원하거나 다시 만들거나 복구를 시도하세요.",
|
||||
"switch_corrupt_repair": "복구 시도(salvage)",
|
||||
"switch_progress_background": "백그라운드에서 계속",
|
||||
@@ -1620,6 +2048,8 @@
|
||||
"tt_simple_bg": "배경에 단순 그라데이션 사용\\n단축키: Ctrl+Up",
|
||||
"tt_simple_bg_alt": "테마 배경 이미지의 그라데이션 버전 사용\\n단축키: Ctrl+Up",
|
||||
"tt_stop_external": "이 지갑 외부에서 시작된\\n데몬에 연결할 때 적용",
|
||||
"tt_stratum_host": "이 노드에서 RandomX stratum 풀 서버를 실행하여 다른 RandomX 채굴자가 이 컴퓨터를 사용할 수 있게 합니다. v1.3.0 이상 노드와 적용을 위한 데몬 재시작이 필요합니다.",
|
||||
"tt_sweep_current_theme_only": "모든 테마를 순회하는 대신 활성 테마만 스윕합니다",
|
||||
"tt_test_conn": "데몬에 대한 RPC 연결 확인",
|
||||
"tt_theme_effects": "테마별 반짝임, 글로우, 색조 순환",
|
||||
"tt_theme_hotkey": "단축키: Ctrl+왼쪽/오른쪽으로 테마 전환",
|
||||
@@ -1628,6 +2058,7 @@
|
||||
"tt_ui_opacity": "카드 및 사이드바 불투명도 (100% = 완전 불투명, 낮을수록 더 투명)",
|
||||
"tt_validate": "DragonX 주소가 유효한지 확인",
|
||||
"tt_verbose": "콘솔 탭에 상세 연결 진단,\\n데몬 상태 및 포트 소유자 정보 기록",
|
||||
"tt_wallet_size_warn": "보호 지갑은 각 노트의 위트니스 데이터로 인해 커집니다 — 여러 노트를 하나의 주소로 병합하면 크기를 줄일 수 있습니다. 먼저 백업하세요.",
|
||||
"tt_wallets_button": "지갑 파일 목록을 보고 전환합니다",
|
||||
"tt_website": "DragonX 웹사이트 열기",
|
||||
"tt_window_opacity": "배경 불투명도 (낮을수록 = 창을 통해 바탕 화면이 보임)",
|
||||
@@ -1694,8 +2125,68 @@
|
||||
"view_on_explorer": "탐색기에서 보기",
|
||||
"waiting_for_daemon": "데몬 연결 대기 중...",
|
||||
"wallet": "지갑",
|
||||
"wallet_degraded_notify": "지갑이 기능 제한 모드로 열렸습니다. 기존 자금은 안전하며 사용할 수 있지만, 새 주소 생성과 보호 처리는 비활성화되어 있습니다. 시드 문구를 백업하고 이를 통해 복원하면 지갑을 완전히 복구할 수 있습니다.",
|
||||
"wallet_diagnostics_hdr": "진단",
|
||||
"wallet_empty": "지갑이 비어 있습니다",
|
||||
"wallet_empty_hint": "수신으로 전환하여 주소를 받고 자금 수신을 시작하세요.",
|
||||
"wallet_options_hdr": "옵션",
|
||||
"wallet_rebuild_failed": "재구축 결과가 유효한 지갑이 아니어서 아무것도 변경되지 않았습니다. 지갑은 그대로입니다.",
|
||||
"wallet_rebuild_install_failed": "재구축된 지갑을 설치하지 못했습니다. 기존 지갑은 그대로 유지되었습니다.",
|
||||
"wallet_rebuild_no_helper": "이 빌드에서는 지갑 재구축 도우미를 사용할 수 없습니다. 복원을 사용하거나 수동으로 재구축하세요.",
|
||||
"wallet_rebuild_no_source": "재구축할 읽을 수 있는 지갑을 찾지 못했습니다. 아무것도 변경되지 않았습니다.",
|
||||
"wallet_rebuild_ok": "지갑 데이터베이스가 재구축되었습니다 — 노드가 이를 불러오고 잔액을 다시 스캔하는 중입니다.",
|
||||
"wallet_rebuild_started": "지갑 파일을 복구하는 중입니다 — 이 창을 닫지 마세요.",
|
||||
"wallet_recovered_body": "삭제된 것은 없습니다. 원본 지갑은 날짜가 표시된 백업 파일로 컴퓨터에 그대로 저장되어 있으며, 아래의 모든 옵션은 파일을 복사하거나 이름을 바꿀 뿐 절대 삭제하지 않습니다. 키는 다시 생성되지 않고 다시 읽히기만 합니다.\n\n지금 불러온 복구본에는 최근 거래 몇 건이 누락되었을 수 있어, 아래 작업을 마치고 다시 스캔할 때까지 잔액이 약간 적게 보일 수 있습니다.",
|
||||
"wallet_recovered_dismiss": "나중에 — 복구본 계속 사용",
|
||||
"wallet_recovered_dismiss_sub": "파일은 변경되지 않습니다. 상태 표시줄에서 언제든 다시 열 수 있으며, 원본은 어떤 경우에도 안전하게 백업된 상태로 남습니다.",
|
||||
"wallet_recovered_notify": "지갑 파일을 복구해야 했습니다 — 원본은 안전하게 백업되었습니다. 앱을 열어 옵션을 확인하세요.",
|
||||
"wallet_recovered_open_folder": "파일 보기",
|
||||
"wallet_recovered_open_folder_sub": "지갑 데이터 폴더를 열어 백업 파일을 직접 확인할 수 있습니다 — 아무것도 변경되지 않습니다.",
|
||||
"wallet_recovered_rebuild": "자동으로 복구 (권장)",
|
||||
"wallet_recovered_rebuild_sub": "가장 온전한 지갑 파일에서 복구 가능한 모든 기록을 다시 읽어 깨끗한 새 파일로 작성한 다음 재시작합니다. 가장 철저한 방법이며 — 현재 파일은 어떤 경우에도 날짜가 표시된 백업으로 보관됩니다.",
|
||||
"wallet_recovered_restore": "대신 원본 파일 복원",
|
||||
"wallet_recovered_restore_sub": "가장 큰 원본 백업을 그대로 제자리에 넣은 다음 재스캔합니다 — 조금 더 빠르지만 그 파일이 담고 있던 만큼만 완전합니다. 현재 파일은 어떤 경우에도 날짜가 표시된 백업으로 보관됩니다.",
|
||||
"wallet_recovered_safety": "코인은 안전합니다.",
|
||||
"wallet_recovered_title": "지갑 파일에 간단한 복구가 필요합니다",
|
||||
"wallet_recovered_warn": "앱이 시작될 때 지갑 파일이 무결성 검사를 통과하지 못한 것을 발견했습니다 — 보통 앱 업데이트 후나 비정상 종료 후에 발생합니다. 앱은 이미 데이터를 보호했습니다. 기존 파일을 따로 보관하고 복구된 사본을 불러와서 지갑을 계속 사용할 수 있게 했습니다.",
|
||||
"wallet_recovery_decide_later": "나중에 결정",
|
||||
"wallet_recovery_decide_later_tip": "이 창을 닫고 지금 불러온 사본을 그대로 사용합니다. 아무것도 변경되지 않으며, 언제든 복구할 수 있습니다 — 상태 표시줄에 “지갑 복구 가능” 링크가 계속 표시됩니다.",
|
||||
"wallet_recovery_details_label": "기술 세부정보 표시",
|
||||
"wallet_recovery_done": "완료",
|
||||
"wallet_recovery_failure_body": "복구가 실행되었지만 결과가 검증을 통과하지 못했습니다 (읽을 수 없거나 주소가 없음). 그래서 무엇도 교체하기 전에 자동으로 폐기되었습니다. 지갑은 클릭하기 전과 정확히 동일하며 — 디스크에는 아무 변경도 없습니다.",
|
||||
"wallet_recovery_failure_title": "복구가 완료되지 않았습니다",
|
||||
"wallet_recovery_files_detail": "원본 지갑은 그대로 있습니다 — 앱이 데이터 폴더에서 원본 이름을 날짜가 표시된 백업(wallet.<숫자>.bak)으로 변경했을 뿐, 아무것도 삭제하지 않았습니다. “복구”는 가장 온전한 지갑의 모든 기록을 읽어 완전히 새로운 깨끗한 파일로 만듭니다. “복원”은 가장 큰 백업을 있는 그대로 정확히 복사합니다. 지금 불러온 사본 역시 백업으로 보관됩니다.",
|
||||
"wallet_recovery_files_label": "내 파일은 어떻게 되나요?",
|
||||
"wallet_recovery_notnow_short": "나중에",
|
||||
"wallet_recovery_other_options": "다른 옵션",
|
||||
"wallet_recovery_rebuild_card": "자동으로 복구",
|
||||
"wallet_recovery_rebuild_card_desc": "복구 가능한 모든 기록을 깨끗한 파일로 읽어 들인 다음 재시작합니다. 가장 철저한 방법입니다.",
|
||||
"wallet_recovery_recommended": "권장",
|
||||
"wallet_recovery_repair_go": "복구",
|
||||
"wallet_recovery_rescan_body": "잔액과 거래 내역을 재구성하기 위해 블록체인을 재스캔하고 있습니다. 몇 분 정도 걸릴 수 있습니다 — 아무 작업도 할 필요 없으며, 실행 중에는 노드를 재시작하지 마세요.",
|
||||
"wallet_recovery_rescan_elapsed": "%s 동안 진행 중",
|
||||
"wallet_recovery_rescan_size": "복구된 지갑 크기는 현재 %s이며 계속 채워지는 중입니다",
|
||||
"wallet_recovery_rescan_slow": "평소보다 오래 걸리고 있으며, 지갑이 클 경우 정상입니다. 그대로 실행되도록 두어도 안전합니다 — 고급 ▸ 콘솔에서 진행 상황을 확인할 수 있습니다.",
|
||||
"wallet_recovery_rescan_title": "지갑 복구 마무리 중",
|
||||
"wallet_recovery_restore_card": "원본 복원",
|
||||
"wallet_recovery_restore_card_desc": "가장 큰 원본 백업을 그대로 되돌려 놓습니다 — 그 파일이 담고 있던 만큼만 완전합니다.",
|
||||
"wallet_recovery_restore_go": "복원",
|
||||
"wallet_recovery_success_body": "앱이 복구된 지갑을 불러왔으며 잔액을 합산하기 위해 재스캔하고 있습니다 — 몇 분 정도 걸릴 수 있습니다. 가능한 모든 기록을 읽어 들이지만, 드물게 손상된 파일에서는 이전보다 주소가 조금 적게 — 또는 가끔 더 많이 — 복구될 수 있습니다. 재스캔이 끝나면 잔액과 내역이 올바른지 확인하세요.",
|
||||
"wallet_recovery_success_restore": "가장 큰 백업 파일이 제자리로 돌아와 지금 불러오는 중이며, 앱이 재스캔하고 있습니다. 있는 그대로 복원되었으므로 그 파일이 담고 있던 만큼만 완전합니다 — 재스캔이 끝나면 잔액을 확인하세요.",
|
||||
"wallet_recovery_try_other": "다른 옵션 시도",
|
||||
"wallet_recovery_whats_happened": "무슨 일이 있었나요?",
|
||||
"wallet_recovery_working_label": "작업 중",
|
||||
"wallet_restore_bad_backup": "백업 지갑 파일을 읽을 수 없어 복원되지 않았습니다 — 현재 지갑은 변경되지 않았습니다. 대신 직접 보관한 백업에서 복원하세요.",
|
||||
"wallet_restore_busy": "노드가 재시작 중이라 바쁩니다 — 잠시 후 다시 시도하세요.",
|
||||
"wallet_restore_copy_failed": "백업 지갑을 설치하지 못했습니다. 현재 지갑은 그대로 유지되었습니다.",
|
||||
"wallet_restore_move_failed": "현재 지갑을 따로 보관하지 못했습니다 — 아무것도 변경되지 않았습니다.",
|
||||
"wallet_restore_no_backup": "복원할 wallet.<타임스탬프>.bak을 찾을 수 없습니다. 아무것도 변경되지 않았습니다.",
|
||||
"wallet_restore_no_restart": "원본 지갑은 복원되었지만 노드가 재시작되지 않았습니다 — 설정에서 노드를 시작하세요.",
|
||||
"wallet_restore_ok": "원본 지갑이 복원되었습니다. 노드가 지금 불러오고 있습니다.",
|
||||
"wallet_restore_started": "원본 파일을 복원하는 중입니다 — 이 창을 닫지 마세요.",
|
||||
"wallet_restore_stop_failed": "노드가 제시간에 멈추지 않아 아무것도 변경되지 않았습니다. 다시 시도하세요.",
|
||||
"wallet_size_consolidate": "노트 통합…",
|
||||
"wallet_size_warn": "이 지갑 파일이 큽니다. 노트를 통합하면 추가 증가를 억제할 수 있습니다.",
|
||||
"wallets_active": "활성",
|
||||
"wallets_add": "폴더 추가",
|
||||
"wallets_add_folder": "지갑 파일을 검색할 폴더를 추가로 지정:",
|
||||
@@ -1738,6 +2229,7 @@
|
||||
"wallets_open_inplace_tt": "이 지갑을 있는 자리에서 엽니다 — 데이터 디렉터리에 링크 (복사 없음)",
|
||||
"wallets_remove_folder": "이 폴더 스캔 중지",
|
||||
"wallets_reveal": "폴더 열기",
|
||||
"wallets_scan_folder": "다른 폴더에서 지갑 검색…",
|
||||
"wallets_scanned_folders": "스캔한 폴더:",
|
||||
"wallets_sort_addresses": "주소",
|
||||
"wallets_sort_asc": "오름차순 (오래된/적은/작은 순)",
|
||||
@@ -1807,19 +2299,30 @@
|
||||
"wiz_welcome_sub": "몇 가지만 선택하면 풀 노드가 준비됩니다.",
|
||||
"wiz_welcome_title": "ObsidianDragon에 오신 것을 환영합니다!",
|
||||
"wizard_daemon_start_failed": "데몬 시작 실패 — 자동으로 재시도됩니다",
|
||||
"xmrig_active_tt": "현재 설치된 (사용 중인) 채굴기",
|
||||
"xmrig_browse_releases": "모든 릴리스 보기…",
|
||||
"xmrig_checking": "최신 채굴기를 확인하는 중…",
|
||||
"xmrig_current": "현재:",
|
||||
"xmrig_downgrade_note": "다른 버전의 채굴기를 설치하면 현재 버전을 대체합니다. 먼저 채굴을 중지하면 다음 시작 시 적용됩니다.",
|
||||
"xmrig_download_install": "다운로드 및 설치",
|
||||
"xmrig_downloading": "다운로드 중…",
|
||||
"xmrig_install_this": "이 버전 설치",
|
||||
"xmrig_installed": "설치됨:",
|
||||
"xmrig_installed_ok": "채굴기가 설치되었습니다",
|
||||
"xmrig_installing": "설치 중…",
|
||||
"xmrig_latest": "최신:",
|
||||
"xmrig_latest_badge": "최신",
|
||||
"xmrig_latest_tt": "사용 가능한 최신 버전",
|
||||
"xmrig_loading_releases": "릴리스를 불러오는 중…",
|
||||
"xmrig_no_notes": "이 버전에 대한 릴리스 노트가 없습니다.",
|
||||
"xmrig_none": "없음",
|
||||
"xmrig_prerelease_tt": "사전 릴리스 / 테스트 빌드",
|
||||
"xmrig_reinstall": "재설치",
|
||||
"xmrig_released": "출시일:",
|
||||
"xmrig_releases": "xmrig 릴리스",
|
||||
"xmrig_status_current": "이 버전이 설치되어 있습니다",
|
||||
"xmrig_status_latest": "최신 버전",
|
||||
"xmrig_status_older": "설치된 버전보다 이전 버전입니다",
|
||||
"xmrig_stop_mining_first": "채굴기를 업데이트하기 전에 채굴을 중지하세요.",
|
||||
"xmrig_unavailable_body": "이 플랫폼에서 사용 가능한 채굴기 빌드가 없습니다.",
|
||||
"xmrig_unavailable_title": "채굴기 업데이트를 사용할 수 없습니다",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"about_license_text": "Este software é disponibilizado sob a Licença Pública Geral GNU v3 (GPLv3). Você é livre para usar, modificar e distribuir este software sob os termos da licença.",
|
||||
"about_peers_count": "%zu pares",
|
||||
"about_release": "Versão",
|
||||
"about_source": "Origem",
|
||||
"about_title": "Sobre o ObsidianDragon",
|
||||
"about_version": "Versão:",
|
||||
"about_website": "Website",
|
||||
@@ -60,7 +61,84 @@
|
||||
"amount_label": "Valor:",
|
||||
"animate_avatars": "Animar avatares",
|
||||
"appearance": "APARÊNCIA",
|
||||
"appx_back": "Voltar",
|
||||
"appx_back_up_seed_phrase_title": "Faça o backup da sua frase-semente",
|
||||
"appx_birthday_block_height": "Nascimento (altura do bloco): %llu — faça o backup disto também.",
|
||||
"appx_blockchain_data_deleted": "Dados da blockchain excluídos (%d itens). O daemon está reiniciando para ressincronizar com a rede.",
|
||||
"appx_blockchain_maintenance_in_progress": "Uma operação de manutenção da blockchain já está em andamento.",
|
||||
"appx_blockchain_rescan_complete": "Reescaneamento da blockchain concluído",
|
||||
"appx_bootstrap_complete_reconciling": "Bootstrap concluído — reconciliando sua carteira com os novos dados da chain.",
|
||||
"appx_cancel": "Cancelar",
|
||||
"appx_cleaning_up": "Limpando...",
|
||||
"appx_confirm_your_backup": "Confirme seu backup",
|
||||
"appx_copied_clipboard_autoclears": "Copiado — a área de transferência será limpa em 45s",
|
||||
"appx_copy": "Copiar",
|
||||
"appx_could_not_start_restore": "Não foi possível iniciar a restauração",
|
||||
"appx_create_failed_prefix": "Falha na criação: ",
|
||||
"appx_creating_your_wallet": "Criando sua carteira…",
|
||||
"appx_daemon_error": "Erro do Daemon",
|
||||
"appx_daemon_reinstall_in_progress": "A reinstalação do daemon já está em andamento.",
|
||||
"appx_disconnecting": "Desconectando...",
|
||||
"appx_done": "Concluído",
|
||||
"appx_dragonxd_output": "saída do dragonxd",
|
||||
"appx_encrypting_wallet": "Criptografando a carteira...",
|
||||
"appx_fullnode_lifecycle_unavailable_lite": "As ações de ciclo de vida do nó completo não estão disponíveis na versão lite",
|
||||
"appx_installing_bundled_daemon": "Instalando o daemon incluído — o nó vai parar, atualizar e reiniciar...",
|
||||
"appx_invalid_payment_uri_prefix": "URI de pagamento inválida: ",
|
||||
"appx_ive_written_it_down": "Já anotei",
|
||||
"appx_keep_node_running_and_quit": "Manter o nó em execução e sair",
|
||||
"appx_last_block_n": "Último bloco: %d",
|
||||
"appx_last_used_wallet_not_found_prefix": "Seu último arquivo de carteira usado (",
|
||||
"appx_last_used_wallet_not_found_suffix": ") não foi encontrado — a carteira padrão foi aberta em seu lugar. Se você o moveu, restaure-o e volte a ele pela lista de carteiras.",
|
||||
"appx_low_spec_mode_disabled": "Modo de baixo desempenho desativado",
|
||||
"appx_low_spec_mode_enabled": "Modo de baixo desempenho ativado",
|
||||
"appx_miner_stopped_prefix": "Minerador parado: ",
|
||||
"appx_miner_stopped_unexpectedly": "O minerador parou inesperadamente.",
|
||||
"appx_n_min_n_sec": "%d min %d s",
|
||||
"appx_n_seconds": "%d segundos",
|
||||
"appx_no_bundled_daemon_to_install": "Esta versão não tem daemon incluído para instalar",
|
||||
"appx_no_embedded_daemon_to_install": "Esta versão não tem daemon embutido para instalar",
|
||||
"appx_node_busy_restarting": "O nó está ocupado reiniciando — tente novamente em instantes.",
|
||||
"appx_node_rebuilding_witness_cache": "O nó está reconstruindo seu cache de testemunhas",
|
||||
"appx_not_next_word": " — essa não é a próxima palavra",
|
||||
"appx_payment_request_loaded": "Solicitação de pagamento carregada",
|
||||
"appx_pool_miner_connected_and_hashing": "Minerador de pool conectado e processando hashes.",
|
||||
"appx_progress_n_of_n": "Progresso: %d / %d",
|
||||
"appx_rebuilding_sapling_note_witnesses": "Reconstruindo testemunhas de notas Sapling…",
|
||||
"appx_rebuilding_witness_cache_blocks_left": "Reconstruindo cache de testemunhas %.0f%% — %d blocos restantes",
|
||||
"appx_rebuilding_witness_cache_pct": "Reconstruindo cache de testemunhas %.0f%%",
|
||||
"appx_recovery_phrase_word_count": "A frase de recuperação deve ter 24 palavras — você tem %d.",
|
||||
"appx_restarting_daemon_rescan_flag": "Reiniciando o daemon com a flag -rescan...",
|
||||
"appx_restarting_daemon_zapwallettxes": "Reiniciando o daemon com -zapwallettxes=2 (reparo da carteira)...",
|
||||
"appx_restoring_your_wallet": "Restaurando sua carteira…",
|
||||
"appx_seed_backup_warning": "Estas 24 palavras são a ÚNICA forma de restaurar sua carteira. Anote-as na ordem, guarde-as offline e nunca as compartilhe. Se perdê-las, seus fundos desaparecem para sempre.",
|
||||
"appx_seed_not_backed_up_warning": "Você não fez o backup da sua semente — os fundos podem ser perdidos. Pular mesmo assim?",
|
||||
"appx_sending_stop_command_to_daemon": "Enviando comando de parada ao daemon...",
|
||||
"appx_setting_initial_sapling_witnesses": "Definindo testemunhas Sapling iniciais %.0f%%",
|
||||
"appx_shutdown_complete": "Encerramento concluído",
|
||||
"appx_simple_background_disabled": "Fundo simples desativado",
|
||||
"appx_simple_background_enabled": "Fundo simples ativado",
|
||||
"appx_skip": "Pular",
|
||||
"appx_skip_anyway": "Pular mesmo assim",
|
||||
"appx_still_status_prefix": "Ainda \"",
|
||||
"appx_still_status_suffix": "\" — forçar o encerramento agora pode corromper os dados da chain.",
|
||||
"appx_stop_anyway_and_quit": "Parar mesmo assim e sair",
|
||||
"appx_stopping_daemon_deleting_blockchain": "Parando o daemon e excluindo os dados da blockchain...",
|
||||
"appx_stopping_node_discards_rebuild": "Parar o nó agora descarta a reconstrução em andamento e a reinicia (vários minutos) na próxima vez que você abrir a carteira. Em vez disso, você pode manter o nó em execução.",
|
||||
"appx_stopping_pool_miner": "Parando o minerador de pool...",
|
||||
"appx_syncing_pct_block_n_of_n": "Sincronizando %.1f%% — Bloco %d / %d",
|
||||
"appx_tap_words_in_order": "Toque nas palavras na ordem correta para confirmar que você as salvou.",
|
||||
"appx_theme_effects_disabled": "Efeitos de tema desativados",
|
||||
"appx_theme_effects_enabled": "Efeitos de tema ativados",
|
||||
"appx_theme_prefix": "Tema: ",
|
||||
"appx_use_settings_restart_daemon_hint": "Use Configurações > Reiniciar Daemon para tentar novamente",
|
||||
"appx_waiting_for_daemon_to_encrypt_wallet": "Aguardando o daemon criptografar a carteira...",
|
||||
"appx_wallet_created_and_backed_up": "Carteira criada e com backup feito.",
|
||||
"appx_wallet_open_failed_prefix": "Falha ao abrir a carteira: ",
|
||||
"auto_shield": "Auto-blindar mineração",
|
||||
"autoshield_by_node": "Seu node blinda automaticamente o coinbase minerado para você.",
|
||||
"autoshield_off_backup_seed": "O coinbase minerado ainda não está sendo blindado automaticamente. Faça backup da sua frase de recuperação (Configurações → Backup e Dados) para que seu node possa ativar isso com segurança.",
|
||||
"autoshield_off_generic": "Seu node não está blindando automaticamente o coinbase minerado no momento.",
|
||||
"av_intro": "Softwares de mineração costumam ser sinalizados como potencialmente indesejados. Siga estes passos para habilitar a mineração em pool:",
|
||||
"av_open_security": "Abrir Segurança do Windows",
|
||||
"av_step1": "Passo 1: Adicionar exclusão",
|
||||
@@ -100,6 +178,17 @@
|
||||
"balance_shielded_fmt": "Blindado: %.8f",
|
||||
"balance_syncing_pct": "Sincronizando %.1f%%",
|
||||
"balance_transparent_fmt": "Transparente: %.8f",
|
||||
"baltab_market": "Mercado",
|
||||
"baltab_market_price_4dp": "Mercado: $%.4f",
|
||||
"baltab_market_price_8dp": "Mercado: $%.8f",
|
||||
"baltab_pct_change_24h": "%s%.1f%% 24h",
|
||||
"baltab_pct_of_total_zaddr": "%.0f%% do total · %d Z-addr",
|
||||
"baltab_shielded": "Blindado",
|
||||
"baltab_shielded_amount": "Blindado %.8f",
|
||||
"baltab_t_addresses_count": "%d T-addresses",
|
||||
"baltab_total_balance": "Saldo Total",
|
||||
"baltab_transparent": "Transparente",
|
||||
"baltab_transparent_amount": "Transparente %.8f",
|
||||
"ban": "Banir",
|
||||
"banned_peers": "Pares Banidos",
|
||||
"block": "Bloco",
|
||||
@@ -107,6 +196,12 @@
|
||||
"block_click_copy": "Clique para copiar",
|
||||
"block_click_next": "Clique para ver o próximo bloco",
|
||||
"block_click_prev": "Clique para ver o bloco anterior",
|
||||
"block_db_reindex_body": "Isso geralmente acontece após uma atualização do daemon alterar o formato em disco, ou se o índice de blocos estiver danificado. Sua carteira e suas moedas estão seguras — o node apenas não consegue carregar a chain, então os saldos aparecem como zero.\n\nReconstruir relê seus arquivos de blocos existentes e pode levar um tempo (também refaz o rescan da sua carteira). Nada é baixado.",
|
||||
"block_db_reindex_confirm": "Reconstruir banco de dados de blocos",
|
||||
"block_db_reindex_notify": "O node não consegue ler seu banco de dados de blocos (geralmente após uma atualização do daemon). Reconstrua-o para restaurar seu saldo — veja o aviso, ou Configurações › Node.",
|
||||
"block_db_reindex_started": "Reconstruindo o banco de dados de blocos a partir dos seus blocos — isso pode levar um tempo.",
|
||||
"block_db_reindex_title": "Reconstruir banco de dados de blocos?",
|
||||
"block_db_reindex_warn": "O node não consegue ler seu banco de dados de blocos.",
|
||||
"block_explorer": "Explorador de Blocos",
|
||||
"block_get_info": "Obter Info do Bloco",
|
||||
"block_hash": "Hash do Bloco:",
|
||||
@@ -154,6 +249,10 @@
|
||||
"chat_accent_theme": "Tema",
|
||||
"chat_add_contact": "Adicionar contato",
|
||||
"chat_awaiting_key": "Aguardando resposta",
|
||||
"chat_blocked_desc": "Conversas bloqueadas são removidas e suas mensagens são descartadas — antigas e novas — até você desbloquear. Desbloquear reimporta a conversa a partir da chain.",
|
||||
"chat_blocked_manage": "Bloqueadas",
|
||||
"chat_blocked_title": "Conversas bloqueadas",
|
||||
"chat_blocked_toast": "Conversa excluída e bloqueada",
|
||||
"chat_bubble_minimal": "Mínimo",
|
||||
"chat_bubble_rounded": "Arredondado",
|
||||
"chat_bubble_square": "Quadrado",
|
||||
@@ -166,6 +265,16 @@
|
||||
"chat_contact_added": "Contato adicionado — renomeie em Contatos",
|
||||
"chat_contact_request": "solicitação de contato",
|
||||
"chat_copy_address_tip": "Clique para copiar o endereço",
|
||||
"chat_delete": "Excluir conversa",
|
||||
"chat_delete_block": "Excluir e bloquear",
|
||||
"chat_delete_body_prefix": "Excluir sua cópia local da conversa com ",
|
||||
"chat_delete_body_suffix": "?",
|
||||
"chat_delete_confirm": "Excluir",
|
||||
"chat_delete_failed": "Não foi possível excluir a conversa — nada foi alterado.",
|
||||
"chat_delete_local_note": "Isso afeta apenas este dispositivo — as mensagens permanecem na blockchain e a outra pessoa mantém sua cópia.",
|
||||
"chat_delete_revive_note": "\"Excluir\" limpa o histórico neste dispositivo. Se essa pessoa enviar uma mensagem novamente, a conversa volta.",
|
||||
"chat_delete_title": "Excluir conversa?",
|
||||
"chat_deleted_toast": "Conversa excluída",
|
||||
"chat_density_comfortable": "Confortável",
|
||||
"chat_density_compact": "Compacta",
|
||||
"chat_emoji_color": "Colorido",
|
||||
@@ -189,6 +298,7 @@
|
||||
"chat_new_button": "Novo chat",
|
||||
"chat_new_message": "Mensagem",
|
||||
"chat_new_message_toast": "Nova mensagem de chat criptografada",
|
||||
"chat_new_needs_zaddr": "O chat precisa de um endereço blindado (z) — endereços transparentes (t) não podem receber mensagens criptografadas.",
|
||||
"chat_new_send": "Enviar solicitação",
|
||||
"chat_new_title": "Novo chat",
|
||||
"chat_new_zaddr": "Endereço-z do destinatário",
|
||||
@@ -234,6 +344,8 @@
|
||||
"chat_ts_24h": "24 horas",
|
||||
"chat_ts_global": "Seguir global",
|
||||
"chat_ts_global_short": "Global",
|
||||
"chat_unblock": "Desbloquear",
|
||||
"chat_unblocked_toast": "Conversa desbloqueada",
|
||||
"chat_unhide": "Mostrar",
|
||||
"chat_unmute": "Reativar som",
|
||||
"chat_verify_key": "Chave de identidade — compare para verificar",
|
||||
@@ -286,6 +398,7 @@
|
||||
"console": "Console",
|
||||
"console_accents": "Destaques de cor",
|
||||
"console_app": "App",
|
||||
"console_auto_focus": "Focar entrada ao abrir",
|
||||
"console_auto_scroll": "Rolagem automática",
|
||||
"console_available_commands": "Comandos disponíveis:",
|
||||
"console_backend_reference": "Referência de Comandos do Backend",
|
||||
@@ -380,11 +493,15 @@
|
||||
"console_tab_completion": "Tab para completar",
|
||||
"console_text_colors": "Cores do texto",
|
||||
"console_toggle_accents": "Alternar destaques de cor das linhas",
|
||||
"console_toggle_auto_focus": "Colocar o cursor na caixa de comando ao abrir a aba Console",
|
||||
"console_toggle_text_color": "Alternar cores do texto das linhas",
|
||||
"console_type_help": "Digite 'help' para comandos disponíveis",
|
||||
"console_welcome": "Bem-vindo ao Console ObsidianDragon",
|
||||
"console_zoom_in": "Aumentar zoom",
|
||||
"console_zoom_out": "Diminuir zoom",
|
||||
"consolidate_desc": "Combine muitas entradas pequenas em uma única nota blindada. Menos notas significa um arquivo de carteira menor e mais privacidade.",
|
||||
"consolidate_funds_btn": "Consolidar",
|
||||
"consolidate_title": "Consolidar fundos",
|
||||
"contact_avatar": "AVATAR",
|
||||
"contact_avatar_bad_image": "Não foi possível carregar essa imagem.",
|
||||
"contact_avatar_badge": "Selo",
|
||||
@@ -414,6 +531,7 @@
|
||||
"contacts_shape_square": "Quadrado",
|
||||
"contacts_shape_tab": "Aba",
|
||||
"copied": "Copiado!",
|
||||
"copied_to_clipboard": "Copiado para a área de transferência",
|
||||
"copy": "Copiar",
|
||||
"copy_address": "Copiar Endereço Completo",
|
||||
"copy_error": "Copiar Erro",
|
||||
@@ -429,9 +547,13 @@
|
||||
"daemon_maintenance_label": "MANUTENÇÃO",
|
||||
"daemon_none_bundled": "nenhum nesta build",
|
||||
"daemon_not_installed": "não instalado",
|
||||
"daemon_status_diff": "Versão diferente",
|
||||
"daemon_status_differ": "O binário instalado difere da versão empacotada.",
|
||||
"daemon_status_match": "O binário instalado corresponde à versão incluída.",
|
||||
"daemon_status_missing": "Nenhum daemon instalado — instale a versão incluída.",
|
||||
"daemon_status_none": "Não instalado",
|
||||
"daemon_status_ok": "Atualizado",
|
||||
"daemon_update_active_tt": "O node atualmente instalado (ativo)",
|
||||
"daemon_update_available": "Uma nova versão do nó está disponível",
|
||||
"daemon_update_body": "Esta versão da carteira inclui um nó DragonX mais recente do que o atualmente instalado. A atualização substitui o dragonxd instalado (e o dragonx-cli/dragonx-tx), depois para e reinicia o nó para que a nova versão entre em vigor. Recomendado — um nó mais recente pode adicionar recursos (por exemplo, suporte a frase de recuperação) que o antigo não tem.",
|
||||
"daemon_update_browse": "Ver todos os lançamentos…",
|
||||
@@ -441,19 +563,28 @@
|
||||
"daemon_update_download_install": "Baixar e instalar",
|
||||
"daemon_update_downloading": "Baixando…",
|
||||
"daemon_update_failed": "Falha na atualização",
|
||||
"daemon_update_install_this": "Instalar esta versão",
|
||||
"daemon_update_installed": "Instalado:",
|
||||
"daemon_update_installed_ok": "Nó instalado",
|
||||
"daemon_update_installing": "Instalando…",
|
||||
"daemon_update_keep": "Manter atual",
|
||||
"daemon_update_later": "Depois",
|
||||
"daemon_update_latest": "Mais recente:",
|
||||
"daemon_update_latest_badge": "mais recente",
|
||||
"daemon_update_latest_tt": "Versão mais recente disponível",
|
||||
"daemon_update_loading": "Carregando versões…",
|
||||
"daemon_update_no_notes": "Sem notas de versão para esta versão.",
|
||||
"daemon_update_now": "Atualizar agora",
|
||||
"daemon_update_prerelease_tt": "Build de pré-lançamento / teste",
|
||||
"daemon_update_prompt_title": "Atualizar o daemon do node?",
|
||||
"daemon_update_reinstall": "Reinstalar",
|
||||
"daemon_update_released": "Lançado:",
|
||||
"daemon_update_restart_note": "Reinicie o daemon para começar a executar a nova versão.",
|
||||
"daemon_update_restart_now": "Reiniciar daemon agora",
|
||||
"daemon_update_safe": "Sua carteira, chaves e dados da blockchain não são tocados — apenas os arquivos de programa do daemon são substituídos. Se você executa deliberadamente um nó personalizado, escolha Manter atual.",
|
||||
"daemon_update_status_current": "Esta versão está instalada",
|
||||
"daemon_update_status_latest": "Versão mais recente",
|
||||
"daemon_update_status_older": "Mais antiga que sua versão instalada",
|
||||
"daemon_update_title": "Atualizar o daemon do nó?",
|
||||
"daemon_update_unavailable_body": "Nenhuma versão do nó está disponível para esta plataforma.",
|
||||
"daemon_update_unavailable_title": "Atualizações do nó indisponíveis",
|
||||
@@ -469,6 +600,13 @@
|
||||
"data_stale_tooltip": "O saldo pode estar desatualizado — a carteira não recebeu uma atualização recente. Verifique a conexão com o seu nó.",
|
||||
"date": "Data",
|
||||
"date_label": "Data:",
|
||||
"debug_gate_confirm": "Mostrar opções de depuração",
|
||||
"debug_gate_incorrect": "PIN ou senha incorretos.",
|
||||
"debug_gate_pass_prompt": "Digite a senha da sua carteira para continuar:",
|
||||
"debug_gate_pin_prompt": "Digite seu PIN de desbloqueio para continuar:",
|
||||
"debug_gate_title": "Mostrar opções de depuração?",
|
||||
"debug_gate_verifying": "Verificando…",
|
||||
"debug_gate_warning": "Estas são opções avançadas de diagnóstico e ajuste do node. Elas podem expor informações sensíveis e alterar o funcionamento do seu node. Só continue se souber o que está fazendo.",
|
||||
"debug_logging": "REGISTRO DE DEPURAÇÃO",
|
||||
"decrypt_desc": "A carteira será exportada, o daemon reiniciado com uma nova carteira sem criptografia e todas as chaves reimportadas. Isso pode levar alguns minutos, dependendo do tamanho da carteira.",
|
||||
"decrypt_error_title": "Falha na descriptografia",
|
||||
@@ -491,6 +629,7 @@
|
||||
"disconnected": "Desconectado",
|
||||
"dismiss": "Dispensar",
|
||||
"display": "Exibição",
|
||||
"done": "Concluído",
|
||||
"download": "Baixar",
|
||||
"download_bootstrap": "Baixar Bootstrap",
|
||||
"dragonx_green": "DragonX (Verde)",
|
||||
@@ -544,6 +683,7 @@
|
||||
"explorer_tx_outputs": "Saídas",
|
||||
"explorer_tx_size": "Tamanho",
|
||||
"explorer_txid": "TxID:",
|
||||
"explorer_urls_hdr": "URLS",
|
||||
"export": "Exportar",
|
||||
"export_csv": "Exportar CSV",
|
||||
"export_keys_btn": "Exportar Chaves",
|
||||
@@ -571,6 +711,151 @@
|
||||
"export_viewing_key": "Exportar Chave de Visualização",
|
||||
"failed_create_shielded": "Falha ao criar endereço blindado",
|
||||
"failed_create_transparent": "Falha ao criar endereço transparente",
|
||||
"faq": "Perguntas frequentes",
|
||||
"faq_d_mgmt_1_a": "O node incorporado inicia automaticamente com a carteira e para quando apropriado, então normalmente você não precisa gerenciá-lo manualmente. Os controles avançados (incluindo reiniciar o daemon) ficam em Configurações > Node e Segurança.",
|
||||
"faq_d_mgmt_1_q": "Como inicio ou paro o node?",
|
||||
"faq_d_mgmt_2_a": "Se você conectar a carteira a um node que você mesmo iniciou, esta opção decide se fechar a carteira também encerra esse node. Deixe desativado se quiser que seu node continue rodando após fechar a carteira.",
|
||||
"faq_d_mgmt_2_q": "O que faz \"fechar daemon externo ao sair\"?",
|
||||
"faq_d_mgmt_3_a": "Os dados do node e da carteira ficam no diretório de dados do DragonX dentro do seu perfil de usuário (por exemplo, em AppData no Windows ou na sua pasta pessoal no Linux/macOS). A blockchain é a parte grande; mantenha espaço livre em disco suficiente para ela crescer.",
|
||||
"faq_d_mgmt_3_q": "Onde ficam armazenados a blockchain e os dados da carteira?",
|
||||
"faq_d_mgmt_title": "Gestão do nó",
|
||||
"faq_d_mine_1_a": "A aba Mineração permite minerar com sua CPU. O DragonX usa o algoritmo RandomX, projetado para processadores comuns. Defina o número de threads e inicie — as recompensas se acumulam na sua carteira.",
|
||||
"faq_d_mine_1_q": "Como faço para minerar DragonX?",
|
||||
"faq_d_mine_2_a": "A mineração solo envia recompensas diretamente para você, mas só paga quando você encontra um bloco, o que é raro a menos que você tenha muito hashrate. A mineração em pool compartilha o trabalho com outros para pagamentos menores e mais constantes. A aba Mineração suporta ambos.",
|
||||
"faq_d_mine_2_q": "Solo ou pool — qual devo escolher?",
|
||||
"faq_d_mine_3_a": "A seção de pool da aba Mineração tem um botão 'Atualizar minerador…' que baixa, verifica e instala a versão mais recente do minerador DragonX otimizado. Assim como o atualizador do nó, cada download é verificado por checksum e assinatura antes da instalação.",
|
||||
"faq_d_mine_3_q": "Como faço para atualizar o minerador?",
|
||||
"faq_d_mine_4_a": "O nó atual pode executar um servidor stratum embutido para que outros mineradores possam apontar para sua máquina. Há uma opção para isso em Configurações > Nó & Segurança. Por padrão, ele escuta apenas localmente; expô-lo a outros computadores exige abertura deliberada.",
|
||||
"faq_d_mine_4_q": "Posso hospedar um pool de mineração?",
|
||||
"faq_d_mine_title": "Mineração e Pools",
|
||||
"faq_d_net_1_a": "Logo após a inicialização, o nó precisa de um momento para encontrar peers, então um breve '0 peers' é normal. Se persistir, verifique sua conexão com a internet e se um firewall não está bloqueando o nó. O nó encontra peers automaticamente através de seeds embutidos.",
|
||||
"faq_d_net_1_q": "A carteira mostra nenhum peer ou conexão — o que está errado?",
|
||||
"faq_d_net_2_a": "Sim — os nós DragonX se comunicam entre si via TLS, então as conexões entre peers são criptografadas. A carteira também se comunica com seu próprio nó por um canal local seguro.",
|
||||
"faq_d_net_2_q": "Minha conexão com a rede é criptografada?",
|
||||
"faq_d_net_title": "Peers e Rede",
|
||||
"faq_d_node_1_a": "O daemon, dragonxd, é o nó completo do DragonX. Ele baixa e verifica toda a blockchain, retransmite transações para a rede e responde às consultas da carteira. Rodar seu próprio nó significa que você não precisa confiar no servidor de outra pessoa.",
|
||||
"faq_d_node_1_q": "O que é o daemon (dragonxd)?",
|
||||
"faq_d_node_2_a": "Por padrão, a carteira inicia e gerencia seu próprio nó embutido (embedded) — você não precisa fazer nada. Se você já roda o dragonxd por conta própria, a carteira pode se conectar a esse nó externo. A carteira detecta em qual situação está e se comporta de acordo.",
|
||||
"faq_d_node_2_q": "Nó embutido vs externo — qual a diferença?",
|
||||
"faq_d_node_3_a": "Um nó completo valida independentemente cada regra da blockchain, então sua carteira confia em matemática em vez de em terceiros. Isso também fortalece a rede. O custo é espaço em disco e uma sincronização inicial.",
|
||||
"faq_d_node_3_q": "Por que rodar um nó completo?",
|
||||
"faq_d_node_title": "O Nó Completo",
|
||||
"faq_d_perf_1_a": "A blockchain é o maior item e cresce com o tempo, então mantenha vários gigabytes livres. O nó usa um cache de banco de dados para velocidade; os padrões incluídos são ajustados para máquinas comuns. Usuários avançados podem ajustar o cache de banco de dados do nó editando dbcache no arquivo de configuração (DRAGONX.conf).",
|
||||
"faq_d_perf_1_q": "Quanto disco o nó usa, e posso ajustar a memória?",
|
||||
"faq_d_perf_2_a": "Ative o download de bootstrap (oferecido no assistente de configuração) para obter uma cópia recente e verificada da chain em vez de validar cada bloco do zero. Um disco rápido (SSD) também ajuda bastante.",
|
||||
"faq_d_perf_2_q": "Como faço para acelerar a sincronização inicial?",
|
||||
"faq_d_perf_title": "Armazenamento e Desempenho",
|
||||
"faq_d_sync_1_a": "Na inicialização, o nó carrega e valida blocos antes de poder atender a carteira. A tela 'Processando blocos / Aplicando blocos para construir o estado atual da chain' é esse aquecimento. É normal; a carteira fica utilizável assim que o nó termina e reporta sua altura.",
|
||||
"faq_d_sync_1_q": "O que significa 'Processando blocos' na inicialização?",
|
||||
"faq_d_sync_2_a": "Para seus fundos blindados, o nó mantém 'testemunhas' (witnesses) criptográficas que permitem gastar de forma privada. Após certas reinicializações ou atualizações, ele reconstrói esse cache reescaneando blocos recentes. Em uma carteira com muitas transações isso pode levar vários minutos e usar memória extra.\n\nNão está travado — deixe terminar. Forçar o encerramento no meio disso só faz com que a reconstrução reinicie do zero na próxima vez.",
|
||||
"faq_d_sync_2_q": "O que é 'Reconstruindo cache de testemunhas'?",
|
||||
"faq_d_sync_3_a": "Perto do topo da chain, o trabalho que escaneia sua carteira (verificando saldos e notas) disputa com o nó o mesmo bloqueio interno, o que pode retardar a conexão dos últimos blocos em uma carteira grande. Versões recentes limitam esse escaneamento para que o nó possa se atualizar. Ficar em uma aba mais leve (ou apenas esperar) permite que termine mais rápido.",
|
||||
"faq_d_sync_3_q": "Por que a sincronização está lenta quando estou quase em dia?",
|
||||
"faq_d_sync_4_a": "A primeira sincronização baixa e verifica toda a chain e pode levar um tempo dependendo da sua conexão e disco. Você pode acelerá-la drasticamente ativando o download de bootstrap no assistente de configuração, que obtém uma cópia recente e verificada dos dados da chain.",
|
||||
"faq_d_sync_4_q": "Quanto tempo leva a sincronização inicial?",
|
||||
"faq_d_sync_title": "Sincronização e Blockchain",
|
||||
"faq_d_trbl_1_a": "Geralmente não. Em uma carteira grande, o nó pode passar vários minutos reconstruindo testemunhas blindadas ou aplicando blocos, durante os quais parece pausado e pode demorar para responder. Dê um tempo — o log de depuração (Configurações) mostra progresso constante se estiver funcionando.\n\nEvite forçar o encerramento durante essa fase; isso reinicia o trabalho do zero na próxima inicialização.",
|
||||
"faq_d_trbl_1_q": "O nó parece travado em 'Ativando a melhor chain' — está congelado?",
|
||||
"faq_d_trbl_2_a": "Se um arquivo de carteira for recuperado mas parte de seus dados de chave estiver faltando, o nó pode abri-lo em um modo 'degradado' limitado, no qual não pode derivar novos endereços ou blindar fundos. A correção mais segura é restaurar a partir da sua frase de recuperação para uma carteira nova.",
|
||||
"faq_d_trbl_2_q": "O que é o 'modo degradado'?",
|
||||
"faq_d_trbl_3_a": "Um re-scan faz a carteira reler a chain para redescobrir suas transações (útil após importar chaves). Um re-index reconstrói o banco de dados da blockchain do nó e só é necessário se esse banco de dados estiver corrompido ou incompatível após uma mudança de versão. Ambos podem levar um tempo; inicie-os em Configurações > Nó & Segurança.",
|
||||
"faq_d_trbl_3_q": "Quando devo fazer re-index ou re-scan?",
|
||||
"faq_d_trbl_4_a": "O uso de memória aumenta durante trabalho pesado, como uma reconstrução de testemunhas ou uma sincronização inicial, e depois cai novamente quando termina. Uso de memória alto e sustentado em repouso é incomum — reiniciar a carteira o limpa. Em uma máquina com pouca RAM, certifique-se de que outros aplicativos pesados em memória não estejam competindo.",
|
||||
"faq_d_trbl_4_q": "O nó está usando muita memória — isso é normal?",
|
||||
"faq_d_trbl_title": "Solução de Problemas",
|
||||
"faq_d_upd_1_a": "Abra Configurações > Nó & Segurança > Binário do daemon e use 'Verificar atualizações…'. A carteira baixa a versão mais recente e verificada do nó e a instala; a nova versão entra em vigor na próxima vez que o daemon iniciar.",
|
||||
"faq_d_upd_1_q": "Como faço para atualizar o nó?",
|
||||
"faq_d_upd_2_a": "Sim — a caixa de diálogo de atualização lista todas as versões para que você possa escolher uma específica ou mais antiga. Tenha cuidado ao fazer downgrade: um nó mais antigo pode não aceitar dados da blockchain gravados por um mais novo e pode precisar de re-index.",
|
||||
"faq_d_upd_2_q": "Posso instalar uma versão específica do nó?",
|
||||
"faq_d_upd_3_a": "Todo arquivo do nó baixado é verificado por checksum e assinatura criptográfica antes de ser instalado. Se a verificação falhar, a carteira recusa a atualização.",
|
||||
"faq_d_upd_3_q": "A atualização é segura?",
|
||||
"faq_d_upd_title": "Atualizando o Nó",
|
||||
"faq_group_daemon": "Daemon",
|
||||
"faq_group_wallet": "Carteira",
|
||||
"faq_intro": "Respostas sobre sua carteira e o nó DragonX. Pesquise, ou navegue por tópico abaixo.",
|
||||
"faq_intro_lite": "Respostas sobre sua carteira Lite. Pesquise, ou navegue por tópico abaixo.",
|
||||
"faq_l_gs_1_a": "O ObsidianDragonLite é a carteira leve do DragonX. Em vez de rodar um nó completo, ele se conecta a um servidor lite do DragonX, então inicia rápido e não baixa toda a blockchain.\n\nEle gerencia suas chaves, saldos e envios assim como a carteira completa — veja a seção 'Carteira Lite' abaixo para saber como funciona a conexão com o servidor e a troca envolvendo privacidade.",
|
||||
"faq_l_gs_1_q": "O que é o ObsidianDragonLite?",
|
||||
"faq_l_lite_1_a": "A carteira Lite se comunica com um servidor lite do DragonX em vez de rodar seu próprio nó completo. Isso a torna rápida para iniciar e leve em disco e largura de banda, pois ela não baixa nem verifica toda a blockchain sozinha. Suas chaves privadas sempre permanecem no seu dispositivo — nunca são enviadas ao servidor.",
|
||||
"faq_l_lite_1_q": "Qual a diferença entre a carteira Lite e a carteira completa?",
|
||||
"faq_l_lite_2_a": "Abra Configurações e escolha um servidor. Você pode trocar de servidor a qualquer momento e a carteira ressincroniza a partir do novo. Se um servidor estiver lento ou inacessível, tente outro.",
|
||||
"faq_l_lite_2_q": "Como escolho ou altero o servidor?",
|
||||
"faq_l_lite_3_a": "Suas chaves nunca saem do seu dispositivo, e os valores blindados permanecem criptografados. Mas o servidor lite vê quais endereços sua carteira consulta e seu endereço IP, então ele pode vincular seus endereços entre si — uma troca de conveniência por privacidade. Para máxima privacidade, use a carteira de nó completo, que verifica a chain por conta própria e não revela seus endereços a um servidor.",
|
||||
"faq_l_lite_3_q": "A carteira Lite é tão privada quanto o nó completo?",
|
||||
"faq_l_lite_title": "Carteira Lite",
|
||||
"faq_l_sec_1_a": "Vá em Configurações > Carteira e defina uma senha na seção de segurança. A encriptação protege suas chaves privadas em disco, então quem tiver acesso aos arquivos ainda assim não poderá gastar suas moedas.\n\nEscolha uma senha forte e não a perca — não há como recuperar uma carteira encriptada sem ela.",
|
||||
"faq_no_results": "Nenhum resultado. Tente outro termo de busca.",
|
||||
"faq_open_tooltip": "Ajuda e FAQ",
|
||||
"faq_search_hint": "Buscar no FAQ…",
|
||||
"faq_title": "Ajuda e FAQ",
|
||||
"faq_w_addr_1_a": "Endereços transparentes (começam com R ou t) funcionam como a maioria das moedas: os valores e endereços são públicos na blockchain.\n\nEndereços blindados (endereços-z, começam com zs) usam criptografia de conhecimento zero — o valor, remetente e destinatário são encriptados on-chain. Saldos em endereços blindados são privados.",
|
||||
"faq_w_addr_1_q": "Qual a diferença entre endereços transparentes e blindados?",
|
||||
"faq_w_addr_2_a": "Prefira endereços blindados (z) sempre que possível — eles mantêm seu saldo e histórico de pagamentos privados. DragonX é uma moeda de privacidade, e blindado é o padrão para fundos recebidos.\n\nUse um endereço transparente apenas quando um serviço com o qual você interage não conseguir lidar com endereços blindados.",
|
||||
"faq_w_addr_2_q": "Qual tipo de endereço devo usar?",
|
||||
"faq_w_addr_3_a": "Para endereços blindados, reutilizar é tranquilo e não expõe o histórico. Para endereços transparentes, usar um endereço novo a cada pagamento melhora a privacidade. Crie novos endereços na aba Receber.",
|
||||
"faq_w_addr_3_q": "Devo reutilizar um endereço ou criar um novo?",
|
||||
"faq_w_addr_4_a": "Blindar move moedas de um endereço transparente para um blindado, tornando-as privadas. Moedas recém-mineradas chegam transparentes e são blindadas automaticamente para você.\n\nVocê também pode blindar manualmente pela carteira; um blindamento é apenas uma transação especial, então leva o tempo normal de confirmação para ser concluído.",
|
||||
"faq_w_addr_4_q": "O que é \"blindar\"?",
|
||||
"faq_w_addr_title": "Endereços e Privacidade",
|
||||
"faq_w_bal_1_a": "Enquanto sua carteira ainda está sincronizando, seu saldo está incompleto e pode aparecer como 0 — ainda não foram escaneadas todas as suas transações. Ele se completa assim que a sincronização terminar.\n\nObserve a barra de status na parte inferior: ela mostra o estado da conexão e a altura do bloco. Quando a sincronização estiver completa, seu saldo estará correto.",
|
||||
"faq_w_bal_1_q": "Por que meu saldo está em 0 ou não atualiza?",
|
||||
"faq_w_bal_2_a": "Cada novo bloco minerado sobre o bloco que contém sua transação adiciona uma confirmação. Mais confirmações significam que o pagamento está mais firmemente consolidado. Fundos recebidos ficam disponíveis para gasto após a primeira confirmação.",
|
||||
"faq_w_bal_2_q": "O que são confirmações?",
|
||||
"faq_w_bal_3_a": "O total inclui fundos que ainda estão confirmando ou estão temporariamente bloqueados (por exemplo, moedas no meio do processo de blindagem). O disponível é o que você pode enviar agora mesmo. Eles convergem à medida que as transações são confirmadas.",
|
||||
"faq_w_bal_3_q": "Qual a diferença entre saldo total e saldo disponível?",
|
||||
"faq_w_bal_title": "Saldo e Sincronização",
|
||||
"faq_w_chat_1_a": "O Chat é um mensageiro encriptado, on-chain, integrado à carteira. As mensagens são enviadas como memos blindados privados, então só você e seu contato podem lê-las.",
|
||||
"faq_w_chat_1_q": "O que é o recurso de Chat?",
|
||||
"faq_w_chat_2_a": "Sua identidade de chat é derivada da frase de recuperação da sua carteira, então ela acompanha sua carteira — restaure a carteira e sua identidade volta junto. Você não cria uma conta ou senha separada.",
|
||||
"faq_w_chat_2_q": "Como funciona minha identidade de chat?",
|
||||
"faq_w_chat_3_a": "Adicione um contato pelo endereço na aba Contatos, opcionalmente com nome e avatar. Os contatos podem ficar privados apenas para a carteira atual ou compartilhados entre todas as carteiras que você abrir neste computador.",
|
||||
"faq_w_chat_3_q": "Como funcionam os contatos?",
|
||||
"faq_w_chat_4_a": "Abra uma conversa e use os ícones no cabeçalho dela. Ocultar (o olho) remove a conversa da lista, mas mantém todas as mensagens — uma nova mensagem a torna visível novamente. Excluir (a lixeira) abre um diálogo: \"Excluir\" limpa o histórico neste dispositivo (se essa pessoa te enviar mensagem de novo, a conversa volta), enquanto \"Excluir e bloquear\" também impede mensagens futuras dela até você desbloqueá-la na lista \"Bloqueados\" acima da lista de conversas. Tudo isso é local a este dispositivo — as mensagens permanecem na blockchain e a outra pessoa mantém sua própria cópia, então não é um \"desenvio\".",
|
||||
"faq_w_chat_4_q": "Como eu oculto, excluo ou bloqueio uma conversa?",
|
||||
"faq_w_chat_title": "Chat e Contatos",
|
||||
"faq_w_gs_1_a": "ObsidianDragon é uma carteira de nó completo para DragonX (DRGX). Ela gerencia suas moedas e, na versão desktop, executa um nó completo DragonX (o daemon) em segundo plano, para que sua carteira verifique a blockchain por conta própria em vez de confiar em um servidor de terceiros.\n\nPense nisso como duas partes trabalhando juntas: a carteira (chaves, saldos, envios) e o nó (a blockchain e a rede). As perguntas aqui estão divididas da mesma forma — veja as abas Carteira e Daemon acima.",
|
||||
"faq_w_gs_1_q": "O que é o ObsidianDragon?",
|
||||
"faq_w_gs_2_a": "Na primeira execução, o assistente de configuração cria uma carteira para você automaticamente. Novas carteiras são protegidas por uma frase de recuperação secreta (uma lista de palavras). Anote essa frase e guarde-a offline — é a única forma de restaurar seus fundos se este computador for perdido.\n\nVocê pode revisar ou fazer backup da frase a qualquer momento em Configurações > Backup e Dados > Frase de recuperação.",
|
||||
"faq_w_gs_2_q": "Como eu crio uma nova carteira?",
|
||||
"faq_w_gs_3_a": "Use o fluxo de restauração por frase de recuperação se você tiver uma. (Na versão de nó completo você também pode copiar um arquivo wallet.dat existente para o diretório de dados da carteira antes de iniciá-la.)\n\nApós restaurar, a carteira reescaneia para encontrar suas transações passadas, então seu saldo e histórico podem levar um tempo para aparecer na primeira vez.",
|
||||
"faq_w_gs_3_q": "Como eu restauro uma carteira que já tenho?",
|
||||
"faq_w_gs_4_a": "O assistente permite escolher uma aparência/tema, configurar encriptação e um PIN e — na versão de nó completo — opcionalmente baixar um bootstrap para acelerar a primeira sincronização. Você pode pular qualquer etapa e alterar tudo isso depois em Configurações.",
|
||||
"faq_w_gs_4_q": "O que o assistente de primeira execução faz?",
|
||||
"faq_w_gs_title": "Primeiros Passos",
|
||||
"faq_w_sec_1_a": "Vá em Configurações > Nó e Segurança e defina uma senha (seção SEGURANÇA). A encriptação protege suas chaves privadas em disco, então quem tiver acesso aos arquivos ainda assim não poderá gastar suas moedas.\n\nEscolha uma senha forte e não a perca — não há como recuperar uma carteira encriptada sem ela.",
|
||||
"faq_w_sec_1_q": "Como eu encripto minha carteira?",
|
||||
"faq_w_sec_2_a": "O PIN bloqueia a tela do app para que saldos e ações fiquem ocultos quando você se afasta. É um bloqueio de conveniência sobre a encriptação; você o configura durante o assistente ou em Configurações, depois de encriptar a carteira.",
|
||||
"faq_w_sec_2_q": "O que é o PIN / tela de bloqueio?",
|
||||
"faq_w_sec_3_a": "Não. A encriptação da carteira não pode ser contornada. Se você ainda tiver sua frase de recuperação, pode restaurar a carteira a partir dela em uma carteira nova e definir uma nova senha. Sem a senha ou a frase de recuperação, os fundos não podem ser recuperados.",
|
||||
"faq_w_sec_3_q": "Esqueci minha senha — ela pode ser recuperada?",
|
||||
"faq_w_sec_title": "Segurança e Encriptação",
|
||||
"faq_w_seed_1_a": "Vá em Configurações > Backup e Dados > Frase de recuperação. Anote as palavras no papel, em ordem, e guarde-as em um lugar seguro e offline. Qualquer pessoa com a frase pode gastar suas moedas, então nunca a guarde em uma foto, e-mail ou nota na nuvem.",
|
||||
"faq_w_seed_1_q": "Como eu faço backup da minha frase de recuperação?",
|
||||
"faq_w_seed_2_a": "Carteiras mais antigas (legadas) não eram baseadas em frase semente. A carteira pode migrar uma carteira legada para uma moderna, baseada em frase semente: ela cria uma nova carteira semente e transfere seus fundos para ela. Procure por \"Migrar para carteira semente…\" em Configurações > Backup e Dados. Esse recurso requer a versão atual do daemon.",
|
||||
"faq_w_seed_2_q": "Minha carteira não tem frase de recuperação — posso adicionar uma?",
|
||||
"faq_w_seed_3_a": "Use o fluxo de restauração por frase semente ao configurar uma carteira e digite suas palavras em ordem. Sua carteira então reescaneia para reconstruir seu saldo e histórico, o que pode levar um tempo na primeira vez.",
|
||||
"faq_w_seed_3_q": "Como eu restauro a partir da minha frase de recuperação?",
|
||||
"faq_w_seed_4_a": "Sua frase de recuperação é o backup principal e é suficiente para restaurar tudo. Uma cópia do arquivo da sua carteira é um backup secundário conveniente que também preserva rótulos e configurações. Mantenha qualquer backup offline e privado.",
|
||||
"faq_w_seed_4_q": "Devo também fazer backup do wallet.dat?",
|
||||
"faq_w_seed_title": "Frase de Recuperação e Backup",
|
||||
"faq_w_send_1_a": "Abra a aba Enviar, cole o endereço do destinatário, informe um valor e confirme. Se a carteira estiver encriptada, você será solicitado a desbloqueá-la para a transação.\n\nEnvios para endereços blindados são privados; envios para endereços transparentes são públicos.",
|
||||
"faq_w_send_1_q": "Como eu envio fundos?",
|
||||
"faq_w_send_2_a": "As taxas do DragonX são muito baixas e definidas automaticamente. A taxa é mostrada antes de você confirmar. Normalmente você não precisa alterá-la.",
|
||||
"faq_w_send_2_q": "Qual taxa eu pago?",
|
||||
"faq_w_send_3_a": "Sim — ao enviar para um endereço blindado (z) você pode incluir um memo encriptado. Só o destinatário pode lê-lo. Endereços transparentes não suportam memos.",
|
||||
"faq_w_send_3_q": "Posso anexar uma mensagem a um pagamento?",
|
||||
"faq_w_send_4_a": "Uma transação fica \"pendente\" até ser minerada em um bloco e ganhar confirmações. Isso costuma levar um ou dois minutos. Transações blindadas também precisam que sua carteira esteja sincronizada para gerar a prova.\n\nSe um envio permanecer pendente por tempo incomumente longo, verifique se sua carteira está conectada e sincronizada (veja a barra de status na parte inferior da janela).",
|
||||
"faq_w_send_4_q": "Enviei um pagamento, mas ele diz pendente — por quê?",
|
||||
"faq_w_send_title": "Envio e recebimento",
|
||||
"faq_w_set_1_a": "Abra Configurações > Aparência para escolher um tema e um destaque. Vários skins claros e escuros estão incluídos, e efeitos como o desfoque podem ser desativados em máquinas menos potentes.",
|
||||
"faq_w_set_1_q": "Como mudo o tema?",
|
||||
"faq_w_set_2_a": "Sim — Configurações > Aparência tem um seletor de idioma. A carteira já vem com várias traduções; o que ainda não foi traduzido volta para o inglês.",
|
||||
"faq_w_set_2_q": "Posso mudar o idioma?",
|
||||
"faq_w_set_3_a": "Use a opção de escala de fonte em Configurações > Aparência. Ela redimensiona toda a interface, o que é útil em telas de alta resolução (HiDPI), onde o app pode renderizar pequeno demais.",
|
||||
"faq_w_set_3_q": "O texto está pequeno demais (ou grande demais) — dá para redimensionar?",
|
||||
"faq_w_set_4_a": "Ative a busca de preços em Configurações. A carteira passa a mostrar um valor aproximado em moeda fiduciária ao lado dos saldos e pode exibir um pequeno gráfico de mercado. Os preços são apenas informativos.",
|
||||
"faq_w_set_4_q": "Como exibo preços em moeda fiduciária?",
|
||||
"faq_w_set_title": "Configurações e aparência",
|
||||
"favorite_address": "Adicionar aos favoritos",
|
||||
"fee": "Taxa",
|
||||
"fee_high": "Alta",
|
||||
@@ -593,6 +878,63 @@
|
||||
"general": "Geral",
|
||||
"generating": "Gerando",
|
||||
"go_to_receive": "Ir para Receber",
|
||||
"grpa_current_block_paren": "(Atual: %d)",
|
||||
"grpa_days_ago": "há %lld dias",
|
||||
"grpa_dbg_addrman": "Rastreamento e gerenciamento de endereços de pares",
|
||||
"grpa_dbg_alert": "Mensagens do sistema de alertas",
|
||||
"grpa_dbg_bench": "Medições de tempo de benchmark das operações",
|
||||
"grpa_dbg_coindb": "Operações de leitura/gravação do banco de dados de moedas",
|
||||
"grpa_dbg_db": "Operações do Berkeley DB",
|
||||
"grpa_dbg_estimatefee": "Algoritmo de estimativa de taxa",
|
||||
"grpa_dbg_http": "Atividade do servidor RPC HTTP",
|
||||
"grpa_dbg_libevent": "Biblioteca de rede Libevent",
|
||||
"grpa_dbg_lock": "Depuração de contenção de bloqueios",
|
||||
"grpa_dbg_mempool": "Atividade do pool de memória de transações",
|
||||
"grpa_dbg_net": "Conexões e mensagens de rede",
|
||||
"grpa_dbg_paymentdisclosure": "Protocolo de divulgação de pagamento",
|
||||
"grpa_dbg_pow": "Atividade de mineração de prova de trabalho",
|
||||
"grpa_dbg_proxy": "Conexões de proxy SOCKS5",
|
||||
"grpa_dbg_prune": "Operações de poda de blocos",
|
||||
"grpa_dbg_rand": "Geração de números aleatórios",
|
||||
"grpa_dbg_reindex": "Progresso da reindexação da blockchain",
|
||||
"grpa_dbg_rpc": "Processamento de comandos RPC",
|
||||
"grpa_dbg_selectcoins": "Seleção de moedas para transações",
|
||||
"grpa_dbg_tor": "Integração com Tor e informações de circuito",
|
||||
"grpa_dbg_zmq": "Sistema de notificações ZeroMQ",
|
||||
"grpa_dbg_zrpc": "Operações RPC blindadas (z-address)",
|
||||
"grpa_enter_private_key_to_import": "Insira uma chave privada para importar.",
|
||||
"grpa_error_prefix": "Erro: ",
|
||||
"grpa_hr_ago": "há %lld h",
|
||||
"grpa_invalid_response_from_daemon": "Resposta inválida do daemon",
|
||||
"grpa_invalid_suffix": " (inválido)",
|
||||
"grpa_min_ago": "há %lld min",
|
||||
"grpa_sec_ago": "há %lld s",
|
||||
"grpa_seed_demo_chat": "Chat de demonstração da semente",
|
||||
"grpa_showing_first_100_of": "... mostrando os primeiros 100 de %d",
|
||||
"grpa_tab_about": "Sobre",
|
||||
"grpa_tab_appearance": "Aparência",
|
||||
"grpa_tab_backup_data": "Backup e Dados",
|
||||
"grpa_tab_chat": "Chat",
|
||||
"grpa_tab_explorer": "Explorer",
|
||||
"grpa_tab_node_security": "Nó e Segurança",
|
||||
"grpa_tab_wallet": "Carteira",
|
||||
"grpa_unexpected_getblockhash_result": "resultado inesperado de getblockhash",
|
||||
"grpb_copy": "Copiar",
|
||||
"grpb_max": "Máx",
|
||||
"grpb_new_badge_suffix": " [NOVO]",
|
||||
"grpb_preview_msg_payment_through": "O pagamento foi concluído? 🙂",
|
||||
"grpb_preview_msg_sending_rest": "Enviando o restante agora 👍",
|
||||
"grpb_preview_msg_yep_confirmed": "Sim — acabei de confirmar ✅",
|
||||
"grpb_selected_suffix": "\n(selecionado)",
|
||||
"grpb_tooltip_address_balance": "%s\nSaldo: %.8f %s%s",
|
||||
"grpb_undo_clear": "Desfazer Limpar",
|
||||
"grpc_benchmark_inconclusive": "Benchmark inconclusivo: nenhuma amostra de taxa de hash foi registrada. Verifique a conexão com a pool e tente novamente.",
|
||||
"grpc_benchmark_takes_secs": "O benchmark leva cerca de %ds e interrompe a mineração. Clique novamente para iniciar.",
|
||||
"grpc_bootstrap_failed": "Falha no bootstrap",
|
||||
"grpc_bootstrap_not_initialized": "Bootstrap não inicializado",
|
||||
"grpc_hashrate_fee": "%s %s%% de taxa",
|
||||
"grpc_key_not_available": "Chave não disponível para este endereço",
|
||||
"grpc_na": "N/D",
|
||||
"height": "Altura",
|
||||
"help": "Ajuda",
|
||||
"hidden_tag": " (oculto)",
|
||||
@@ -817,9 +1159,27 @@
|
||||
"memo_optional": "MEMO (OPCIONAL)",
|
||||
"memo_upper": "MEMO",
|
||||
"memo_z_only": "Nota: Memos só estão disponíveis ao enviar para endereços blindados (z)",
|
||||
"merge_addr_created": "Endereço blindado criado.",
|
||||
"merge_advanced": "Avançado",
|
||||
"merge_back": "Voltar",
|
||||
"merge_batch_fmt": "Mescla até %d entradas por execução — repita para concluir o restante.",
|
||||
"merge_confirm_btn": "Confirmar",
|
||||
"merge_confirm_fmt": "Consolidar ~%s DRGX de %d entrada(s) em %s?",
|
||||
"merge_create_zaddr": "Criar endereço blindado",
|
||||
"merge_creating": "Criando endereço…",
|
||||
"merge_description": "Fundir múltiplos UTXOs em um único endereço blindado. Isso pode ajudar a reduzir o tamanho da carteira e melhorar a privacidade.",
|
||||
"merge_fee_hint": "Taxa de rede para esta transação.",
|
||||
"merge_funds": "Fundir Fundos",
|
||||
"merge_max_inputs": "Máx. de entradas por lote",
|
||||
"merge_no_spendable": "Ainda não há entradas disponíveis para consolidar.",
|
||||
"merge_progress": "Consolidando… isso pode levar alguns minutos. Você pode fechar esta janela.",
|
||||
"merge_scope_fmt": "%d entradas transparentes + %d blindadas · ~%s DRGX disponíveis",
|
||||
"merge_scope_loading": "Verificando suas entradas…",
|
||||
"merge_send_failed": "Falha na fusão: ",
|
||||
"merge_source": "Consolidar",
|
||||
"merge_src_both": "Ambos",
|
||||
"merge_src_shielded": "Blindado",
|
||||
"merge_src_transparent": "Transparente",
|
||||
"merge_started": "Operação de fusão iniciada",
|
||||
"merge_title": "Fundir para Endereço",
|
||||
"mig_adopt": "Tornar esta minha carteira",
|
||||
@@ -1059,6 +1419,13 @@
|
||||
"peers_upper": "PARES",
|
||||
"peers_version": "Versão",
|
||||
"pending": "Pendente",
|
||||
"picker_dat_count": "%d arquivo(s) de carteira nesta pasta",
|
||||
"picker_dat_none": "Nenhum arquivo de carteira diretamente nesta pasta",
|
||||
"picker_empty": "Esta pasta não tem subpastas nem arquivos de carteira.",
|
||||
"picker_home": "Pasta pessoal",
|
||||
"picker_select": "Escanear esta pasta",
|
||||
"picker_title": "Selecione uma pasta para escanear",
|
||||
"picker_up": "Subir um nível",
|
||||
"pin_change_desc": "Altere seu PIN de desbloqueio. Você precisa do seu PIN atual e de um novo PIN.",
|
||||
"pin_confirm_new_label": "Confirmar novo PIN:",
|
||||
"pin_current_label": "PIN atual:",
|
||||
@@ -1195,6 +1562,7 @@
|
||||
"save_z_transactions": "Salvar Z-tx na lista de tx",
|
||||
"sb_auth_failed": "Autenticação falhou — verifique rpcuser/rpcpassword",
|
||||
"sb_block": "Bloco: %d",
|
||||
"sb_block_db_unreadable": "Banco de dados de blocos ilegível — reconstrução necessária",
|
||||
"sb_building_witnesses": "Definindo testemunhas",
|
||||
"sb_building_witnesses_pct": "Reconstruindo testemunhas %.0f%%",
|
||||
"sb_connecting_daemon": "Conectando ao dragonxd...",
|
||||
@@ -1210,6 +1578,7 @@
|
||||
"sb_dragonxd_stopped": "dragonxd parado",
|
||||
"sb_dragonxd_stopping": "Parando dragonxd...",
|
||||
"sb_extracting_sapling": "Extraindo parâmetros Sapling...",
|
||||
"sb_finishing_repair": "Concluindo o reparo da carteira — reescaneando…",
|
||||
"sb_importing_keys": "Importando chaves",
|
||||
"sb_loading_config": "Carregando configuração...",
|
||||
"sb_mining_hs": "%.1f H/s",
|
||||
@@ -1231,6 +1600,7 @@
|
||||
"sb_waiting_config": "Aguardando configuração do daemon...",
|
||||
"sb_waiting_daemon": "Aguardando dragonxd...",
|
||||
"sb_waiting_daemon_err": "Aguardando dragonxd — %s",
|
||||
"sb_wallet_needs_recovery": "Reparo de carteira disponível",
|
||||
"sb_warming_up": "Aquecendo...",
|
||||
"sb_witness_cache": "Reconstruindo testemunhas",
|
||||
"scale_effects": "ESCALA E EFEITOS",
|
||||
@@ -1240,6 +1610,45 @@
|
||||
"screenshot_sweep_full": "Varredura completa da interface",
|
||||
"search_icons": "Pesquisar ícones...",
|
||||
"search_placeholder": "Pesquisar...",
|
||||
"sec_changing_passphrase": "Alterando a frase-senha...",
|
||||
"sec_changing_pin": "Alterando o PIN...",
|
||||
"sec_couldnt_lock_wallet": "Não foi possível bloquear a carteira — ela continua desbloqueada. Verifique a conexão com o daemon.",
|
||||
"sec_encrypted_backup_suffix": "\nBackup criptografado: wallet.dat.encrypted.bak",
|
||||
"sec_encrypting_wallet": "Criptografando a carteira...",
|
||||
"sec_encryption_did_not_complete": "A criptografia da carteira não foi concluída — sua carteira NÃO está criptografada. Abra as Configurações para terminar de criptografá-la.",
|
||||
"sec_encryption_failed_prefix": "Falha na criptografia: ",
|
||||
"sec_failed_prefix": "Falhou: ",
|
||||
"sec_failed_to_create_vault": "Falha ao criar o cofre",
|
||||
"sec_importing_keys_rescanning": "Importando chaves e reescaneando a blockchain — a carteira pode ser usada durante o processo",
|
||||
"sec_incorrect_current_pin": "PIN atual incorreto",
|
||||
"sec_incorrect_passphrase_decrypt": "Frase-senha incorreta",
|
||||
"sec_incorrect_passphrase_pin_setup": "Frase-senha incorreta",
|
||||
"sec_incorrect_pin_remove": "PIN incorreto",
|
||||
"sec_internal_error_change_pin": "Erro interno",
|
||||
"sec_internal_error_remove_pin": "Erro interno",
|
||||
"sec_mode_passphrase": " Frase-senha",
|
||||
"sec_not_connected_to_daemon": "Não conectado ao daemon",
|
||||
"sec_not_connected_to_daemon_pin": "Não conectado ao daemon",
|
||||
"sec_passphrase_changed_successfully": "Frase-senha alterada com sucesso",
|
||||
"sec_pin_changed_successfully": "PIN alterado com sucesso",
|
||||
"sec_pin_removed": "PIN removido",
|
||||
"sec_pin_set_successfully": "PIN definido com sucesso",
|
||||
"sec_restart_daemon_for_encryption": "Reinicie o daemon para que a criptografia entre em vigor.",
|
||||
"sec_too_many_attempts_wait": "Tentativas em excesso. Aguarde %.0f segundos...",
|
||||
"sec_total_elapsed_fmt": "Tempo total: %dm %02ds",
|
||||
"sec_unlock_button": "Desbloquear",
|
||||
"sec_unlock_failed_prefix": "Falha ao desbloquear: ",
|
||||
"sec_unlocking_fmt": "Desbloqueando%s",
|
||||
"sec_use_passphrase_instead": "Usar frase-senha",
|
||||
"sec_use_pin_instead": "Usar PIN",
|
||||
"sec_verifying_passphrase": "Verificando a frase-senha...",
|
||||
"sec_verifying_pin": "Verificando o PIN...",
|
||||
"sec_wallet_decrypted_all_keys_imported": "Carteira descriptografada com sucesso! Todas as chaves importadas.",
|
||||
"sec_wallet_encrypted_and_pin_set": "Carteira criptografada e PIN definido",
|
||||
"sec_wallet_encrypted_but_pin_vault_failed": "Carteira criptografada, mas o cofre do PIN falhou",
|
||||
"sec_wallet_encrypted_restarting_daemon": "Carteira criptografada. Reiniciando o daemon...",
|
||||
"sec_wallet_encrypted_successfully": "Carteira criptografada com sucesso",
|
||||
"sec_wallet_locked_title": "Carteira Bloqueada",
|
||||
"security": "SEGURANÇA",
|
||||
"seed_backup_button": "Frase de recuperação",
|
||||
"seed_backup_close": "Fechar",
|
||||
@@ -1263,6 +1672,7 @@
|
||||
"send_amount": "Valor",
|
||||
"send_amount_details": "DETALHES DO VALOR",
|
||||
"send_amount_upper": "VALOR",
|
||||
"send_available_note": "disponível",
|
||||
"send_cannot_send_now": "Não é possível enviar agora — verifique a conexão, a sincronização e o saldo disponível.",
|
||||
"send_clear_fields": "Limpar todos os campos do formulário?",
|
||||
"send_contacts_button": "Escolher dos contatos",
|
||||
@@ -1450,17 +1860,34 @@
|
||||
"status": "Status",
|
||||
"stop_external": "Parar daemon externo",
|
||||
"stop_mining": "Parar Mineração",
|
||||
"stratum_allowip_hint": "Permitir mineradores a partir de IP ou CIDR (em branco = apenas este computador)",
|
||||
"stratum_expose_warn": "Abre uma porta de mineração para a rede que você permitir — use apenas em uma LAN confiável.",
|
||||
"stratum_host": "Hospedar um pool de mineração (stratum)",
|
||||
"stratum_host_hint": "Os mineradores se conectam a este computador na porta 22769 (porta RPC + 1000) com um minerador stratum RandomX. Reinicie o daemon para aplicar.",
|
||||
"stratum_host_section": "HOSPEDAGEM DE POOL DE MINERAÇÃO",
|
||||
"submitting_transaction": "Enviando transação...",
|
||||
"success": "Sucesso",
|
||||
"summary": "Resumo",
|
||||
"sweep_button": "Varrer",
|
||||
"sweep_caveat": "Importa a chave para assinar uma transação que move todos os seus fundos para o seu endereço. A chave permanece na carteira com saldo zero.",
|
||||
"sweep_current_theme_only": "Apenas o tema atual",
|
||||
"sweep_dest_label": "Enviar fundos varridos para",
|
||||
"sweep_dest_new": "Novo endereço blindado (recomendado)",
|
||||
"sweep_done": "Concluído — fundos varridos para o seu endereço.",
|
||||
"sweep_to": "Varrido para:",
|
||||
"sweep_toggle": "Varrer para minha carteira (não manter a chave)",
|
||||
"sweep_tx": "Transação:",
|
||||
"swin_connection_failed": "Falha na conexão: ",
|
||||
"swin_connection_successful": "Conexão bem-sucedida!\nversão do dragonxd: ",
|
||||
"swin_invalid_suffix": " (inválido)",
|
||||
"swin_no_history_file_found": "Nenhum arquivo de histórico encontrado",
|
||||
"swin_rescan_failed": "Falha no reescaneamento: ",
|
||||
"swin_rescan_started_from_block": "Reescaneamento iniciado a partir do bloco ",
|
||||
"swin_rescan_to": " até ",
|
||||
"swin_rpc_client_not_initialized": "Cliente RPC não inicializado",
|
||||
"swin_settings_saved": "Configurações salvas",
|
||||
"swin_theme_list_refreshed": "Lista de temas atualizada",
|
||||
"swin_ztx_history_cleared": "Histórico de transações Z limpo",
|
||||
"switch_corrupt_body": "Esta carteira parece corrompida — o nó não conseguiu abri-la. Restaure de um backup, recrie-a ou tente repará-la.",
|
||||
"switch_corrupt_repair": "Tentar reparar (salvage)",
|
||||
"switch_progress_background": "Continuar em segundo plano",
|
||||
@@ -1621,6 +2048,8 @@
|
||||
"tt_simple_bg": "Usar um gradiente simples para o fundo\\nAtalho: Ctrl+Up",
|
||||
"tt_simple_bg_alt": "Usar uma versão gradiente da imagem de fundo do tema\\nAtalho: Ctrl+Up",
|
||||
"tt_stop_external": "Aplica-se ao conectar a um daemon\\niniciado fora desta carteira",
|
||||
"tt_stratum_host": "Executa um servidor de pool stratum RandomX neste nó para que outros mineradores RandomX possam apontar para este computador. Requer um nó v1.3.0+ e reinício do daemon para aplicar.",
|
||||
"tt_sweep_current_theme_only": "Varrer apenas o tema ativo em vez de percorrer todos os temas",
|
||||
"tt_test_conn": "Verificar a conexão RPC ao daemon",
|
||||
"tt_theme_effects": "Brilho, luminescência, ciclo de matiz por tema",
|
||||
"tt_theme_hotkey": "Atalho: Ctrl+Esquerda/Direita para alternar temas",
|
||||
@@ -1629,6 +2058,7 @@
|
||||
"tt_ui_opacity": "Opacidade de cartões e barra lateral (100% = totalmente opaco, menor = mais transparente)",
|
||||
"tt_validate": "Verificar se um endereço DragonX é válido",
|
||||
"tt_verbose": "Registrar diagnósticos detalhados de conexão,\\nestado do daemon e info de proprietário de porta\\nna aba Console",
|
||||
"tt_wallet_size_warn": "Carteiras blindadas crescem com os dados de testemunha de cada nota — mesclar várias notas em um único endereço reduz esse tamanho. Faça backup antes.",
|
||||
"tt_wallets_button": "Liste os arquivos de carteira e alterne entre eles",
|
||||
"tt_website": "Abrir o site do DragonX",
|
||||
"tt_window_opacity": "Opacidade do fundo (menor = área de trabalho visível através da janela)",
|
||||
@@ -1695,8 +2125,68 @@
|
||||
"view_on_explorer": "Ver no Explorador",
|
||||
"waiting_for_daemon": "Aguardando conexão com o daemon...",
|
||||
"wallet": "CARTEIRA",
|
||||
"wallet_degraded_notify": "Sua carteira foi aberta em modo de função reduzida: os fundos existentes estão seguros e disponíveis para gasto, mas a criação de novos endereços e o blindamento estão desativados. Faça backup da sua frase de recuperação e restaure-a para reparar totalmente a carteira.",
|
||||
"wallet_diagnostics_hdr": "DIAGNÓSTICOS",
|
||||
"wallet_empty": "Sua carteira está vazia",
|
||||
"wallet_empty_hint": "Mude para Receber para obter seu endereço e começar a receber fundos.",
|
||||
"wallet_options_hdr": "OPÇÕES",
|
||||
"wallet_rebuild_failed": "A reconstrução não produziu uma carteira válida, então nada foi alterado. Sua carteira permanece intacta.",
|
||||
"wallet_rebuild_install_failed": "Não foi possível instalar a carteira reconstruída; sua carteira atual foi mantida.",
|
||||
"wallet_rebuild_no_helper": "O assistente de reconstrução de carteira não está disponível nesta build. Use Restaurar, ou reconstrua manualmente.",
|
||||
"wallet_rebuild_no_source": "Não foi possível encontrar uma carteira legível para reconstruir. Nada foi alterado.",
|
||||
"wallet_rebuild_ok": "Banco de dados da carteira reconstruído — o nó está carregando-o e reescaneando para encontrar seu saldo.",
|
||||
"wallet_rebuild_started": "Reparando seu arquivo de carteira — não feche esta janela.",
|
||||
"wallet_recovered_body": "Nada foi excluído. Sua carteira original continua salva no seu computador como um arquivo de backup datado, e cada opção abaixo apenas copia ou renomeia arquivos — nunca apaga nenhum. Suas chaves nunca são regeneradas, apenas relidas.\n\nA cópia reparada carregada agora pode estar sem algumas transações recentes, então seu saldo pode parecer um pouco baixo até você concluir abaixo e ele reescanear.",
|
||||
"wallet_recovered_dismiss": "Agora não — manter a cópia reparada",
|
||||
"wallet_recovered_dismiss_sub": "Nenhum arquivo é alterado. Você pode reabrir isto a qualquer momento na barra de status; seu original permanece guardado com segurança de qualquer forma.",
|
||||
"wallet_recovered_notify": "Seu arquivo de carteira precisou de reparo — seu original foi guardado com segurança. Abra o app para ver suas opções.",
|
||||
"wallet_recovered_open_folder": "Mostrar os arquivos",
|
||||
"wallet_recovered_open_folder_sub": "Abre a pasta de dados da carteira para você inspecionar os arquivos de backup — nada é alterado.",
|
||||
"wallet_recovered_rebuild": "Reparar automaticamente (recomendado)",
|
||||
"wallet_recovered_rebuild_sub": "Relê cada registro recuperável do seu arquivo de carteira mais completo e grava um novo arquivo limpo, depois reinicia. A opção mais completa — seu arquivo atual é mantido como backup datado de qualquer forma.",
|
||||
"wallet_recovered_restore": "Restaurar o arquivo original em vez disso",
|
||||
"wallet_recovered_restore_sub": "Coloca de volta seu maior backup intocado, palavra por palavra, depois faz uma nova varredura — um pouco mais rápido, mas só tão completo quanto aquele arquivo era. Seu arquivo atual é mantido como backup datado de qualquer forma.",
|
||||
"wallet_recovered_safety": "Suas moedas estão seguras.",
|
||||
"wallet_recovered_title": "Seu arquivo de carteira precisa de um reparo rápido",
|
||||
"wallet_recovered_warn": "Ao iniciar, o app encontrou que seu arquivo de carteira não passou na verificação de consistência — isso costuma acontecer após uma atualização do app ou um desligamento incorreto. O app já protegeu seus dados: guardou o arquivo antigo à parte e carregou uma cópia reparada para você não ficar preso.",
|
||||
"wallet_recovery_decide_later": "Decidir depois",
|
||||
"wallet_recovery_decide_later_tip": "Fecha isto e mantém a cópia carregada agora. Nada é alterado, e você pode reparar quando quiser — a barra de status mantém um link de “Reparo de carteira disponível”.",
|
||||
"wallet_recovery_details_label": "Mostrar detalhes técnicos",
|
||||
"wallet_recovery_done": "Concluído",
|
||||
"wallet_recovery_failure_body": "O reparo foi executado, mas o resultado não passou na verificação (não pôde ser lido, ou não tinha endereços), então foi descartado automaticamente antes de substituir qualquer coisa. Sua carteira está exatamente como estava antes de você clicar — nada no disco mudou.",
|
||||
"wallet_recovery_failure_title": "O reparo não foi concluído",
|
||||
"wallet_recovery_files_detail": "Sua carteira original ainda está aqui — o app a renomeou para um backup datado (wallet.<números>.bak) na sua pasta de dados e não apagou nada. “Reparar” lê cada registro do seu arquivo de carteira mais completo em um novo arquivo limpo. “Restaurar” copia seu maior backup de volta exatamente como está. A cópia carregada agora também é mantida como backup.",
|
||||
"wallet_recovery_files_label": "O que acontece com meus arquivos?",
|
||||
"wallet_recovery_notnow_short": "Agora não",
|
||||
"wallet_recovery_other_options": "Outras opções",
|
||||
"wallet_recovery_rebuild_card": "Reparar automaticamente",
|
||||
"wallet_recovery_rebuild_card_desc": "Lê cada registro recuperável em um arquivo limpo, depois reinicia. A opção mais completa.",
|
||||
"wallet_recovery_recommended": "RECOMENDADO",
|
||||
"wallet_recovery_repair_go": "Reparar",
|
||||
"wallet_recovery_rescan_body": "Refazendo a varredura da blockchain para reconstruir seu saldo e histórico de transações. Isso pode levar vários minutos — você não precisa fazer nada, e por favor não reinicie o node enquanto estiver em execução.",
|
||||
"wallet_recovery_rescan_elapsed": "Trabalhando há %s",
|
||||
"wallet_recovery_rescan_size": "A carteira reconstruída já está em %s e ainda está sendo preenchida",
|
||||
"wallet_recovery_rescan_slow": "Isso está demorando mais que o normal, o que é comum em uma carteira grande. É seguro deixar em execução — você pode acompanhar o progresso em Avançado ▸ Console.",
|
||||
"wallet_recovery_rescan_title": "Finalizando o reparo da sua carteira",
|
||||
"wallet_recovery_restore_card": "Restaurar original",
|
||||
"wallet_recovery_restore_card_desc": "Coloca de volta seu maior backup intocado, palavra por palavra — só tão completo quanto aquele arquivo era.",
|
||||
"wallet_recovery_restore_go": "Restaurar",
|
||||
"wallet_recovery_success_body": "O app carregou sua carteira reparada e está refazendo a varredura para totalizar seu saldo — isso pode levar alguns minutos. Ele lê cada registro que consegue, mas em arquivos danificados raros pode recuperar um pouco menos — ou ocasionalmente mais — endereços do que antes. Depois que a nova varredura terminar, confira se seu saldo e histórico estão corretos.",
|
||||
"wallet_recovery_success_restore": "Seu maior arquivo de backup foi colocado de volta e está carregando agora, e o app está refazendo a varredura. Foi restaurado palavra por palavra, então está exatamente tão completo quanto aquele arquivo era — confira seu saldo depois que a nova varredura terminar.",
|
||||
"wallet_recovery_try_other": "Tentar a outra opção",
|
||||
"wallet_recovery_whats_happened": "O que aconteceu?",
|
||||
"wallet_recovery_working_label": "Trabalhando",
|
||||
"wallet_restore_bad_backup": "O arquivo de backup da carteira parece ilegível, então NÃO foi restaurado — sua carteira atual está inalterada. Restaure a partir do seu próprio backup.",
|
||||
"wallet_restore_busy": "O node está ocupado reiniciando — tente novamente em instantes.",
|
||||
"wallet_restore_copy_failed": "Não foi possível instalar a carteira de backup; sua carteira atual foi mantida.",
|
||||
"wallet_restore_move_failed": "Não foi possível colocar a carteira atual de lado — nada foi alterado.",
|
||||
"wallet_restore_no_backup": "Não foi possível encontrar um wallet.<timestamp>.bak para restaurar. Nada foi alterado.",
|
||||
"wallet_restore_no_restart": "Sua carteira original foi restaurada, mas o node não reiniciou — inicie-o em Configurações.",
|
||||
"wallet_restore_ok": "Carteira original restaurada. O node está carregando-a agora.",
|
||||
"wallet_restore_started": "Restaurando seu arquivo original — por favor não feche esta janela.",
|
||||
"wallet_restore_stop_failed": "O node não parou a tempo, então nada foi alterado. Tente novamente.",
|
||||
"wallet_size_consolidate": "Consolidar notas…",
|
||||
"wallet_size_warn": "Este arquivo de carteira está grande. Consolidar suas notas pode conter o crescimento.",
|
||||
"wallets_active": "Ativa",
|
||||
"wallets_add": "Adicionar pasta",
|
||||
"wallets_add_folder": "Verificar também outra pasta em busca de arquivos de carteira:",
|
||||
@@ -1739,6 +2229,7 @@
|
||||
"wallets_open_inplace_tt": "Abre esta carteira onde está — vinculada ao diretório de dados (sem cópia)",
|
||||
"wallets_remove_folder": "Parar de verificar esta pasta",
|
||||
"wallets_reveal": "Mostrar pasta",
|
||||
"wallets_scan_folder": "Escanear outra pasta em busca de carteiras…",
|
||||
"wallets_scanned_folders": "Pastas verificadas:",
|
||||
"wallets_sort_addresses": "Endereços",
|
||||
"wallets_sort_asc": "Crescente (mais antigo / menos / menor primeiro)",
|
||||
@@ -1808,19 +2299,30 @@
|
||||
"wiz_welcome_sub": "Algumas escolhas rápidas e o seu full node estará pronto.",
|
||||
"wiz_welcome_title": "Bem-vindo ao ObsidianDragon!",
|
||||
"wizard_daemon_start_failed": "Falha ao iniciar o daemon — será tentado novamente automaticamente",
|
||||
"xmrig_active_tt": "O minerador atualmente instalado (ativo)",
|
||||
"xmrig_browse_releases": "Navegar por todos os lançamentos…",
|
||||
"xmrig_checking": "Verificando o minerador mais recente…",
|
||||
"xmrig_current": "Atual:",
|
||||
"xmrig_downgrade_note": "Instalar uma versão diferente do minerador substitui a atual; pare de minerar primeiro, e a mudança terá efeito na próxima inicialização.",
|
||||
"xmrig_download_install": "Baixar e instalar",
|
||||
"xmrig_downloading": "Baixando…",
|
||||
"xmrig_install_this": "Instalar esta versão",
|
||||
"xmrig_installed": "Instalado:",
|
||||
"xmrig_installed_ok": "Minerador instalado",
|
||||
"xmrig_installing": "Instalando…",
|
||||
"xmrig_latest": "Mais recente:",
|
||||
"xmrig_latest_badge": "mais recente",
|
||||
"xmrig_latest_tt": "Versão mais recente disponível",
|
||||
"xmrig_loading_releases": "Carregando versões…",
|
||||
"xmrig_no_notes": "Sem notas de lançamento para esta versão.",
|
||||
"xmrig_none": "nenhum",
|
||||
"xmrig_prerelease_tt": "Build de pré-lançamento / teste",
|
||||
"xmrig_reinstall": "Reinstalar",
|
||||
"xmrig_released": "Lançado em:",
|
||||
"xmrig_releases": "versões do xmrig",
|
||||
"xmrig_status_current": "Esta versão está instalada",
|
||||
"xmrig_status_latest": "Versão mais recente",
|
||||
"xmrig_status_older": "Mais antiga que sua versão instalada",
|
||||
"xmrig_stop_mining_first": "Pare a mineração antes de atualizar o minerador.",
|
||||
"xmrig_unavailable_body": "Nenhuma versão do minerador está disponível para esta plataforma.",
|
||||
"xmrig_unavailable_title": "Atualizações do minerador indisponíveis",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"about_license_text": "Это программное обеспечение выпущено под лицензией GNU General Public License v3 (GPLv3). Вы можете свободно использовать, изменять и распространять это ПО в соответствии с условиями лицензии.",
|
||||
"about_peers_count": "%zu узлов",
|
||||
"about_release": "Релиз",
|
||||
"about_source": "Источник",
|
||||
"about_title": "Об ObsidianDragon",
|
||||
"about_version": "Версия:",
|
||||
"about_website": "Веб-сайт",
|
||||
@@ -60,7 +61,84 @@
|
||||
"amount_label": "Сумма:",
|
||||
"animate_avatars": "Анимировать аватары",
|
||||
"appearance": "ВНЕШНИЙ ВИД",
|
||||
"appx_back": "Назад",
|
||||
"appx_back_up_seed_phrase_title": "Сохраните seed-фразу",
|
||||
"appx_birthday_block_height": "Дата создания (высота блока): %llu — сохраните и её.",
|
||||
"appx_blockchain_data_deleted": "Данные блокчейна удалены (%d элементов). Демон перезапускается для повторной синхронизации с сетью.",
|
||||
"appx_blockchain_maintenance_in_progress": "Операция обслуживания блокчейна уже выполняется.",
|
||||
"appx_blockchain_rescan_complete": "Повторное сканирование блокчейна завершено",
|
||||
"appx_bootstrap_complete_reconciling": "Начальная загрузка завершена — сверка кошелька с новыми данными цепи.",
|
||||
"appx_cancel": "Отмена",
|
||||
"appx_cleaning_up": "Очистка...",
|
||||
"appx_confirm_your_backup": "Подтвердите резервную копию",
|
||||
"appx_copied_clipboard_autoclears": "Скопировано — буфер обмена очистится через 45 с",
|
||||
"appx_copy": "Копировать",
|
||||
"appx_could_not_start_restore": "Не удалось начать восстановление",
|
||||
"appx_create_failed_prefix": "Ошибка создания: ",
|
||||
"appx_creating_your_wallet": "Создание кошелька…",
|
||||
"appx_daemon_error": "Ошибка демона",
|
||||
"appx_daemon_reinstall_in_progress": "Переустановка демона уже выполняется.",
|
||||
"appx_disconnecting": "Отключение...",
|
||||
"appx_done": "Готово",
|
||||
"appx_dragonxd_output": "вывод dragonxd",
|
||||
"appx_encrypting_wallet": "Шифрование кошелька...",
|
||||
"appx_fullnode_lifecycle_unavailable_lite": "Действия жизненного цикла полного узла недоступны в lite-сборке",
|
||||
"appx_installing_bundled_daemon": "Установка встроенного демона — узел остановится, обновится и перезапустится...",
|
||||
"appx_invalid_payment_uri_prefix": "Неверный платёжный URI: ",
|
||||
"appx_ive_written_it_down": "Я записал её",
|
||||
"appx_keep_node_running_and_quit": "Оставить узел и выйти",
|
||||
"appx_last_block_n": "Последний блок: %d",
|
||||
"appx_last_used_wallet_not_found_prefix": "Ваш последний использованный файл кошелька (",
|
||||
"appx_last_used_wallet_not_found_suffix": ") не найден — вместо него открыт кошелёк по умолчанию. Если вы его переместили, восстановите файл и переключитесь обратно из списка кошельков.",
|
||||
"appx_low_spec_mode_disabled": "Режим слабого оборудования выключен",
|
||||
"appx_low_spec_mode_enabled": "Режим слабого оборудования включён",
|
||||
"appx_miner_stopped_prefix": "Майнер остановлен: ",
|
||||
"appx_miner_stopped_unexpectedly": "Майнер неожиданно остановился.",
|
||||
"appx_n_min_n_sec": "%d мин %d сек",
|
||||
"appx_n_seconds": "%d сек",
|
||||
"appx_no_bundled_daemon_to_install": "В этой сборке нет встроенного демона для установки",
|
||||
"appx_no_embedded_daemon_to_install": "В этой сборке нет встроенного демона для установки",
|
||||
"appx_node_busy_restarting": "Узел занят перезапуском — попробуйте ещё раз через мгновение.",
|
||||
"appx_node_rebuilding_witness_cache": "Узел перестраивает кэш свидетелей",
|
||||
"appx_not_next_word": " — это не следующее слово",
|
||||
"appx_payment_request_loaded": "Запрос на оплату загружен",
|
||||
"appx_pool_miner_connected_and_hashing": "Пул-майнер подключён и вычисляет хеши.",
|
||||
"appx_progress_n_of_n": "Прогресс: %d / %d",
|
||||
"appx_rebuilding_sapling_note_witnesses": "Перестройка свидетелей заметок Sapling…",
|
||||
"appx_rebuilding_witness_cache_blocks_left": "Перестройка кэша свидетелей %.0f%% — осталось блоков: %d",
|
||||
"appx_rebuilding_witness_cache_pct": "Перестройка кэша свидетелей %.0f%%",
|
||||
"appx_recovery_phrase_word_count": "Фраза восстановления должна содержать 24 слова — у вас %d.",
|
||||
"appx_restarting_daemon_rescan_flag": "Перезапуск демона с флагом -rescan...",
|
||||
"appx_restarting_daemon_zapwallettxes": "Перезапуск демона с -zapwallettxes=2 (восстановление кошелька)...",
|
||||
"appx_restoring_your_wallet": "Восстановление кошелька…",
|
||||
"appx_seed_backup_warning": "Эти 24 слова — ЕДИНСТВЕННЫЙ способ восстановить кошелёк. Запишите их по порядку, храните офлайн и никому не сообщайте. Если вы их потеряете, средства пропадут навсегда.",
|
||||
"appx_seed_not_backed_up_warning": "Вы не сохранили seed-фразу — средства могут быть потеряны. Всё равно пропустить?",
|
||||
"appx_sending_stop_command_to_daemon": "Отправка команды остановки демону...",
|
||||
"appx_setting_initial_sapling_witnesses": "Установка начальных свидетелей Sapling %.0f%%",
|
||||
"appx_shutdown_complete": "Завершение работы выполнено",
|
||||
"appx_simple_background_disabled": "Простой фон выключен",
|
||||
"appx_simple_background_enabled": "Простой фон включён",
|
||||
"appx_skip": "Пропустить",
|
||||
"appx_skip_anyway": "Всё равно пропустить",
|
||||
"appx_still_status_prefix": "Всё ещё \"",
|
||||
"appx_still_status_suffix": "\" — принудительный выход сейчас может повредить данные цепи.",
|
||||
"appx_stop_anyway_and_quit": "Всё равно остановить и выйти",
|
||||
"appx_stopping_daemon_deleting_blockchain": "Остановка демона и удаление данных блокчейна...",
|
||||
"appx_stopping_node_discards_rebuild": "Остановка узла сейчас отменит текущую перестройку и запустит её заново (несколько минут) при следующем открытии кошелька. Вместо этого можно оставить узел работать.",
|
||||
"appx_stopping_pool_miner": "Остановка пул-майнера...",
|
||||
"appx_syncing_pct_block_n_of_n": "Синхронизация %.1f%% — Блок %d / %d",
|
||||
"appx_tap_words_in_order": "Нажимайте слова в правильном порядке, чтобы подтвердить, что вы их сохранили.",
|
||||
"appx_theme_effects_disabled": "Эффекты темы выключены",
|
||||
"appx_theme_effects_enabled": "Эффекты темы включены",
|
||||
"appx_theme_prefix": "Тема: ",
|
||||
"appx_use_settings_restart_daemon_hint": "Откройте Настройки > Перезапустить демон, чтобы повторить попытку",
|
||||
"appx_waiting_for_daemon_to_encrypt_wallet": "Ожидание шифрования кошелька демоном...",
|
||||
"appx_wallet_created_and_backed_up": "Кошелёк создан, резервная копия сохранена.",
|
||||
"appx_wallet_open_failed_prefix": "Не удалось открыть кошелёк: ",
|
||||
"auto_shield": "Авто-экранирование майнинга",
|
||||
"autoshield_by_node": "Ваш узел автоматически защищает добытый coinbase за вас.",
|
||||
"autoshield_off_backup_seed": "Добытый coinbase пока не защищается автоматически. Сделайте резервную копию сид-фразы (Настройки → Резервное копирование и данные), чтобы узел мог безопасно включить эту функцию.",
|
||||
"autoshield_off_generic": "Ваш узел сейчас не защищает добытый coinbase автоматически.",
|
||||
"av_intro": "Программы для майнинга часто помечаются как потенциально нежелательные. Выполните эти шаги, чтобы включить пул-майнинг:",
|
||||
"av_open_security": "Открыть Безопасность Windows",
|
||||
"av_step1": "Шаг 1: Добавить исключение",
|
||||
@@ -100,6 +178,17 @@
|
||||
"balance_shielded_fmt": "Экранировано: %.8f",
|
||||
"balance_syncing_pct": "Синхронизация %.1f%%",
|
||||
"balance_transparent_fmt": "Прозрачный: %.8f",
|
||||
"baltab_market": "Рынок",
|
||||
"baltab_market_price_4dp": "Рынок: $%.4f",
|
||||
"baltab_market_price_8dp": "Рынок: $%.8f",
|
||||
"baltab_pct_change_24h": "%s%.1f%% за 24ч",
|
||||
"baltab_pct_of_total_zaddr": "%.0f%% от общего · %d Z-addr",
|
||||
"baltab_shielded": "Скрытый",
|
||||
"baltab_shielded_amount": "Скрытый %.8f",
|
||||
"baltab_t_addresses_count": "%d T-адресов",
|
||||
"baltab_total_balance": "Общий баланс",
|
||||
"baltab_transparent": "Прозрачный",
|
||||
"baltab_transparent_amount": "Прозрачный %.8f",
|
||||
"ban": "Заблокировать",
|
||||
"banned_peers": "Заблокированные узлы",
|
||||
"block": "Блок",
|
||||
@@ -107,6 +196,12 @@
|
||||
"block_click_copy": "Нажмите для копирования",
|
||||
"block_click_next": "Нажмите для следующего блока",
|
||||
"block_click_prev": "Нажмите для предыдущего блока",
|
||||
"block_db_reindex_body": "Обычно это происходит после обновления демона, изменившего формат данных на диске, или при повреждении индекса блоков. Ваш кошелёк и монеты в безопасности — узел просто не может загрузить цепочку, поэтому баланс показывается как нулевой.\n\nПересборка заново считывает имеющиеся файлы блоков и может занять некоторое время (также пересканируется кошелёк). Ничего не загружается.",
|
||||
"block_db_reindex_confirm": "Пересобрать базу данных блоков",
|
||||
"block_db_reindex_notify": "Узел не может прочитать свою базу данных блоков (часто после обновления демона). Пересоберите её, чтобы восстановить баланс — см. подсказку или Настройки › Узел.",
|
||||
"block_db_reindex_started": "Пересборка базы данных блоков из ваших блоков — это может занять некоторое время.",
|
||||
"block_db_reindex_title": "Пересобрать базу данных блоков?",
|
||||
"block_db_reindex_warn": "Узел не может прочитать свою базу данных блоков.",
|
||||
"block_explorer": "Обозреватель блоков",
|
||||
"block_get_info": "Получить информацию о блоке",
|
||||
"block_hash": "Хэш блока:",
|
||||
@@ -154,6 +249,10 @@
|
||||
"chat_accent_theme": "Тема",
|
||||
"chat_add_contact": "Добавить контакт",
|
||||
"chat_awaiting_key": "Ожидание ответа",
|
||||
"chat_blocked_desc": "Заблокированные разговоры удаляются, а их сообщения — старые и новые — отбрасываются, пока вы не разблокируете. Разблокировка заново импортирует разговор из блокчейна.",
|
||||
"chat_blocked_manage": "Заблокированные",
|
||||
"chat_blocked_title": "Заблокированные разговоры",
|
||||
"chat_blocked_toast": "Разговор удалён и заблокирован",
|
||||
"chat_bubble_minimal": "Минимальный",
|
||||
"chat_bubble_rounded": "Скруглённый",
|
||||
"chat_bubble_square": "Прямоугольный",
|
||||
@@ -166,6 +265,16 @@
|
||||
"chat_contact_added": "Контакт добавлен — переименуйте его в Контактах",
|
||||
"chat_contact_request": "запрос контакта",
|
||||
"chat_copy_address_tip": "Нажмите, чтобы скопировать адрес",
|
||||
"chat_delete": "Удалить разговор",
|
||||
"chat_delete_block": "Удалить и заблокировать",
|
||||
"chat_delete_body_prefix": "Удалить локальную копию разговора с ",
|
||||
"chat_delete_body_suffix": "?",
|
||||
"chat_delete_confirm": "Удалить",
|
||||
"chat_delete_failed": "Не удалось удалить разговор — ничего не изменено.",
|
||||
"chat_delete_local_note": "Это влияет только на это устройство — сообщения остаются в блокчейне, и у собеседника сохранится своя копия.",
|
||||
"chat_delete_revive_note": "«Удалить» очищает историю на этом устройстве. Если вам снова напишут, разговор появится снова.",
|
||||
"chat_delete_title": "Удалить разговор?",
|
||||
"chat_deleted_toast": "Разговор удалён",
|
||||
"chat_density_comfortable": "Свободная",
|
||||
"chat_density_compact": "Компактная",
|
||||
"chat_emoji_color": "Цветной",
|
||||
@@ -189,6 +298,7 @@
|
||||
"chat_new_button": "Новый чат",
|
||||
"chat_new_message": "Сообщение",
|
||||
"chat_new_message_toast": "Новое зашифрованное сообщение",
|
||||
"chat_new_needs_zaddr": "Для чата нужен защищённый (z) адрес — прозрачные (t) адреса не могут принимать зашифрованные сообщения.",
|
||||
"chat_new_send": "Отправить запрос",
|
||||
"chat_new_title": "Новый чат",
|
||||
"chat_new_zaddr": "Z-адрес получателя",
|
||||
@@ -234,6 +344,8 @@
|
||||
"chat_ts_24h": "24 часа",
|
||||
"chat_ts_global": "Как глобально",
|
||||
"chat_ts_global_short": "Общий",
|
||||
"chat_unblock": "Разблокировать",
|
||||
"chat_unblocked_toast": "Разговор разблокирован",
|
||||
"chat_unhide": "Показать",
|
||||
"chat_unmute": "Включить уведомления",
|
||||
"chat_verify_key": "Ключ личности — сравните для проверки",
|
||||
@@ -286,6 +398,7 @@
|
||||
"console": "Консоль",
|
||||
"console_accents": "Цветовые акценты",
|
||||
"console_app": "Прил.",
|
||||
"console_auto_focus": "Фокус на поле ввода при открытии",
|
||||
"console_auto_scroll": "Авто-прокрутка",
|
||||
"console_available_commands": "Доступные команды:",
|
||||
"console_backend_reference": "Справочник команд бэкенда",
|
||||
@@ -380,11 +493,15 @@
|
||||
"console_tab_completion": "Tab для дополнения",
|
||||
"console_text_colors": "Цвета текста",
|
||||
"console_toggle_accents": "Переключить цветовые акценты строк",
|
||||
"console_toggle_auto_focus": "Помещать курсор в поле команд при открытии вкладки Консоль",
|
||||
"console_toggle_text_color": "Переключить цвета текста строк",
|
||||
"console_type_help": "Введите 'help' для списка команд",
|
||||
"console_welcome": "Добро пожаловать в консоль ObsidianDragon",
|
||||
"console_zoom_in": "Увеличить",
|
||||
"console_zoom_out": "Уменьшить",
|
||||
"consolidate_desc": "Объедините множество мелких входов в одну защищённую заметку. Меньше заметок означает файл кошелька меньшего размера и лучшую приватность.",
|
||||
"consolidate_funds_btn": "Объединить",
|
||||
"consolidate_title": "Объединение средств",
|
||||
"contact_avatar": "АВАТАР",
|
||||
"contact_avatar_bad_image": "Не удалось загрузить это изображение.",
|
||||
"contact_avatar_badge": "Значок",
|
||||
@@ -414,6 +531,7 @@
|
||||
"contacts_shape_square": "Квадрат",
|
||||
"contacts_shape_tab": "Вкладка",
|
||||
"copied": "Скопировано!",
|
||||
"copied_to_clipboard": "Скопировано в буфер обмена",
|
||||
"copy": "Копировать",
|
||||
"copy_address": "Копировать полный адрес",
|
||||
"copy_error": "Копировать ошибку",
|
||||
@@ -429,9 +547,13 @@
|
||||
"daemon_maintenance_label": "ОБСЛУЖИВАНИЕ",
|
||||
"daemon_none_bundled": "нет в этой сборке",
|
||||
"daemon_not_installed": "не установлен",
|
||||
"daemon_status_diff": "Версия отличается",
|
||||
"daemon_status_differ": "Установленный бинарный файл отличается от встроенной версии.",
|
||||
"daemon_status_match": "Установленный бинарник соответствует встроенной версии.",
|
||||
"daemon_status_missing": "Демон не установлен — установите встроенную версию.",
|
||||
"daemon_status_none": "Не установлен",
|
||||
"daemon_status_ok": "Актуальная версия",
|
||||
"daemon_update_active_tt": "Текущий установленный (активный) узел",
|
||||
"daemon_update_available": "Доступна новая версия узла",
|
||||
"daemon_update_body": "В эту сборку кошелька встроена более новая версия узла DragonX, чем установленная сейчас. При обновлении установленный dragonxd (а также dragonx-cli/dragonx-tx) заменяется, после чего узел останавливается и перезапускается, чтобы новая версия вступила в силу. Рекомендуется — более новый узел может добавить возможности (например, поддержку сид-фразы), которых нет в старом.",
|
||||
"daemon_update_browse": "Просмотреть все релизы…",
|
||||
@@ -441,19 +563,28 @@
|
||||
"daemon_update_download_install": "Скачать и установить",
|
||||
"daemon_update_downloading": "Загрузка…",
|
||||
"daemon_update_failed": "Ошибка обновления",
|
||||
"daemon_update_install_this": "Установить эту версию",
|
||||
"daemon_update_installed": "Установлено:",
|
||||
"daemon_update_installed_ok": "Узел установлен",
|
||||
"daemon_update_installing": "Установка…",
|
||||
"daemon_update_keep": "Оставить текущий",
|
||||
"daemon_update_later": "Позже",
|
||||
"daemon_update_latest": "Последняя:",
|
||||
"daemon_update_latest_badge": "новейшая",
|
||||
"daemon_update_latest_tt": "Новейшая доступная версия",
|
||||
"daemon_update_loading": "Загрузка релизов…",
|
||||
"daemon_update_no_notes": "Нет заметок о выпуске для этой версии.",
|
||||
"daemon_update_now": "Обновить сейчас",
|
||||
"daemon_update_prerelease_tt": "Предварительная / тестовая сборка",
|
||||
"daemon_update_prompt_title": "Обновить демон узла?",
|
||||
"daemon_update_reinstall": "Переустановить",
|
||||
"daemon_update_released": "Выпущено:",
|
||||
"daemon_update_restart_note": "Перезапустите daemon, чтобы начать работу с новой версией.",
|
||||
"daemon_update_restart_now": "Перезапустить демон сейчас",
|
||||
"daemon_update_safe": "Ваш кошелёк, ключи и данные блокчейна не затрагиваются — заменяются только программные файлы демона. Если вы намеренно используете собственный узел, выберите «Оставить текущий».",
|
||||
"daemon_update_status_current": "Эта версия установлена",
|
||||
"daemon_update_status_latest": "Последняя версия",
|
||||
"daemon_update_status_older": "Старее установленной версии",
|
||||
"daemon_update_title": "Обновить демон узла?",
|
||||
"daemon_update_unavailable_body": "Для этой платформы нет доступной сборки узла.",
|
||||
"daemon_update_unavailable_title": "Обновления узла недоступны",
|
||||
@@ -469,6 +600,13 @@
|
||||
"data_stale_tooltip": "Баланс может быть устаревшим — кошелёк давно не получал обновлений. Проверьте подключение к узлу.",
|
||||
"date": "Дата",
|
||||
"date_label": "Дата:",
|
||||
"debug_gate_confirm": "Показать отладочные параметры",
|
||||
"debug_gate_incorrect": "Неверный PIN или пароль.",
|
||||
"debug_gate_pass_prompt": "Введите пароль кошелька, чтобы продолжить:",
|
||||
"debug_gate_pin_prompt": "Введите PIN разблокировки, чтобы продолжить:",
|
||||
"debug_gate_title": "Показать отладочные параметры?",
|
||||
"debug_gate_verifying": "Проверка…",
|
||||
"debug_gate_warning": "Это расширенные диагностические и настроечные параметры узла. Они могут раскрыть конфиденциальную информацию и изменить работу вашего узла. Продолжайте, только если понимаете, что делаете.",
|
||||
"debug_logging": "ЖУРНАЛ ОТЛАДКИ",
|
||||
"decrypt_desc": "Кошелёк будет экспортирован, демон перезапущен с новым незашифрованным кошельком, и все ключи будут повторно импортированы. Это может занять несколько минут в зависимости от размера кошелька.",
|
||||
"decrypt_error_title": "Не удалось расшифровать",
|
||||
@@ -491,6 +629,7 @@
|
||||
"disconnected": "Отключено",
|
||||
"dismiss": "Отклонить",
|
||||
"display": "Отображение",
|
||||
"done": "Готово",
|
||||
"download": "Скачать",
|
||||
"download_bootstrap": "Скачать бутстрап",
|
||||
"dragonx_green": "DragonX (Зелёная)",
|
||||
@@ -544,6 +683,7 @@
|
||||
"explorer_tx_outputs": "Выходы",
|
||||
"explorer_tx_size": "Размер",
|
||||
"explorer_txid": "TxID:",
|
||||
"explorer_urls_hdr": "URL-АДРЕСА",
|
||||
"export": "Экспорт",
|
||||
"export_csv": "Экспорт в CSV",
|
||||
"export_keys_btn": "Экспорт ключей",
|
||||
@@ -571,6 +711,151 @@
|
||||
"export_viewing_key": "Экспорт ключа просмотра",
|
||||
"failed_create_shielded": "Не удалось создать экранированный адрес",
|
||||
"failed_create_transparent": "Не удалось создать прозрачный адрес",
|
||||
"faq": "ЧаВо",
|
||||
"faq_d_mgmt_1_a": "Встроенный узел запускается автоматически вместе с кошельком и останавливается, когда это уместно, поэтому обычно управлять им вручную не нужно. Расширенные элементы управления (включая перезапуск демона) находятся в Настройки > Узел и безопасность.",
|
||||
"faq_d_mgmt_1_q": "Как запустить или остановить узел?",
|
||||
"faq_d_mgmt_2_a": "Если вы подключаете кошелёк к узлу, который запустили сами, этот параметр определяет, останавливается ли этот узел при закрытии кошелька. Оставьте его выключенным, если хотите, чтобы узел продолжал работать после закрытия кошелька.",
|
||||
"faq_d_mgmt_2_q": "Что делает параметр «закрывать внешний демон при выходе»?",
|
||||
"faq_d_mgmt_3_a": "Данные узла и кошелька хранятся в каталоге данных DragonX в профиле вашего пользователя (например, в AppData в Windows или в домашней папке в Linux/macOS). Блокчейн занимает большую часть; оставьте достаточно свободного места на диске для его роста.",
|
||||
"faq_d_mgmt_3_q": "Где хранятся данные блокчейна и кошелька?",
|
||||
"faq_d_mgmt_title": "Управление узлом",
|
||||
"faq_d_mine_1_a": "Вкладка «Майнинг» позволяет майнить на вашем процессоре (CPU). DragonX использует алгоритм RandomX, разработанный для обычных процессоров. Задайте количество потоков и запустите — вознаграждения начисляются на ваш кошелёк.",
|
||||
"faq_d_mine_1_q": "Как майнить DragonX?",
|
||||
"faq_d_mine_2_a": "Соло-майнинг отправляет награду прямо вам, но платит только тогда, когда вы находите блок — а это редкость, если у вас нет большого хешрейта. Пул-майнинг делит работу с другими ради меньших, но более стабильных выплат. Вкладка «Майнинг» поддерживает оба варианта.",
|
||||
"faq_d_mine_2_q": "Соло или пул-майнинг — что выбрать?",
|
||||
"faq_d_mine_3_a": "В разделе пула на вкладке «Майнинг» есть кнопка «Обновить майнер…», которая скачивает, проверяет и устанавливает последнюю оптимизированную сборку майнера DragonX. Как и в случае с обновлением узла, каждая загрузка проверяется по контрольной сумме и цифровой подписи перед установкой.",
|
||||
"faq_d_mine_3_q": "Как обновить майнер?",
|
||||
"faq_d_mine_4_a": "Текущий узел может запускать встроенный stratum-сервер, чтобы другие майнеры могли подключаться к вашему компьютеру. Переключатель для этого находится в Настройки > Узел и безопасность. По умолчанию он слушает только локально; чтобы открыть его для других компьютеров, это нужно включить намеренно.",
|
||||
"faq_d_mine_4_q": "Можно ли разместить у себя майнинг-пул?",
|
||||
"faq_d_mine_title": "Майнинг и пулы",
|
||||
"faq_d_net_1_a": "Сразу после запуска узлу нужно немного времени, чтобы найти пиров, поэтому кратковременное «0 пиров» — это нормально. Если это продолжается долго, проверьте подключение к интернету и убедитесь, что брандмауэр не блокирует узел. Узел находит пиров автоматически через встроенные сиды.",
|
||||
"faq_d_net_1_q": "Кошелёк показывает отсутствие пиров или подключений — что не так?",
|
||||
"faq_d_net_2_a": "Да — узлы DragonX общаются друг с другом по TLS, поэтому соединения с пирами зашифрованы. Кошелёк также общается со своим узлом по защищённому локальному каналу.",
|
||||
"faq_d_net_2_q": "Зашифровано ли моё соединение с сетью?",
|
||||
"faq_d_net_title": "Пиры и сеть",
|
||||
"faq_d_node_1_a": "Демон, dragonxd, — это полный узел DragonX. Он скачивает и проверяет весь блокчейн, передаёт транзакции в сеть и отвечает на запросы кошелька. Запуск собственного узла означает, что вам не нужно доверять чужому серверу.",
|
||||
"faq_d_node_1_q": "Что такое демон (dragonxd)?",
|
||||
"faq_d_node_2_a": "По умолчанию кошелёк запускает и управляет собственным встроенным узлом (embedded) — вам не нужно ничего делать. Если у вас уже запущен свой dragonxd, кошелёк может подключиться к этому внешнему узлу вместо своего. Кошелёк определяет, какая ситуация имеет место, и действует соответственно.",
|
||||
"faq_d_node_2_q": "В чём разница между встроенным и внешним узлом?",
|
||||
"faq_d_node_3_a": "Полный узел независимо проверяет каждое правило блокчейна, поэтому ваш кошелёк доверяет математике, а не третьей стороне. Это также укрепляет сеть. Плата за это — место на диске и первоначальная синхронизация.",
|
||||
"faq_d_node_3_q": "Зачем вообще запускать полный узел?",
|
||||
"faq_d_node_title": "Полный узел",
|
||||
"faq_d_perf_1_a": "Блокчейн — самая большая часть, и он растёт со временем, поэтому держите свободными несколько гигабайт. Узел использует кеш базы данных для скорости; встроенные значения по умолчанию настроены для типичных машин. Продвинутые пользователи могут изменить размер кеша базы данных узла, отредактировав dbcache в файле конфигурации (DRAGONX.conf).",
|
||||
"faq_d_perf_1_q": "Сколько места на диске использует узел и можно ли настроить память?",
|
||||
"faq_d_perf_2_a": "Включите загрузку bootstrap (предлагается в мастере настройки), чтобы получить свежую проверенную копию цепочки вместо проверки каждого блока с нуля. Быстрый диск (SSD) также сильно помогает.",
|
||||
"faq_d_perf_2_q": "Как ускорить первоначальную синхронизацию?",
|
||||
"faq_d_perf_title": "Хранилище и производительность",
|
||||
"faq_d_sync_1_a": "При запуске узел загружает и проверяет блоки, прежде чем сможет обслуживать кошелёк. Экран «Обработка блоков / Применение блоков для построения текущего состояния цепочки» — это и есть такой разогрев. Это нормально; кошелёк становится доступен, как только узел закончит и сообщит свою высоту.",
|
||||
"faq_d_sync_1_q": "Что означает «Обработка блоков» при запуске?",
|
||||
"faq_d_sync_2_a": "Для ваших защищённых средств узел хранит криптографические «свидетели» (witnesses), которые позволяют тратить их приватно. После некоторых перезапусков или обновлений он перестраивает этот кеш, повторно сканируя недавние блоки. На кошельке со множеством транзакций это может занять несколько минут и потребовать дополнительной памяти.\n\nЭто не зависание — дайте процессу завершиться. Принудительное закрытие в середине процесса просто заставит его перезапустить перестроение в следующий раз.",
|
||||
"faq_d_sync_2_q": "Что такое «Перестроение кеша свидетелей»?",
|
||||
"faq_d_sync_3_a": "Вблизи вершины цепочки работа по сканированию кошелька (проверка балансов и заметок) конкурирует с узлом за одну и ту же внутреннюю блокировку, что может замедлить подключение последних блоков на крупном кошельке. Последние версии ограничивают это сканирование, чтобы узел мог наверстать упущенное. Если оставаться на более лёгкой вкладке (или просто подождать), процесс завершится быстрее.",
|
||||
"faq_d_sync_3_q": "Почему синхронизация медленная, когда я почти догнал сеть?",
|
||||
"faq_d_sync_4_a": "Первая синхронизация скачивает и проверяет всю цепочку и может занять некоторое время в зависимости от вашего соединения и диска. Вы можете значительно ускорить её, включив загрузку bootstrap в мастере настройки — она получает свежую проверенную копию данных цепочки.",
|
||||
"faq_d_sync_4_q": "Сколько занимает первоначальная синхронизация?",
|
||||
"faq_d_sync_title": "Синхронизация и блокчейн",
|
||||
"faq_d_trbl_1_a": "Обычно нет. На крупном кошельке узел может тратить несколько минут на перестроение защищённых свидетелей или применение блоков, в течение которых он выглядит приостановленным и может медленно отвечать. Дайте ему время — журнал отладки (в настройках) показывает стабильный прогресс, если всё работает.\n\nИзбегайте принудительного закрытия на этом этапе — иначе работа перезапустится с нуля при следующем запуске.",
|
||||
"faq_d_trbl_1_q": "Узел словно застрял на «Активация лучшей цепочки» — он завис?",
|
||||
"faq_d_trbl_2_a": "Если файл кошелька был восстановлен, но часть его ключевых данных отсутствует, узел может открыть его в ограниченном «пониженном» режиме, в котором нельзя создавать новые адреса или защищать средства. Самое надёжное решение — восстановить кошелёк из сид-фразы в новый кошелёк.",
|
||||
"faq_d_trbl_2_q": "Что такое «пониженный режим»?",
|
||||
"faq_d_trbl_3_a": "Пересканирование заставляет кошелёк заново прочитать цепочку, чтобы заново обнаружить ваши транзакции (полезно после импорта ключей). Переиндексация перестраивает базу данных блокчейна узла и нужна только в том случае, если эта база повреждена или несовместима после смены версии. Оба процесса могут занять некоторое время; запускайте их из Настройки > Узел и безопасность.",
|
||||
"faq_d_trbl_3_q": "Когда нужно переиндексировать или пересканировать?",
|
||||
"faq_d_trbl_4_a": "Использование памяти резко возрастает во время тяжёлой работы, например перестроения свидетелей или первоначальной синхронизации, а затем снижается после завершения. Постоянно высокое потребление памяти в состоянии простоя — это необычно; перезапуск кошелька сбрасывает его. На машине с малым объёмом ОЗУ убедитесь, что другие требовательные к памяти приложения не конкурируют за ресурсы.",
|
||||
"faq_d_trbl_4_q": "Узел использует много памяти — это нормально?",
|
||||
"faq_d_trbl_title": "Устранение неполадок",
|
||||
"faq_d_upd_1_a": "Откройте Настройки > Узел и безопасность > Бинарник демона и нажмите «Проверить обновления…». Кошелёк скачивает последнюю проверенную сборку узла и устанавливает её; новая версия вступает в силу при следующем запуске демона.",
|
||||
"faq_d_upd_1_q": "Как обновить узел?",
|
||||
"faq_d_upd_2_a": "Да — диалог обновления перечисляет все релизы, чтобы вы могли выбрать конкретную или более старую сборку. Будьте осторожны при откате на более старую версию: старый узел может не принять данные блокчейна, записанные более новым, и может потребоваться переиндексация.",
|
||||
"faq_d_upd_2_q": "Можно ли установить конкретную версию узла?",
|
||||
"faq_d_upd_3_a": "Каждый скачанный архив узла проверяется по контрольной сумме и криптографической подписи перед установкой. Если проверка не проходит, кошелёк отклоняет обновление.",
|
||||
"faq_d_upd_3_q": "Безопасно ли обновление?",
|
||||
"faq_d_upd_title": "Обновление узла",
|
||||
"faq_group_daemon": "Демон",
|
||||
"faq_group_wallet": "Кошелёк",
|
||||
"faq_intro": "Ответы о вашем кошельке и узле DragonX. Ищите или просматривайте темы ниже.",
|
||||
"faq_intro_lite": "Ответы о вашем Lite-кошельке. Ищите или просматривайте темы ниже.",
|
||||
"faq_l_gs_1_a": "ObsidianDragonLite — это облегчённый кошелёк DragonX. Вместо запуска полного узла он подключается к lite-серверу DragonX, поэтому запускается быстро и не скачивает весь блокчейн.\n\nОн управляет вашими ключами, балансами и отправкой так же, как и полный кошелёк — см. раздел «Lite-кошелёк» ниже о том, как работает подключение к серверу, и о компромиссе с приватностью.",
|
||||
"faq_l_gs_1_q": "Что такое ObsidianDragonLite?",
|
||||
"faq_l_lite_1_a": "Lite-кошелёк общается с lite-сервером DragonX вместо запуска собственного полного узла. Это делает его быстрым в запуске и лёгким по нагрузке на диск и трафик, поскольку он сам не скачивает и не проверяет весь блокчейн. Ваши приватные ключи всегда остаются на вашем устройстве — они никогда не отправляются на сервер.",
|
||||
"faq_l_lite_1_q": "Чем Lite-кошелёк отличается от полного кошелька?",
|
||||
"faq_l_lite_2_a": "Откройте настройки и выберите сервер. Вы можете сменить сервер в любой момент, и кошелёк заново синхронизируется с новым. Если сервер медленный или недоступен, попробуйте другой.",
|
||||
"faq_l_lite_2_q": "Как выбрать или сменить сервер?",
|
||||
"faq_l_lite_3_a": "Ваши ключи никогда не покидают устройство, а защищённые суммы остаются зашифрованными. Но lite-сервер видит, какими адресами интересуется ваш кошелёк, и ваш IP-адрес, поэтому он может связать ваши адреса между собой — это компромисс удобства ради приватности. Для максимальной приватности используйте кошелёк с полным узлом, который сам проверяет цепочку и не раскрывает ваши адреса серверу.",
|
||||
"faq_l_lite_3_q": "Так же ли приватен Lite-кошелёк, как полный узел?",
|
||||
"faq_l_lite_title": "Lite-кошелёк",
|
||||
"faq_l_sec_1_a": "Откройте Настройки > Кошелёк и задайте пароль в разделе безопасности. Шифрование защищает ваши приватные ключи на диске, поэтому тот, кто получит доступ к файлам, всё равно не сможет потратить ваши монеты.\n\nВыберите надёжный пароль и не теряйте его — восстановить зашифрованный кошелёк без него невозможно.",
|
||||
"faq_no_results": "Ничего не найдено. Попробуйте другой запрос.",
|
||||
"faq_open_tooltip": "Справка и FAQ",
|
||||
"faq_search_hint": "Поиск по FAQ…",
|
||||
"faq_title": "Справка и FAQ",
|
||||
"faq_w_addr_1_a": "Прозрачные адреса (начинаются с R или t) работают как в большинстве монет: суммы и адреса видны в блокчейне.\n\nЗащищённые адреса (Z-адреса, начинаются с zs) используют криптографию с нулевым разглашением — сумма, отправитель и получатель зашифрованы в цепочке. Балансы на защищённых адресах приватны.",
|
||||
"faq_w_addr_1_q": "В чём разница между прозрачными и защищёнными адресами?",
|
||||
"faq_w_addr_2_a": "По возможности используйте защищённые (Z) адреса — они сохраняют приватность вашего баланса и истории платежей. DragonX — приватная монета, и защищённый адрес используется по умолчанию для получения средств.\n\nПрозрачный адрес стоит использовать только если сервис, с которым вы работаете, не поддерживает защищённые адреса.",
|
||||
"faq_w_addr_2_q": "Какой тип адреса мне использовать?",
|
||||
"faq_w_addr_3_a": "Для защищённых адресов повторное использование допустимо и не раскрывает историю. Для прозрачных адресов использование нового адреса для каждого платежа повышает приватность. Создавайте новые адреса на вкладке «Получить».",
|
||||
"faq_w_addr_3_q": "Использовать один адрес повторно или создавать новый?",
|
||||
"faq_w_addr_4_a": "Экранирование перемещает монеты с прозрачного адреса на защищённый, делая их приватными. Новые добытые монеты поступают как прозрачные и экранируются автоматически.\n\nВы также можете экранировать монеты вручную из кошелька — экранирование — это обычная транзакция, поэтому она подтверждается за обычное время.",
|
||||
"faq_w_addr_4_q": "Что такое «экранирование»?",
|
||||
"faq_w_addr_title": "Адреса и приватность",
|
||||
"faq_w_bal_1_a": "Пока кошелёк синхронизируется, ваш баланс неполный и может отображаться как 0 — не все транзакции ещё просканированы. Он заполнится после завершения синхронизации.\n\nСледите за строкой состояния внизу окна: она показывает состояние соединения и высоту блока. После завершения синхронизации баланс будет точным.",
|
||||
"faq_w_bal_1_q": "Почему мой баланс 0 или не обновляется?",
|
||||
"faq_w_bal_2_a": "Каждый новый блок, добытый поверх блока с вашей транзакцией, добавляет одно подтверждение. Чем больше подтверждений, тем надёжнее закреплён платёж. Полученные средства становятся доступны для отправки после первого подтверждения.",
|
||||
"faq_w_bal_2_q": "Что такое подтверждения?",
|
||||
"faq_w_bal_3_a": "Общий баланс включает средства, которые ещё подтверждаются или временно заблокированы (например, монеты в процессе экранирования). Доступный баланс — это то, что можно отправить прямо сейчас. Они сравниваются по мере подтверждения транзакций.",
|
||||
"faq_w_bal_3_q": "В чём разница между общим и доступным балансом?",
|
||||
"faq_w_bal_title": "Баланс и синхронизация",
|
||||
"faq_w_chat_1_a": "Чат — это зашифрованный мессенджер на основе блокчейна, встроенный в кошелёк. Сообщения отправляются как приватные защищённые заметки, поэтому прочитать их могут только вы и ваш собеседник.",
|
||||
"faq_w_chat_1_q": "Что такое функция чата?",
|
||||
"faq_w_chat_2_a": "Ваша личность в чате формируется из сид-фразы кошелька, поэтому она всегда следует за кошельком — восстановите кошелёк, и ваша личность вернётся. Отдельный аккаунт или пароль не нужны.",
|
||||
"faq_w_chat_2_q": "Как работает моя личность в чате?",
|
||||
"faq_w_chat_3_a": "Добавьте контакт по его адресу на вкладке «Контакты», при желании указав имя и аватар. Контакты можно хранить только для текущего кошелька или использовать во всех кошельках, которые вы открываете на этом компьютере.",
|
||||
"faq_w_chat_3_q": "Как работают контакты?",
|
||||
"faq_w_chat_4_a": "Откройте разговор и используйте значки в его заголовке. «Скрыть» (глаз) убирает разговор из списка, но сохраняет все сообщения — новое сообщение снова покажет разговор. «Удалить» (корзина) открывает диалог: «Удалить» очищает историю на этом устройстве (если собеседник снова напишет вам, разговор появится вновь), а «Удалить и заблокировать» также останавливает будущие сообщения от него, пока вы не разблокируете его в списке «Заблокированные» над списком разговоров. Всё это действует только на этом устройстве — сообщения остаются в блокчейне, и у собеседника сохраняется своя копия, поэтому это не «отмена отправки».",
|
||||
"faq_w_chat_4_q": "Как скрыть, удалить или заблокировать разговор?",
|
||||
"faq_w_chat_title": "Чат и контакты",
|
||||
"faq_w_gs_1_a": "ObsidianDragon — это полноузловой кошелёк для DragonX (DRGX). Он управляет вашими монетами и в десктопной сборке запускает полный узел DragonX (демон) в фоне, поэтому кошелёк сам проверяет блокчейн, а не доверяет стороннему серверу.\n\nПредставьте это как две части, работающие вместе: кошелёк (ключи, балансы, отправка) и узел (блокчейн и сеть). Вопросы здесь разделены так же — см. вкладки «Кошелёк» и «Демон» выше.",
|
||||
"faq_w_gs_1_q": "Что такое ObsidianDragon?",
|
||||
"faq_w_gs_2_a": "При первом запуске мастер настройки автоматически создаёт для вас кошелёк. Новые кошельки защищены секретной сид-фразой (списком слов). Запишите эту фразу и храните её офлайн — это единственный способ восстановить средства, если этот компьютер будет потерян.\n\nПросмотреть или сохранить фразу можно в любой момент в Настройки > Резервное копирование и данные > Сид-фраза.",
|
||||
"faq_w_gs_2_q": "Как создать новый кошелёк?",
|
||||
"faq_w_gs_3_a": "Используйте восстановление по сид-фразе, если у вас есть сид-фраза. (В полноузловой сборке вы также можете скопировать существующий файл wallet.dat в папку данных кошелька перед запуском.)\n\nПосле восстановления кошелёк повторно сканирует блокчейн, чтобы найти ваши прошлые транзакции, поэтому баланс и история могут появиться не сразу.",
|
||||
"faq_w_gs_3_q": "Как восстановить уже имеющийся кошелёк?",
|
||||
"faq_w_gs_4_a": "Мастер позволяет выбрать оформление/тему, настроить шифрование и PIN, а также — в полноузловой сборке — при желании загрузить bootstrap для ускорения первой синхронизации. Любой шаг можно пропустить и изменить позже в Настройках.",
|
||||
"faq_w_gs_4_q": "Что делает первоначальная настройка?",
|
||||
"faq_w_gs_title": "Начало работы",
|
||||
"faq_w_sec_1_a": "Откройте Настройки > Узел и безопасность и задайте пароль (раздел БЕЗОПАСНОСТЬ). Шифрование защищает ваши приватные ключи на диске, поэтому тот, кто получит доступ к файлам, всё равно не сможет потратить ваши монеты.\n\nВыберите надёжный пароль и не теряйте его — восстановить зашифрованный кошелёк без него невозможно.",
|
||||
"faq_w_sec_1_q": "Как зашифровать свой кошелёк?",
|
||||
"faq_w_sec_2_a": "PIN блокирует экран приложения, чтобы скрыть балансы и действия, когда вы отходите. Это дополнительная блокировка поверх шифрования; вы настраиваете её в мастере настройки или в Настройках после шифрования кошелька.",
|
||||
"faq_w_sec_2_q": "Что такое PIN / экран блокировки?",
|
||||
"faq_w_sec_3_a": "Нет. Шифрование кошелька невозможно обойти. Если у вас сохранилась сид-фраза восстановления, вы можете восстановить кошелёк из неё в новый кошелёк и задать новый пароль. Без пароля или сид-фразы средства восстановить нельзя.",
|
||||
"faq_w_sec_3_q": "Я забыл пароль — можно ли его восстановить?",
|
||||
"faq_w_sec_title": "Безопасность и шифрование",
|
||||
"faq_w_seed_1_a": "Перейдите в Настройки > Резервное копирование и данные > Сид-фраза. Запишите слова на бумаге по порядку и храните их в надёжном месте офлайн. Любой, у кого есть эта фраза, может потратить ваши монеты, поэтому никогда не храните её в фото, письме или облачной заметке.",
|
||||
"faq_w_seed_1_q": "Как сделать резервную копию сид-фразы?",
|
||||
"faq_w_seed_2_a": "Старые (устаревшие) кошельки не были основаны на сид-фразе. Кошелёк умеет переносить устаревший кошелёк в современный кошелёк с сид-фразой: он создаёт новый сид-кошелёк и переводит в него ваши средства. Ищите «Перейти на сид-кошелёк…» в Настройки > Резервное копирование и данные. Для этой функции нужна текущая версия демона.",
|
||||
"faq_w_seed_2_q": "У моего кошелька нет сид-фразы — можно ли её добавить?",
|
||||
"faq_w_seed_3_a": "При настройке кошелька используйте восстановление по сид-фразе и введите слова по порядку. Затем кошелёк повторно сканирует блокчейн, чтобы восстановить баланс и историю, что при первом запуске может занять некоторое время.",
|
||||
"faq_w_seed_3_q": "Как восстановить кошелёк по сид-фразе?",
|
||||
"faq_w_seed_4_a": "Ваша сид-фраза — это основная резервная копия, и её достаточно для полного восстановления. Копия файла кошелька — удобная дополнительная резервная копия, которая также сохраняет метки и настройки. Храните любые резервные копии офлайн и в тайне.",
|
||||
"faq_w_seed_4_q": "Нужно ли также сохранять резервную копию wallet.dat?",
|
||||
"faq_w_seed_title": "Сид-фраза и резервное копирование",
|
||||
"faq_w_send_1_a": "Откройте вкладку «Отправить», вставьте адрес получателя, укажите сумму и подтвердите. Если кошелёк зашифрован, вам предложат разблокировать его для транзакции.\n\nОтправка на защищённые адреса приватна; отправка на прозрачные адреса публична.",
|
||||
"faq_w_send_1_q": "Как отправить средства?",
|
||||
"faq_w_send_2_a": "Комиссии DragonX очень низкие и устанавливаются автоматически. Комиссия показывается перед подтверждением. Обычно менять её не нужно.",
|
||||
"faq_w_send_2_q": "Какую комиссию я плачу?",
|
||||
"faq_w_send_3_a": "Да — при отправке на защищённый (Z) адрес можно приложить зашифрованную заметку. Прочитать её сможет только получатель. Прозрачные адреса не поддерживают заметки.",
|
||||
"faq_w_send_3_q": "Можно ли прикрепить сообщение к платежу?",
|
||||
"faq_w_send_4_a": "Транзакция остаётся «в ожидании», пока не будет добыта в блок и не получит подтверждения. Обычно это занимает минуту-две. Защищённым транзакциям также нужно, чтобы кошелёк был синхронизирован, для построения доказательства.\n\nЕсли отправка необычно долго остаётся в ожидании, проверьте, что кошелёк подключён и синхронизирован (см. строку состояния внизу окна).",
|
||||
"faq_w_send_4_q": "Я отправил платёж, но он в статусе «в ожидании» — почему?",
|
||||
"faq_w_send_title": "Отправка и получение",
|
||||
"faq_w_set_1_a": "Откройте Настройки > Внешний вид, чтобы выбрать тему и акцентный цвет. Доступно несколько светлых и тёмных оформлений, а эффекты вроде размытия можно отключить на маломощных компьютерах.",
|
||||
"faq_w_set_1_q": "Как изменить тему?",
|
||||
"faq_w_set_2_a": "Да — в Настройки > Внешний вид есть выбор языка. В кошелёк встроено несколько переводов; всё, что ещё не переведено, отображается на английском.",
|
||||
"faq_w_set_2_q": "Можно ли изменить язык?",
|
||||
"faq_w_set_3_a": "Используйте параметр масштаба шрифта в Настройки > Внешний вид. Он масштабирует весь интерфейс — это удобно на дисплеях высокого разрешения (HiDPI), где приложение иначе может выглядеть слишком мелким.",
|
||||
"faq_w_set_3_q": "Текст слишком мелкий (или слишком крупный) — можно ли его масштабировать?",
|
||||
"faq_w_set_4_a": "Включите получение курса в Настройках. После этого кошелёк будет показывать приблизительную стоимость в фиатной валюте рядом с балансами и сможет отображать небольшой график рынка. Цены приведены только для справки.",
|
||||
"faq_w_set_4_q": "Как показать цены в фиатной валюте?",
|
||||
"faq_w_set_title": "Настройки и внешний вид",
|
||||
"favorite_address": "Добавить в избранное",
|
||||
"fee": "Комиссия",
|
||||
"fee_high": "Высокая",
|
||||
@@ -593,6 +878,63 @@
|
||||
"general": "Общие",
|
||||
"generating": "Генерация",
|
||||
"go_to_receive": "Перейти к получению",
|
||||
"grpa_current_block_paren": "(Текущий: %d)",
|
||||
"grpa_days_ago": "%lld дн назад",
|
||||
"grpa_dbg_addrman": "Отслеживание и управление адресами узлов",
|
||||
"grpa_dbg_alert": "Сообщения системы оповещений",
|
||||
"grpa_dbg_bench": "Замеры производительности операций",
|
||||
"grpa_dbg_coindb": "Операции чтения/записи базы монет",
|
||||
"grpa_dbg_db": "Операции Berkeley DB",
|
||||
"grpa_dbg_estimatefee": "Алгоритм оценки комиссии",
|
||||
"grpa_dbg_http": "Активность HTTP RPC-сервера",
|
||||
"grpa_dbg_libevent": "Сетевая библиотека Libevent",
|
||||
"grpa_dbg_lock": "Отладка конкуренции блокировок",
|
||||
"grpa_dbg_mempool": "Активность пула транзакций в памяти",
|
||||
"grpa_dbg_net": "Сетевые соединения и сообщения",
|
||||
"grpa_dbg_paymentdisclosure": "Протокол раскрытия платежей",
|
||||
"grpa_dbg_pow": "Активность майнинга proof-of-work",
|
||||
"grpa_dbg_proxy": "Соединения через прокси SOCKS5",
|
||||
"grpa_dbg_prune": "Операции обрезки блоков",
|
||||
"grpa_dbg_rand": "Генерация случайных чисел",
|
||||
"grpa_dbg_reindex": "Прогресс переиндексации блокчейна",
|
||||
"grpa_dbg_rpc": "Обработка RPC-команд",
|
||||
"grpa_dbg_selectcoins": "Выбор монет для транзакций",
|
||||
"grpa_dbg_tor": "Интеграция Tor и сведения о цепочках",
|
||||
"grpa_dbg_zmq": "Система уведомлений ZeroMQ",
|
||||
"grpa_dbg_zrpc": "Скрытые (z-addr) RPC-операции",
|
||||
"grpa_enter_private_key_to_import": "Введите приватный ключ для импорта.",
|
||||
"grpa_error_prefix": "Ошибка: ",
|
||||
"grpa_hr_ago": "%lld ч назад",
|
||||
"grpa_invalid_response_from_daemon": "Неверный ответ от демона",
|
||||
"grpa_invalid_suffix": " (неверный)",
|
||||
"grpa_min_ago": "%lld мин назад",
|
||||
"grpa_sec_ago": "%lld сек назад",
|
||||
"grpa_seed_demo_chat": "Демо-чат seed",
|
||||
"grpa_showing_first_100_of": "... показаны первые 100 из %d",
|
||||
"grpa_tab_about": "О программе",
|
||||
"grpa_tab_appearance": "Внешний вид",
|
||||
"grpa_tab_backup_data": "Резервное копирование и данные",
|
||||
"grpa_tab_chat": "Чат",
|
||||
"grpa_tab_explorer": "Обозреватель",
|
||||
"grpa_tab_node_security": "Узел и безопасность",
|
||||
"grpa_tab_wallet": "Кошелёк",
|
||||
"grpa_unexpected_getblockhash_result": "неожиданный результат getblockhash",
|
||||
"grpb_copy": "Копировать",
|
||||
"grpb_max": "Макс",
|
||||
"grpb_new_badge_suffix": " [НОВОЕ]",
|
||||
"grpb_preview_msg_payment_through": "Платёж прошёл? 🙂",
|
||||
"grpb_preview_msg_sending_rest": "Отправляю остаток 👍",
|
||||
"grpb_preview_msg_yep_confirmed": "Да — только что подтвердил ✅",
|
||||
"grpb_selected_suffix": "\n(выбрано)",
|
||||
"grpb_tooltip_address_balance": "%s\nБаланс: %.8f %s%s",
|
||||
"grpb_undo_clear": "Отменить очистку",
|
||||
"grpc_benchmark_inconclusive": "Бенчмарк не дал результата: не записано ни одной выборки хешрейта. Проверьте соединение с пулом и повторите попытку.",
|
||||
"grpc_benchmark_takes_secs": "Бенчмарк занимает ~%dс и прерывает майнинг. Нажмите ещё раз для запуска.",
|
||||
"grpc_bootstrap_failed": "Ошибка начальной загрузки",
|
||||
"grpc_bootstrap_not_initialized": "Начальная загрузка не инициализирована",
|
||||
"grpc_hashrate_fee": "%s комиссия %s%%",
|
||||
"grpc_key_not_available": "Ключ недоступен для этого адреса",
|
||||
"grpc_na": "Н/Д",
|
||||
"height": "Высота",
|
||||
"help": "Справка",
|
||||
"hidden_tag": " (скрыт)",
|
||||
@@ -817,9 +1159,27 @@
|
||||
"memo_optional": "ЗАМЕТКА (НЕОБЯЗАТЕЛЬНО)",
|
||||
"memo_upper": "ЗАМЕТКА",
|
||||
"memo_z_only": "Примечание: Заметки доступны только при отправке на экранированные (z) адреса",
|
||||
"merge_addr_created": "Защищённый адрес создан.",
|
||||
"merge_advanced": "Дополнительно",
|
||||
"merge_back": "Назад",
|
||||
"merge_batch_fmt": "За один запуск объединяется до %d входов — повторите, чтобы завершить остальные.",
|
||||
"merge_confirm_btn": "Подтвердить",
|
||||
"merge_confirm_fmt": "Объединить ~%s DRGX из %d входов в %s?",
|
||||
"merge_create_zaddr": "Создать защищённый адрес",
|
||||
"merge_creating": "Создание адреса…",
|
||||
"merge_description": "Объедините несколько UTXO в один экранированный адрес. Это может уменьшить размер кошелька и улучшить конфиденциальность.",
|
||||
"merge_fee_hint": "Сетевая комиссия за эту транзакцию.",
|
||||
"merge_funds": "Объединить средства",
|
||||
"merge_max_inputs": "Макс. входов за партию",
|
||||
"merge_no_spendable": "Пока нет доступных для расходования входов для объединения.",
|
||||
"merge_progress": "Объединение… это может занять несколько минут. Вы можете закрыть это окно.",
|
||||
"merge_scope_fmt": "%d прозрачных + %d защищённых входов · ~%s DRGX доступно",
|
||||
"merge_scope_loading": "Проверка ваших входов…",
|
||||
"merge_send_failed": "Ошибка объединения: ",
|
||||
"merge_source": "Объединить",
|
||||
"merge_src_both": "Оба типа",
|
||||
"merge_src_shielded": "Защищённые",
|
||||
"merge_src_transparent": "Прозрачные",
|
||||
"merge_started": "Операция объединения начата",
|
||||
"merge_title": "Объединить на адрес",
|
||||
"mig_adopt": "Сделать это моим кошельком",
|
||||
@@ -1059,6 +1419,13 @@
|
||||
"peers_upper": "УЗЛЫ",
|
||||
"peers_version": "Версия",
|
||||
"pending": "Ожидание",
|
||||
"picker_dat_count": "Файлов кошелька в этой папке: %d",
|
||||
"picker_dat_none": "В этой папке нет файлов кошелька",
|
||||
"picker_empty": "В этой папке нет подпапок или файлов кошелька.",
|
||||
"picker_home": "Домашняя папка",
|
||||
"picker_select": "Сканировать эту папку",
|
||||
"picker_title": "Выберите папку для сканирования",
|
||||
"picker_up": "На уровень вверх",
|
||||
"pin_change_desc": "Измените PIN-код разблокировки. Вам понадобится текущий PIN-код и новый PIN-код.",
|
||||
"pin_confirm_new_label": "Подтвердите новый PIN-код:",
|
||||
"pin_current_label": "Текущий PIN-код:",
|
||||
@@ -1195,6 +1562,7 @@
|
||||
"save_z_transactions": "Сохранять Z-tx в списке транзакций",
|
||||
"sb_auth_failed": "Ошибка авторизации — проверьте rpcuser/rpcpassword",
|
||||
"sb_block": "Блок: %d",
|
||||
"sb_block_db_unreadable": "База данных блоков нечитаема — требуется пересборка",
|
||||
"sb_building_witnesses": "Установка свидетелей",
|
||||
"sb_building_witnesses_pct": "Перестроение свидетелей %.0f%%",
|
||||
"sb_connecting_daemon": "Подключение к dragonxd...",
|
||||
@@ -1210,6 +1578,7 @@
|
||||
"sb_dragonxd_stopped": "dragonxd остановлен",
|
||||
"sb_dragonxd_stopping": "Остановка dragonxd...",
|
||||
"sb_extracting_sapling": "Извлечение параметров Sapling...",
|
||||
"sb_finishing_repair": "Завершение восстановления кошелька — повторное сканирование…",
|
||||
"sb_importing_keys": "Импорт ключей",
|
||||
"sb_loading_config": "Загрузка конфигурации...",
|
||||
"sb_mining_hs": "%.1f H/s",
|
||||
@@ -1231,6 +1600,7 @@
|
||||
"sb_waiting_config": "Ожидание конфигурации демона...",
|
||||
"sb_waiting_daemon": "Ожидание dragonxd...",
|
||||
"sb_waiting_daemon_err": "Ожидание dragonxd — %s",
|
||||
"sb_wallet_needs_recovery": "Доступно восстановление кошелька",
|
||||
"sb_warming_up": "Прогрев...",
|
||||
"sb_witness_cache": "Перестроение свидетелей",
|
||||
"scale_effects": "МАСШТАБ И ЭФФЕКТЫ",
|
||||
@@ -1240,6 +1610,45 @@
|
||||
"screenshot_sweep_full": "Полный обход интерфейса",
|
||||
"search_icons": "Поиск значков...",
|
||||
"search_placeholder": "Поиск...",
|
||||
"sec_changing_passphrase": "Смена парольной фразы...",
|
||||
"sec_changing_pin": "Смена PIN...",
|
||||
"sec_couldnt_lock_wallet": "Не удалось заблокировать кошелёк — он всё ещё разблокирован. Проверьте соединение с демоном.",
|
||||
"sec_encrypted_backup_suffix": "\nЗашифрованная резервная копия: wallet.dat.encrypted.bak",
|
||||
"sec_encrypting_wallet": "Шифрование кошелька...",
|
||||
"sec_encryption_did_not_complete": "Шифрование кошелька не завершено — ваш кошелёк НЕ зашифрован. Откройте настройки, чтобы завершить шифрование.",
|
||||
"sec_encryption_failed_prefix": "Ошибка шифрования: ",
|
||||
"sec_failed_prefix": "Ошибка: ",
|
||||
"sec_failed_to_create_vault": "Не удалось создать хранилище",
|
||||
"sec_importing_keys_rescanning": "Импорт ключей и повторное сканирование блокчейна — кошелёк доступен во время выполнения",
|
||||
"sec_incorrect_current_pin": "Неверный текущий PIN",
|
||||
"sec_incorrect_passphrase_decrypt": "Неверная парольная фраза",
|
||||
"sec_incorrect_passphrase_pin_setup": "Неверная парольная фраза",
|
||||
"sec_incorrect_pin_remove": "Неверный PIN",
|
||||
"sec_internal_error_change_pin": "Внутренняя ошибка",
|
||||
"sec_internal_error_remove_pin": "Внутренняя ошибка",
|
||||
"sec_mode_passphrase": " Парольная фраза",
|
||||
"sec_not_connected_to_daemon": "Нет соединения с демоном",
|
||||
"sec_not_connected_to_daemon_pin": "Нет соединения с демоном",
|
||||
"sec_passphrase_changed_successfully": "Парольная фраза успешно изменена",
|
||||
"sec_pin_changed_successfully": "PIN успешно изменён",
|
||||
"sec_pin_removed": "PIN удалён",
|
||||
"sec_pin_set_successfully": "PIN успешно задан",
|
||||
"sec_restart_daemon_for_encryption": "Перезапустите демон, чтобы шифрование вступило в силу.",
|
||||
"sec_too_many_attempts_wait": "Слишком много попыток. Подождите %.0f сек...",
|
||||
"sec_total_elapsed_fmt": "Всего прошло: %dм %02dс",
|
||||
"sec_unlock_button": "Разблокировать",
|
||||
"sec_unlock_failed_prefix": "Ошибка разблокировки: ",
|
||||
"sec_unlocking_fmt": "Разблокировка%s",
|
||||
"sec_use_passphrase_instead": "Использовать парольную фразу",
|
||||
"sec_use_pin_instead": "Использовать PIN",
|
||||
"sec_verifying_passphrase": "Проверка парольной фразы...",
|
||||
"sec_verifying_pin": "Проверка PIN...",
|
||||
"sec_wallet_decrypted_all_keys_imported": "Кошелёк успешно расшифрован! Все ключи импортированы.",
|
||||
"sec_wallet_encrypted_and_pin_set": "Кошелёк зашифрован, PIN задан",
|
||||
"sec_wallet_encrypted_but_pin_vault_failed": "Кошелёк зашифрован, но не удалось создать хранилище PIN",
|
||||
"sec_wallet_encrypted_restarting_daemon": "Кошелёк зашифрован. Перезапуск демона...",
|
||||
"sec_wallet_encrypted_successfully": "Кошелёк успешно зашифрован",
|
||||
"sec_wallet_locked_title": "Кошелёк заблокирован",
|
||||
"security": "БЕЗОПАСНОСТЬ",
|
||||
"seed_backup_button": "Сид-фраза",
|
||||
"seed_backup_close": "Закрыть",
|
||||
@@ -1263,6 +1672,7 @@
|
||||
"send_amount": "Сумма",
|
||||
"send_amount_details": "ДЕТАЛИ СУММЫ",
|
||||
"send_amount_upper": "СУММА",
|
||||
"send_available_note": "доступно",
|
||||
"send_cannot_send_now": "Сейчас отправить нельзя — проверьте соединение, синхронизацию и доступный баланс.",
|
||||
"send_clear_fields": "Очистить все поля формы?",
|
||||
"send_contacts_button": "Выбрать из контактов",
|
||||
@@ -1450,17 +1860,34 @@
|
||||
"status": "Статус",
|
||||
"stop_external": "Остановить внешний daemon",
|
||||
"stop_mining": "Остановить майнинг",
|
||||
"stratum_allowip_hint": "Разрешить майнерам подключаться с IP или CIDR (пусто = только этот компьютер)",
|
||||
"stratum_expose_warn": "Открывает порт для майнинга в разрешённую вами сеть — используйте только в доверенной локальной сети.",
|
||||
"stratum_host": "Разместить майнинг-пул (stratum)",
|
||||
"stratum_host_hint": "Майнеры подключаются к этому компьютеру на порт 22769 (RPC-порт + 1000) с помощью stratum-майнера RandomX. Для применения перезапустите демон.",
|
||||
"stratum_host_section": "РАЗМЕЩЕНИЕ МАЙНИНГ-ПУЛА",
|
||||
"submitting_transaction": "Отправка транзакции...",
|
||||
"success": "Успешно",
|
||||
"summary": "Итоги",
|
||||
"sweep_button": "Перевести",
|
||||
"sweep_caveat": "Импортирует ключ, чтобы подписать одну транзакцию, переводящую все его средства на ваш адрес. Ключ остаётся в кошельке с нулевым балансом.",
|
||||
"sweep_current_theme_only": "Только текущая тема",
|
||||
"sweep_dest_label": "Отправить переведённые средства на",
|
||||
"sweep_dest_new": "Новый экранированный адрес (рекомендуется)",
|
||||
"sweep_done": "Готово — средства переведены на ваш адрес.",
|
||||
"sweep_to": "Переведено на:",
|
||||
"sweep_toggle": "Перевести в мой кошелёк (не сохранять ключ)",
|
||||
"sweep_tx": "Транзакция:",
|
||||
"swin_connection_failed": "Ошибка соединения: ",
|
||||
"swin_connection_successful": "Соединение установлено!\nВерсия dragonxd: ",
|
||||
"swin_invalid_suffix": " (неверный)",
|
||||
"swin_no_history_file_found": "Файл истории не найден",
|
||||
"swin_rescan_failed": "Ошибка повторного сканирования: ",
|
||||
"swin_rescan_started_from_block": "Повторное сканирование начато с блока ",
|
||||
"swin_rescan_to": " до ",
|
||||
"swin_rpc_client_not_initialized": "RPC-клиент не инициализирован",
|
||||
"swin_settings_saved": "Настройки сохранены",
|
||||
"swin_theme_list_refreshed": "Список тем обновлён",
|
||||
"swin_ztx_history_cleared": "История Z-транзакций очищена",
|
||||
"switch_corrupt_body": "Похоже, этот кошелёк повреждён — узел не смог его открыть. Восстановите из резервной копии, создайте заново или попробуйте восстановить.",
|
||||
"switch_corrupt_repair": "Попробовать восстановить (salvage)",
|
||||
"switch_progress_background": "Продолжить в фоне",
|
||||
@@ -1621,6 +2048,8 @@
|
||||
"tt_simple_bg": "Использовать простой градиент для фона\\nГорячая клавиша: Ctrl+Up",
|
||||
"tt_simple_bg_alt": "Использовать градиентную версию фонового изображения темы\\nГорячая клавиша: Ctrl+Up",
|
||||
"tt_stop_external": "Применяется при подключении к демону,\\nзапущенному вне этого кошелька",
|
||||
"tt_stratum_host": "Запускает на этом узле сервер stratum-пула RandomX, чтобы другие майнеры RandomX могли подключаться к этому компьютеру. Требуется узел версии v1.3.0+ и перезапуск демона для применения.",
|
||||
"tt_sweep_current_theme_only": "Применить только к активной теме вместо перебора всех тем",
|
||||
"tt_test_conn": "Проверить RPC-подключение к демону",
|
||||
"tt_theme_effects": "Мерцание, свечение, циклическая смена оттенка по теме",
|
||||
"tt_theme_hotkey": "Горячая клавиша: Ctrl+Влево/Вправо для переключения тем",
|
||||
@@ -1629,6 +2058,7 @@
|
||||
"tt_ui_opacity": "Непрозрачность карточек и боковой панели (100% = полностью непрозрачно, ниже = прозрачнее)",
|
||||
"tt_validate": "Проверить, действителен ли адрес DragonX",
|
||||
"tt_verbose": "Записывать подробную диагностику подключений,\\nсостояние демона и информацию о владельце порта\\nна вкладке Консоль",
|
||||
"tt_wallet_size_warn": "Защищённые кошельки растут за счёт данных witness каждой записи — объединение множества записей в один адрес уменьшает его размер. Сначала сделайте резервную копию.",
|
||||
"tt_wallets_button": "Показать файлы кошельков и переключаться между ними",
|
||||
"tt_website": "Открыть сайт DragonX",
|
||||
"tt_window_opacity": "Непрозрачность фона (ниже = рабочий стол виден сквозь окно)",
|
||||
@@ -1695,8 +2125,68 @@
|
||||
"view_on_explorer": "Посмотреть в обозревателе",
|
||||
"waiting_for_daemon": "Ожидание подключения к daemon...",
|
||||
"wallet": "КОШЕЛЁК",
|
||||
"wallet_degraded_notify": "Ваш кошелёк открыт в режиме с ограниченной функциональностью: существующие средства в безопасности и доступны для расходования, но создание новых адресов и защита средств отключены. Сделайте резервную копию сид-фразы и восстановите из неё кошелёк, чтобы полностью его исправить.",
|
||||
"wallet_diagnostics_hdr": "ДИАГНОСТИКА",
|
||||
"wallet_empty": "Ваш кошелёк пуст",
|
||||
"wallet_empty_hint": "Перейдите к получению, чтобы получить свой адрес и начать получать средства.",
|
||||
"wallet_options_hdr": "ПАРАМЕТРЫ",
|
||||
"wallet_rebuild_failed": "Пересборка не дала исправный кошелёк, поэтому ничего не изменилось. Ваш кошелёк не затронут.",
|
||||
"wallet_rebuild_install_failed": "Не удалось установить пересобранный кошелёк; текущий кошелёк оставлен без изменений.",
|
||||
"wallet_rebuild_no_helper": "Помощник пересборки кошелька недоступен в этой сборке. Используйте Восстановление или пересоберите вручную.",
|
||||
"wallet_rebuild_no_source": "Не удалось найти читаемый кошелёк для пересборки. Ничего не изменилось.",
|
||||
"wallet_rebuild_ok": "База данных кошелька пересобрана — узел загружает её и повторно сканирует для определения вашего баланса.",
|
||||
"wallet_rebuild_started": "Восстановление файла кошелька — пожалуйста, не закрывайте это окно.",
|
||||
"wallet_recovered_body": "Ничего не удалено. Ваш исходный кошелёк по-прежнему сохранён на компьютере как резервная копия с датой, а каждый вариант ниже только копирует или переименовывает файлы — и никогда не стирает их. Ваши ключи никогда не создаются заново, только перечитываются.\n\nВ восстановленной копии, загруженной сейчас, может не хватать нескольких последних транзакций, поэтому баланс может выглядеть немного заниженным, пока вы не завершите процесс ниже и не пройдёт повторное сканирование.",
|
||||
"wallet_recovered_dismiss": "Не сейчас — оставить восстановленную копию",
|
||||
"wallet_recovered_dismiss_sub": "Файлы не изменяются. Вы можете открыть это снова в любой момент из строки состояния; ваш оригинал в любом случае остаётся в безопасной резервной копии.",
|
||||
"wallet_recovered_notify": "Файл вашего кошелька потребовал восстановления — оригинал был безопасно сохранён в резервной копии. Откройте приложение, чтобы рассмотреть варианты.",
|
||||
"wallet_recovered_open_folder": "Показать мне файлы",
|
||||
"wallet_recovered_open_folder_sub": "Открывает папку данных кошелька, чтобы вы могли сами просмотреть файлы резервных копий — ничего не изменяется.",
|
||||
"wallet_recovered_rebuild": "Восстановить автоматически (рекомендуется)",
|
||||
"wallet_recovered_rebuild_sub": "Перечитывает каждую восстанавливаемую запись из вашего самого полного файла кошелька и записывает чистый новый файл, затем перезапускается. Самый тщательный вариант — ваш текущий файл в любом случае сохраняется как резервная копия с датой.",
|
||||
"wallet_recovered_restore": "Восстановить исходный файл вместо этого",
|
||||
"wallet_recovered_restore_sub": "Возвращает на место вашу самую большую нетронутую резервную копию, дословно, затем выполняет повторное сканирование — немного быстрее, но настолько полно, насколько был полон этот файл. Ваш текущий файл в любом случае сохраняется как резервная копия с датой.",
|
||||
"wallet_recovered_safety": "Ваши монеты в безопасности.",
|
||||
"wallet_recovered_title": "Файл вашего кошелька требует быстрого восстановления",
|
||||
"wallet_recovered_warn": "При запуске приложение обнаружило, что файл вашего кошелька не прошёл проверку целостности — обычно это происходит после обновления приложения или некорректного завершения работы. Приложение уже защитило ваши данные: оно отложило старый файл и загрузило восстановленную копию, чтобы вы не остались без доступа.",
|
||||
"wallet_recovery_decide_later": "Решить позже",
|
||||
"wallet_recovery_decide_later_tip": "Закрывает это окно и оставляет загруженной текущую копию. Ничего не меняется, и вы можете выполнить восстановление в любое время — в строке состояния остаётся ссылка «Доступно восстановление кошелька».",
|
||||
"wallet_recovery_details_label": "Показать технические подробности",
|
||||
"wallet_recovery_done": "Готово",
|
||||
"wallet_recovery_failure_body": "Восстановление было выполнено, но его результат не прошёл проверку (файл не удалось прочитать или в нём не оказалось адресов), поэтому он был автоматически отклонён, прежде чем что-либо заменить. Ваш кошелёк остался точно таким же, как до нажатия — на диске ничего не изменилось.",
|
||||
"wallet_recovery_failure_title": "Восстановление не удалось",
|
||||
"wallet_recovery_files_detail": "Ваш исходный кошелёк по-прежнему здесь — приложение переименовало его в резервную копию с датой (wallet.<числа>.bak) в папке данных и ничего не удалило. «Восстановить» считывает каждую запись из вашего самого полного кошелька в совершенно новый чистый файл. «Вернуть» копирует вашу самую большую резервную копию обратно точно в том виде, в каком она есть. Копия, загруженная прямо сейчас, тоже сохраняется как резервная копия.",
|
||||
"wallet_recovery_files_label": "Что происходит с моими файлами?",
|
||||
"wallet_recovery_notnow_short": "Не сейчас",
|
||||
"wallet_recovery_other_options": "Другие варианты",
|
||||
"wallet_recovery_rebuild_card": "Восстановить автоматически",
|
||||
"wallet_recovery_rebuild_card_desc": "Считывает каждую восстанавливаемую запись в чистый файл, затем перезапускается. Самый тщательный вариант.",
|
||||
"wallet_recovery_recommended": "РЕКОМЕНДУЕТСЯ",
|
||||
"wallet_recovery_repair_go": "Восстановить",
|
||||
"wallet_recovery_rescan_body": "Выполняется повторное сканирование блокчейна для восстановления вашего баланса и истории транзакций. Это может занять несколько минут — вам не нужно ничего делать, и, пожалуйста, не перезапускайте узел, пока идёт процесс.",
|
||||
"wallet_recovery_rescan_elapsed": "Выполняется уже %s",
|
||||
"wallet_recovery_rescan_size": "Восстановленный кошелёк сейчас занимает %s и всё ещё заполняется",
|
||||
"wallet_recovery_rescan_slow": "Это занимает больше времени, чем обычно, что нормально для крупного кошелька. Оставить процесс работать безопасно — вы можете наблюдать за прогрессом в разделе Дополнительно ▸ Консоль.",
|
||||
"wallet_recovery_rescan_title": "Завершение восстановления кошелька",
|
||||
"wallet_recovery_restore_card": "Восстановить оригинал",
|
||||
"wallet_recovery_restore_card_desc": "Возвращает на место вашу самую большую нетронутую резервную копию, дословно — настолько полную, насколько был полон этот файл.",
|
||||
"wallet_recovery_restore_go": "Вернуть",
|
||||
"wallet_recovery_success_body": "Приложение загрузило ваш восстановленный кошелёк и выполняет повторное сканирование для подсчёта баланса — это может занять несколько минут. Оно считывает каждую запись, какую может, но в редких случаях повреждённых файлов может восстановить чуть меньше — или иногда чуть больше — адресов, чем было раньше. По завершении повторного сканирования проверьте, что баланс и история выглядят правильно.",
|
||||
"wallet_recovery_success_restore": "Ваша самая большая резервная копия возвращена на место и сейчас загружается, приложение выполняет повторное сканирование. Она восстановлена дословно, поэтому настолько полна, насколько был полон этот файл — проверьте баланс по завершении повторного сканирования.",
|
||||
"wallet_recovery_try_other": "Попробовать другой вариант",
|
||||
"wallet_recovery_whats_happened": "Что произошло?",
|
||||
"wallet_recovery_working_label": "Выполняется",
|
||||
"wallet_restore_bad_backup": "Файл резервной копии кошелька выглядит нечитаемым, поэтому он НЕ был восстановлен — ваш текущий кошелёк не изменён. Восстановите из собственной резервной копии.",
|
||||
"wallet_restore_busy": "Узел занят перезапуском — повторите попытку через момент.",
|
||||
"wallet_restore_copy_failed": "Не удалось установить резервную копию кошелька; ваш текущий кошелёк оставлен без изменений.",
|
||||
"wallet_restore_move_failed": "Не удалось отложить текущий кошелёк — ничего не изменено.",
|
||||
"wallet_restore_no_backup": "Не удалось найти wallet.<timestamp>.bak для восстановления. Ничего не изменено.",
|
||||
"wallet_restore_no_restart": "Ваш исходный кошелёк восстановлен, но узел не перезапустился — запустите его из Настроек.",
|
||||
"wallet_restore_ok": "Исходный кошелёк восстановлен. Узел сейчас его загружает.",
|
||||
"wallet_restore_started": "Восстановление вашего исходного файла — пожалуйста, не закрывайте это окно.",
|
||||
"wallet_restore_stop_failed": "Узел не остановился вовремя, поэтому ничего не изменено. Попробуйте снова.",
|
||||
"wallet_size_consolidate": "Объединить заметки…",
|
||||
"wallet_size_warn": "Файл этого кошелька велик. Объединение ваших заметок может сдержать дальнейший рост.",
|
||||
"wallets_active": "Активен",
|
||||
"wallets_add": "Добавить папку",
|
||||
"wallets_add_folder": "Также сканировать другую папку на наличие файлов кошельков:",
|
||||
@@ -1739,6 +2229,7 @@
|
||||
"wallets_open_inplace_tt": "Открывает этот кошелёк на месте — по ссылке в каталоге данных (без копирования)",
|
||||
"wallets_remove_folder": "Больше не сканировать эту папку",
|
||||
"wallets_reveal": "Открыть папку",
|
||||
"wallets_scan_folder": "Сканировать другую папку на наличие кошельков…",
|
||||
"wallets_scanned_folders": "Просканированные папки:",
|
||||
"wallets_sort_addresses": "Адреса",
|
||||
"wallets_sort_asc": "По возрастанию (сначала старые / меньше / меньший)",
|
||||
@@ -1808,19 +2299,30 @@
|
||||
"wiz_welcome_sub": "Несколько быстрых настроек — и ваш полный узел готов.",
|
||||
"wiz_welcome_title": "Добро пожаловать в ObsidianDragon!",
|
||||
"wizard_daemon_start_failed": "Не удалось запустить демон — будет повторено автоматически",
|
||||
"xmrig_active_tt": "Текущий установленный (активный) майнер",
|
||||
"xmrig_browse_releases": "Просмотреть все релизы…",
|
||||
"xmrig_checking": "Проверка наличия последней версии майнера…",
|
||||
"xmrig_current": "Текущая:",
|
||||
"xmrig_downgrade_note": "Установка другой версии майнера заменяет текущую; сначала остановите майнинг, изменения вступят в силу при следующем запуске.",
|
||||
"xmrig_download_install": "Скачать и установить",
|
||||
"xmrig_downloading": "Загрузка…",
|
||||
"xmrig_install_this": "Установить эту версию",
|
||||
"xmrig_installed": "Установлено:",
|
||||
"xmrig_installed_ok": "Майнер установлен",
|
||||
"xmrig_installing": "Установка…",
|
||||
"xmrig_latest": "Последняя:",
|
||||
"xmrig_latest_badge": "последняя",
|
||||
"xmrig_latest_tt": "Новейшая доступная версия",
|
||||
"xmrig_loading_releases": "Загрузка релизов…",
|
||||
"xmrig_no_notes": "Для этой версии нет заметок о выпуске.",
|
||||
"xmrig_none": "нет",
|
||||
"xmrig_prerelease_tt": "Предварительная / тестовая сборка",
|
||||
"xmrig_reinstall": "Переустановить",
|
||||
"xmrig_released": "Выпущено:",
|
||||
"xmrig_releases": "релизы xmrig",
|
||||
"xmrig_status_current": "Эта версия установлена",
|
||||
"xmrig_status_latest": "Последняя версия",
|
||||
"xmrig_status_older": "Старее вашей установленной версии",
|
||||
"xmrig_stop_mining_first": "Остановите майнинг перед обновлением майнера.",
|
||||
"xmrig_unavailable_body": "Для этой платформы нет доступной сборки майнера.",
|
||||
"xmrig_unavailable_title": "Обновления майнера недоступны",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"about_license_text": "本软件根据 GNU 通用公共许可证 v3 (GPLv3) 发布。您可以根据许可证条款自由使用、修改和分发本软件。",
|
||||
"about_peers_count": "%zu 个节点",
|
||||
"about_release": "发布版",
|
||||
"about_source": "源代码",
|
||||
"about_title": "关于 ObsidianDragon",
|
||||
"about_version": "版本:",
|
||||
"about_website": "网站",
|
||||
@@ -60,7 +61,84 @@
|
||||
"amount_label": "金额:",
|
||||
"animate_avatars": "动画头像",
|
||||
"appearance": "外观",
|
||||
"appx_back": "返回",
|
||||
"appx_back_up_seed_phrase_title": "备份你的助记词",
|
||||
"appx_birthday_block_height": "生日(区块高度):%llu — 也请一并备份。",
|
||||
"appx_blockchain_data_deleted": "区块链数据已删除(%d 项)。守护进程正在重启,以从网络重新同步。",
|
||||
"appx_blockchain_maintenance_in_progress": "已有区块链维护操作正在进行中。",
|
||||
"appx_blockchain_rescan_complete": "区块链重新扫描完成",
|
||||
"appx_bootstrap_complete_reconciling": "引导数据完成——正在将你的钱包与新的链数据进行核对。",
|
||||
"appx_cancel": "取消",
|
||||
"appx_cleaning_up": "正在清理…",
|
||||
"appx_confirm_your_backup": "确认你的备份",
|
||||
"appx_copied_clipboard_autoclears": "已复制——剪贴板将在 45 秒后自动清空",
|
||||
"appx_copy": "复制",
|
||||
"appx_could_not_start_restore": "无法开始恢复",
|
||||
"appx_create_failed_prefix": "创建失败:",
|
||||
"appx_creating_your_wallet": "正在创建你的钱包…",
|
||||
"appx_daemon_error": "守护进程错误",
|
||||
"appx_daemon_reinstall_in_progress": "守护进程重新安装已在进行中。",
|
||||
"appx_disconnecting": "正在断开连接…",
|
||||
"appx_done": "完成",
|
||||
"appx_dragonxd_output": "dragonxd 输出",
|
||||
"appx_encrypting_wallet": "正在加密钱包…",
|
||||
"appx_fullnode_lifecycle_unavailable_lite": "全节点生命周期操作在轻量版中不可用",
|
||||
"appx_installing_bundled_daemon": "正在安装捆绑守护进程——节点将停止、更新并重启…",
|
||||
"appx_invalid_payment_uri_prefix": "无效的支付 URI:",
|
||||
"appx_ive_written_it_down": "我已抄写完毕",
|
||||
"appx_keep_node_running_and_quit": "保持节点运行并退出",
|
||||
"appx_last_block_n": "最新区块:%d",
|
||||
"appx_last_used_wallet_not_found_prefix": "找不到你上次使用的钱包文件(",
|
||||
"appx_last_used_wallet_not_found_suffix": ")——已改为打开默认钱包。如果你移动了它,请恢复该文件,然后从钱包列表中切换回来。",
|
||||
"appx_low_spec_mode_disabled": "已禁用低配模式",
|
||||
"appx_low_spec_mode_enabled": "已启用低配模式",
|
||||
"appx_miner_stopped_prefix": "矿工已停止:",
|
||||
"appx_miner_stopped_unexpectedly": "矿工意外停止。",
|
||||
"appx_n_min_n_sec": "%d 分 %d 秒",
|
||||
"appx_n_seconds": "%d 秒",
|
||||
"appx_no_bundled_daemon_to_install": "此版本没有可安装的捆绑守护进程",
|
||||
"appx_no_embedded_daemon_to_install": "此版本没有可安装的内嵌守护进程",
|
||||
"appx_node_busy_restarting": "节点正忙于重启——请稍后再试。",
|
||||
"appx_node_rebuilding_witness_cache": "节点正在重建其见证缓存",
|
||||
"appx_not_next_word": " — 这不是下一个词",
|
||||
"appx_payment_request_loaded": "支付请求已加载",
|
||||
"appx_pool_miner_connected_and_hashing": "矿池矿工已连接并正在计算哈希。",
|
||||
"appx_progress_n_of_n": "进度:%d / %d",
|
||||
"appx_rebuilding_sapling_note_witnesses": "正在重建 Sapling 票据见证…",
|
||||
"appx_rebuilding_witness_cache_blocks_left": "正在重建见证缓存 %.0f%% —— 剩余 %d 个区块",
|
||||
"appx_rebuilding_witness_cache_pct": "正在重建见证缓存 %.0f%%",
|
||||
"appx_recovery_phrase_word_count": "助记词应为 24 个——你输入了 %d 个。",
|
||||
"appx_restarting_daemon_rescan_flag": "正在以 -rescan 标志重启守护进程…",
|
||||
"appx_restarting_daemon_zapwallettxes": "正在以 -zapwallettxes=2 重启守护进程(钱包修复)…",
|
||||
"appx_restoring_your_wallet": "正在恢复你的钱包…",
|
||||
"appx_seed_backup_warning": "这 24 个词是恢复钱包的唯一方式。请按顺序抄写、离线保存,切勿泄露给他人。一旦丢失,你的资金将永远无法找回。",
|
||||
"appx_seed_not_backed_up_warning": "你尚未备份助记词——资金可能丢失。仍要跳过吗?",
|
||||
"appx_sending_stop_command_to_daemon": "正在向守护进程发送停止命令…",
|
||||
"appx_setting_initial_sapling_witnesses": "正在设置初始 Sapling 见证 %.0f%%",
|
||||
"appx_shutdown_complete": "关闭完成",
|
||||
"appx_simple_background_disabled": "已禁用简约背景",
|
||||
"appx_simple_background_enabled": "已启用简约背景",
|
||||
"appx_skip": "跳过",
|
||||
"appx_skip_anyway": "仍要跳过",
|
||||
"appx_still_status_prefix": "仍处于“",
|
||||
"appx_still_status_suffix": "”——现在强制退出可能损坏链数据。",
|
||||
"appx_stop_anyway_and_quit": "仍要停止并退出",
|
||||
"appx_stopping_daemon_deleting_blockchain": "正在停止守护进程并删除区块链数据…",
|
||||
"appx_stopping_node_discards_rebuild": "现在停止节点会丢弃正在进行的重建,下次打开钱包时将重新开始(需要几分钟)。你也可以让节点继续运行。",
|
||||
"appx_stopping_pool_miner": "正在停止矿池矿工…",
|
||||
"appx_syncing_pct_block_n_of_n": "正在同步 %.1f%% —— 区块 %d / %d",
|
||||
"appx_tap_words_in_order": "按正确顺序点击这些词,以确认你已保存。",
|
||||
"appx_theme_effects_disabled": "已禁用主题特效",
|
||||
"appx_theme_effects_enabled": "已启用主题特效",
|
||||
"appx_theme_prefix": "主题:",
|
||||
"appx_use_settings_restart_daemon_hint": "请使用 设置 > 重启守护进程 重试",
|
||||
"appx_waiting_for_daemon_to_encrypt_wallet": "正在等待守护进程加密钱包…",
|
||||
"appx_wallet_created_and_backed_up": "钱包已创建并完成备份。",
|
||||
"appx_wallet_open_failed_prefix": "钱包打开失败:",
|
||||
"auto_shield": "自动屏蔽挖矿",
|
||||
"autoshield_by_node": "您的节点会自动为您屏蔽挖出的 coinbase。",
|
||||
"autoshield_off_backup_seed": "挖出的 coinbase 尚未自动屏蔽。请备份您的助记词(设置 → 备份与数据),以便节点可以安全启用此功能。",
|
||||
"autoshield_off_generic": "您的节点目前未自动屏蔽挖出的 coinbase。",
|
||||
"av_intro": "挖矿软件经常被标记为潜在有害程序。请按照以下步骤启用矿池挖矿:",
|
||||
"av_open_security": "打开 Windows 安全中心",
|
||||
"av_step1": "第 1 步:添加排除项",
|
||||
@@ -100,6 +178,17 @@
|
||||
"balance_shielded_fmt": "屏蔽:%.8f",
|
||||
"balance_syncing_pct": "同步中 %.1f%%",
|
||||
"balance_transparent_fmt": "透明:%.8f",
|
||||
"baltab_market": "市价",
|
||||
"baltab_market_price_4dp": "市价:$%.4f",
|
||||
"baltab_market_price_8dp": "市价:$%.8f",
|
||||
"baltab_pct_change_24h": "%s%.1f%% 24小时",
|
||||
"baltab_pct_of_total_zaddr": "占总额 %.0f%% · %d 个 Z-addr",
|
||||
"baltab_shielded": "隐蔽",
|
||||
"baltab_shielded_amount": "隐蔽 %.8f",
|
||||
"baltab_t_addresses_count": "%d 个 T-address",
|
||||
"baltab_total_balance": "总余额",
|
||||
"baltab_transparent": "透明",
|
||||
"baltab_transparent_amount": "透明 %.8f",
|
||||
"ban": "封禁",
|
||||
"banned_peers": "已封禁节点",
|
||||
"block": "区块",
|
||||
@@ -107,6 +196,12 @@
|
||||
"block_click_copy": "点击复制",
|
||||
"block_click_next": "点击查看下一个区块",
|
||||
"block_click_prev": "点击查看上一个区块",
|
||||
"block_db_reindex_body": "这通常发生在守护进程更新改变了磁盘格式,或区块索引已损坏之后。您的钱包和币是安全的——节点只是无法加载链,因此余额显示为零。\n\n重建会重新读取您现有的区块文件,可能需要一段时间(同时也会重新扫描您的钱包)。不会下载任何数据。",
|
||||
"block_db_reindex_confirm": "重建区块数据库",
|
||||
"block_db_reindex_notify": "节点无法读取其区块数据库(通常发生在守护进程更新之后)。重建它以恢复您的余额——请查看提示,或前往 设置 › 节点。",
|
||||
"block_db_reindex_started": "正在根据您的区块文件重建区块数据库——这可能需要一段时间。",
|
||||
"block_db_reindex_title": "重建区块数据库?",
|
||||
"block_db_reindex_warn": "节点无法读取其区块数据库。",
|
||||
"block_explorer": "区块浏览器",
|
||||
"block_get_info": "获取区块信息",
|
||||
"block_hash": "区块哈希:",
|
||||
@@ -154,6 +249,10 @@
|
||||
"chat_accent_theme": "主题",
|
||||
"chat_add_contact": "添加联系人",
|
||||
"chat_awaiting_key": "等待回复",
|
||||
"chat_blocked_desc": "被屏蔽的会话会被移除,其消息(无论新旧)都会被丢弃,直至您取消屏蔽。取消屏蔽会从链上重新导入该会话。",
|
||||
"chat_blocked_manage": "已屏蔽",
|
||||
"chat_blocked_title": "已屏蔽的会话",
|
||||
"chat_blocked_toast": "会话已删除并屏蔽",
|
||||
"chat_bubble_minimal": "极简",
|
||||
"chat_bubble_rounded": "圆角",
|
||||
"chat_bubble_square": "方形",
|
||||
@@ -166,6 +265,16 @@
|
||||
"chat_contact_added": "已添加联系人——可在联系人中重命名",
|
||||
"chat_contact_request": "联系人请求",
|
||||
"chat_copy_address_tip": "点击复制地址",
|
||||
"chat_delete": "删除会话",
|
||||
"chat_delete_block": "删除并屏蔽",
|
||||
"chat_delete_body_prefix": "删除与以下用户会话的本地副本:",
|
||||
"chat_delete_body_suffix": "?",
|
||||
"chat_delete_confirm": "删除",
|
||||
"chat_delete_failed": "无法删除会话——未做任何更改。",
|
||||
"chat_delete_local_note": "此操作仅影响此设备——消息仍保留在区块链上,对方也仍保有自己的副本。",
|
||||
"chat_delete_revive_note": "“删除”会清除此设备上的记录。如果对方再次给您发消息,会话会重新出现。",
|
||||
"chat_delete_title": "删除会话?",
|
||||
"chat_deleted_toast": "会话已删除",
|
||||
"chat_density_comfortable": "宽松",
|
||||
"chat_density_compact": "紧凑",
|
||||
"chat_emoji_color": "彩色",
|
||||
@@ -189,6 +298,7 @@
|
||||
"chat_new_button": "新聊天",
|
||||
"chat_new_message": "消息",
|
||||
"chat_new_message_toast": "新的加密聊天消息",
|
||||
"chat_new_needs_zaddr": "聊天需要隐私(z)地址——透明(t)地址无法接收加密消息。",
|
||||
"chat_new_send": "发送请求",
|
||||
"chat_new_title": "新聊天",
|
||||
"chat_new_zaddr": "收款方 z 地址",
|
||||
@@ -234,6 +344,8 @@
|
||||
"chat_ts_24h": "24小时",
|
||||
"chat_ts_global": "跟随全局",
|
||||
"chat_ts_global_short": "全局",
|
||||
"chat_unblock": "取消屏蔽",
|
||||
"chat_unblocked_toast": "会话已取消屏蔽",
|
||||
"chat_unhide": "取消隐藏",
|
||||
"chat_unmute": "取消静音",
|
||||
"chat_verify_key": "身份密钥 — 对比以验证",
|
||||
@@ -286,6 +398,7 @@
|
||||
"console": "控制台",
|
||||
"console_accents": "颜色强调",
|
||||
"console_app": "应用",
|
||||
"console_auto_focus": "打开时聚焦输入框",
|
||||
"console_auto_scroll": "自动滚动",
|
||||
"console_available_commands": "可用命令:",
|
||||
"console_backend_reference": "后端命令参考",
|
||||
@@ -380,11 +493,15 @@
|
||||
"console_tab_completion": "Tab 补全",
|
||||
"console_text_colors": "文本颜色",
|
||||
"console_toggle_accents": "切换行颜色强调",
|
||||
"console_toggle_auto_focus": "打开控制台标签页时将光标置于命令框中",
|
||||
"console_toggle_text_color": "切换行文本颜色",
|
||||
"console_type_help": "输入 'help' 查看可用命令",
|
||||
"console_welcome": "欢迎使用 ObsidianDragon 控制台",
|
||||
"console_zoom_in": "放大",
|
||||
"console_zoom_out": "缩小",
|
||||
"consolidate_desc": "将多个小额输入合并为单个隐私票据。票据越少,钱包文件越小,隐私性也越好。",
|
||||
"consolidate_funds_btn": "合并",
|
||||
"consolidate_title": "合并资金",
|
||||
"contact_avatar": "头像",
|
||||
"contact_avatar_bad_image": "无法加载该图片。",
|
||||
"contact_avatar_badge": "标记",
|
||||
@@ -414,6 +531,7 @@
|
||||
"contacts_shape_square": "方形",
|
||||
"contacts_shape_tab": "左标签",
|
||||
"copied": "已复制!",
|
||||
"copied_to_clipboard": "已复制到剪贴板",
|
||||
"copy": "复制",
|
||||
"copy_address": "复制完整地址",
|
||||
"copy_error": "复制错误",
|
||||
@@ -429,9 +547,13 @@
|
||||
"daemon_maintenance_label": "维护",
|
||||
"daemon_none_bundled": "此版本未内置",
|
||||
"daemon_not_installed": "未安装",
|
||||
"daemon_status_diff": "版本不一致",
|
||||
"daemon_status_differ": "已安装的程序文件与内置版本不同。",
|
||||
"daemon_status_match": "已安装的程序与内置版本一致。",
|
||||
"daemon_status_missing": "未安装守护进程——请安装内置版本。",
|
||||
"daemon_status_none": "未安装",
|
||||
"daemon_status_ok": "已是最新",
|
||||
"daemon_update_active_tt": "当前已安装(正在使用)的节点",
|
||||
"daemon_update_available": "有新的节点版本可用",
|
||||
"daemon_update_body": "此钱包版本内置的 DragonX 节点比当前已安装的更新。更新会替换已安装的 dragonxd(以及 dragonx-cli/dragonx-tx),然后停止并重启节点以使新版本生效。推荐更新——更新的节点可能增加旧版本所缺少的功能(例如助记词支持)。",
|
||||
"daemon_update_browse": "浏览所有版本…",
|
||||
@@ -441,19 +563,28 @@
|
||||
"daemon_update_download_install": "下载并安装",
|
||||
"daemon_update_downloading": "正在下载…",
|
||||
"daemon_update_failed": "更新失败",
|
||||
"daemon_update_install_this": "安装此版本",
|
||||
"daemon_update_installed": "已安装:",
|
||||
"daemon_update_installed_ok": "节点已安装",
|
||||
"daemon_update_installing": "正在安装…",
|
||||
"daemon_update_keep": "保持当前版本",
|
||||
"daemon_update_later": "稍后",
|
||||
"daemon_update_latest": "最新:",
|
||||
"daemon_update_latest_badge": "最新",
|
||||
"daemon_update_latest_tt": "可用的最新版本",
|
||||
"daemon_update_loading": "正在加载版本…",
|
||||
"daemon_update_no_notes": "此版本没有发行说明。",
|
||||
"daemon_update_now": "立即更新",
|
||||
"daemon_update_prerelease_tt": "预发布 / 测试版本",
|
||||
"daemon_update_prompt_title": "是否更新节点守护进程?",
|
||||
"daemon_update_reinstall": "重新安装",
|
||||
"daemon_update_released": "发布时间:",
|
||||
"daemon_update_restart_note": "重启守护进程以开始运行新版本。",
|
||||
"daemon_update_restart_now": "立即重启守护进程",
|
||||
"daemon_update_safe": "您的钱包、密钥和区块链数据不会被改动——仅替换守护进程的程序文件。如果您是有意运行自定义节点,请选择“保持当前版本”。",
|
||||
"daemon_update_status_current": "此版本已安装",
|
||||
"daemon_update_status_latest": "最新版本",
|
||||
"daemon_update_status_older": "早于您已安装的版本",
|
||||
"daemon_update_title": "更新节点守护进程?",
|
||||
"daemon_update_unavailable_body": "此平台没有可用的节点版本。",
|
||||
"daemon_update_unavailable_title": "节点更新不可用",
|
||||
@@ -469,6 +600,13 @@
|
||||
"data_stale_tooltip": "余额可能已过时 — 钱包最近未收到更新。请检查您的节点连接。",
|
||||
"date": "日期",
|
||||
"date_label": "日期:",
|
||||
"debug_gate_confirm": "显示调试选项",
|
||||
"debug_gate_incorrect": "PIN 或密码不正确。",
|
||||
"debug_gate_pass_prompt": "请输入您的钱包密码以继续:",
|
||||
"debug_gate_pin_prompt": "请输入您的解锁 PIN 以继续:",
|
||||
"debug_gate_title": "显示调试选项?",
|
||||
"debug_gate_verifying": "验证中…",
|
||||
"debug_gate_warning": "这些是高级诊断和节点调优选项,可能暴露敏感信息并改变节点的运行方式。仅在您了解自己在做什么的情况下继续。",
|
||||
"debug_logging": "调试日志",
|
||||
"decrypt_desc": "钱包将被导出,守护进程将使用全新的未加密钱包重新启动,并重新导入所有密钥。根据钱包大小,这可能需要几分钟。",
|
||||
"decrypt_error_title": "解密失败",
|
||||
@@ -491,6 +629,7 @@
|
||||
"disconnected": "已断开",
|
||||
"dismiss": "关闭",
|
||||
"display": "显示",
|
||||
"done": "完成",
|
||||
"download": "下载",
|
||||
"download_bootstrap": "下载引导程序",
|
||||
"dragonx_green": "DragonX(绿色)",
|
||||
@@ -544,6 +683,7 @@
|
||||
"explorer_tx_outputs": "输出",
|
||||
"explorer_tx_size": "大小",
|
||||
"explorer_txid": "TxID:",
|
||||
"explorer_urls_hdr": "网址",
|
||||
"export": "导出",
|
||||
"export_csv": "导出 CSV",
|
||||
"export_keys_btn": "导出密钥",
|
||||
@@ -571,6 +711,151 @@
|
||||
"export_viewing_key": "导出查看密钥",
|
||||
"failed_create_shielded": "无法创建屏蔽地址",
|
||||
"failed_create_transparent": "无法创建透明地址",
|
||||
"faq": "常见问题",
|
||||
"faq_d_mgmt_1_a": "内置节点会随钱包自动启动,并在适当时机停止,因此通常无需手动管理。高级控制选项(包括重启守护进程)位于 设置 > 节点与安全。",
|
||||
"faq_d_mgmt_1_q": "如何启动或停止节点?",
|
||||
"faq_d_mgmt_2_a": "如果您将钱包连接到自己启动的节点,此选项决定关闭钱包时是否也关闭该节点。如果希望关闭钱包后节点继续运行,请保持关闭此选项。",
|
||||
"faq_d_mgmt_2_q": "“退出时关闭外部守护进程”是做什么的?",
|
||||
"faq_d_mgmt_3_a": "节点和钱包数据存储在用户配置文件下的 DragonX 数据目录中(例如 Windows 上的 AppData,或 Linux/macOS 上的主文件夹)。区块链占用的空间最大,请为其增长预留足够的磁盘空间。",
|
||||
"faq_d_mgmt_3_q": "区块链和钱包数据存储在哪里?",
|
||||
"faq_d_mgmt_title": "节点管理",
|
||||
"faq_d_mine_1_a": "挖矿标签页可让您使用 CPU 挖矿。DragonX 使用 RandomX 算法,专为普通处理器设计。设置线程数并开始——奖励将累积到您的钱包中。",
|
||||
"faq_d_mine_1_q": "如何挖 DragonX?",
|
||||
"faq_d_mine_2_a": "独立挖矿的奖励直接归您所有,但只有在您挖到区块时才会获得,除非您有很大算力,否则并不频繁。矿池挖矿与他人共享算力,可获得较小但更稳定的收益。挖矿标签页同时支持这两种方式。",
|
||||
"faq_d_mine_2_q": "独立挖矿还是矿池挖矿——我该选哪个?",
|
||||
"faq_d_mine_3_a": "挖矿标签页的矿池部分有一个“更新矿工…”按钮,可下载、验证并安装最新的优化版 DragonX 矿工程序。与节点更新程序一样,每次下载在安装前都会进行校验和与签名验证。",
|
||||
"faq_d_mine_3_q": "如何更新矿工程序?",
|
||||
"faq_d_mine_4_a": "当前节点可以运行内置的 stratum 服务器,让其他矿工连接到您的机器。在“设置 > 节点与安全”下有相应的开关。默认情况下它只监听本地连接;要向其他计算机开放,需要您主动配置。",
|
||||
"faq_d_mine_4_q": "我可以搭建矿池吗?",
|
||||
"faq_d_mine_title": "挖矿与矿池",
|
||||
"faq_d_net_1_a": "节点启动后需要一点时间来寻找对等节点,因此短暂出现“0 个对等节点”是正常现象。如果持续存在,请检查您的网络连接以及防火墙是否阻挡了节点。节点会通过内置种子自动寻找对等节点。",
|
||||
"faq_d_net_1_q": "钱包显示没有对等节点或连接——出了什么问题?",
|
||||
"faq_d_net_2_a": "是的——DragonX 节点之间通过 TLS 通信,因此对等节点连接是加密的。钱包与自己的节点之间也通过安全的本地通道通信。",
|
||||
"faq_d_net_2_q": "我与网络的连接是加密的吗?",
|
||||
"faq_d_net_title": "对等节点与网络",
|
||||
"faq_d_node_1_a": "守护进程 dragonxd 是 DragonX 的全节点。它下载并验证整条区块链,向网络中继交易,并响应钱包的查询。运行自己的节点意味着您无需信任任何其他服务器。",
|
||||
"faq_d_node_1_q": "什么是守护进程(dragonxd)?",
|
||||
"faq_d_node_2_a": "默认情况下,钱包会启动并管理自己内置的节点(内嵌节点)——您无需做任何操作。如果您已经自行运行了 dragonxd,钱包可以改为连接到该外部节点。钱包会检测当前所处的情况并相应地运作。",
|
||||
"faq_d_node_2_q": "内嵌节点与外部节点有什么区别?",
|
||||
"faq_d_node_3_a": "全节点独立验证区块链的每一条规则,因此您的钱包信任的是数学运算,而非第三方。这也增强了网络的健壮性。代价是磁盘空间和一次初始同步。",
|
||||
"faq_d_node_3_q": "为什么要运行全节点?",
|
||||
"faq_d_node_title": "全节点",
|
||||
"faq_d_perf_1_a": "区块链是占用空间最大的部分,且会随时间增长,因此请预留几个 GB 的空闲空间。节点使用数据库缓存来提升速度;内置默认值已针对常见机器进行了调优。高级用户可以通过编辑配置文件(DRAGONX.conf)中的 dbcache 来调整节点的数据库缓存。",
|
||||
"faq_d_perf_1_q": "节点占用多少磁盘空间?可以调整内存吗?",
|
||||
"faq_d_perf_2_a": "启用引导包下载(在设置向导中提供),可获取一份最新的、已验证的链数据副本,而无需从头验证每一个区块。使用高速磁盘(SSD)也有很大帮助。",
|
||||
"faq_d_perf_2_q": "如何加快初始同步速度?",
|
||||
"faq_d_perf_title": "存储与性能",
|
||||
"faq_d_sync_1_a": "启动时,节点会先加载并验证区块,然后才能为钱包提供服务。“正在处理区块 / 正在应用区块以构建当前链状态”界面就是这个预热过程。这是正常现象;节点完成并报告其高度后,钱包即可使用。",
|
||||
"faq_d_sync_1_q": "启动时的“正在处理区块”是什么意思?",
|
||||
"faq_d_sync_2_a": "对于您的隐私资金,节点会保留加密“见证数据”,让您能够进行隐私花费。在某些重启或更新之后,节点会通过重新扫描最近的区块来重建这个缓存。在交易较多的钱包上,这可能需要几分钟并占用额外内存。\n\n这并非卡死——请让它完成。中途强制退出只会导致下次重新开始重建。",
|
||||
"faq_d_sync_2_q": "“正在重建见证缓存”是什么意思?",
|
||||
"faq_d_sync_3_a": "接近链顶时,扫描钱包的工作(检查余额和票据)会与节点争用同一个内部锁,这可能会拖慢大钱包连接最后几个区块的速度。较新版本会限制此类扫描,以便节点能够赶上进度。停留在较轻量的标签页(或耐心等待)可以让它更快完成。",
|
||||
"faq_d_sync_3_q": "为什么快追上进度时同步反而变慢了?",
|
||||
"faq_d_sync_4_a": "首次同步会下载并验证整条链,根据您的网络和磁盘情况可能需要一段时间。在设置向导中启用引导包下载可以大幅加快速度,它会获取一份最新的、已验证的链数据副本。",
|
||||
"faq_d_sync_4_q": "初始同步需要多长时间?",
|
||||
"faq_d_sync_title": "同步与区块链",
|
||||
"faq_d_trbl_1_a": "通常不是。在大钱包上,节点重建隐私见证数据或应用区块可能需要几分钟,此期间它看起来像是暂停了,响应也可能较慢。请给它一些时间——调试日志(设置中)会显示持续的进度,如果它正在正常工作。\n\n在此阶段请避免强制退出,否则下次启动会从头开始这项工作。",
|
||||
"faq_d_trbl_1_q": "节点似乎卡在“正在激活最长链”——是卡死了吗?",
|
||||
"faq_d_trbl_2_a": "如果钱包文件被恢复,但部分密钥数据丢失,节点可能会以受限的“降级”模式打开它,此时无法生成新地址或进行隐私保护。最安全的解决方法是使用您的助记词恢复短语,将钱包恢复到一个全新的钱包中。",
|
||||
"faq_d_trbl_2_q": "什么是“降级模式”?",
|
||||
"faq_d_trbl_3_a": "重新扫描会让钱包重新读取链上数据,以重新发现您的交易记录(在导入密钥后很有用)。重新索引会重建节点的区块链数据库,仅在该数据库损坏或因版本变更而不兼容时才需要。这两个过程都可能需要一段时间;可从“设置 > 节点与安全”中启动它们。",
|
||||
"faq_d_trbl_3_q": "什么时候应该重新索引或重新扫描?",
|
||||
"faq_d_trbl_4_a": "在重建见证数据或初始同步等繁重工作期间,内存使用会激增,完成后会回落。空闲时持续高内存占用并不正常——重启钱包可以清除它。在低内存机器上,请确保没有其他占用大量内存的应用与之争抢资源。",
|
||||
"faq_d_trbl_4_q": "节点占用了大量内存——这正常吗?",
|
||||
"faq_d_trbl_title": "故障排除",
|
||||
"faq_d_upd_1_a": "打开“设置 > 节点与安全 > 守护进程二进制文件”,使用“检查更新…”。钱包会下载最新的已验证节点版本并安装;新版本会在守护进程下次启动时生效。",
|
||||
"faq_d_upd_1_q": "如何更新节点?",
|
||||
"faq_d_upd_2_a": "是的——更新对话框会列出每一个发行版本,您可以选择特定或较旧的版本。降级时请谨慎:较旧的节点可能无法接受较新节点写入的区块链数据,可能需要重新索引。",
|
||||
"faq_d_upd_2_q": "我可以安装特定版本的节点吗?",
|
||||
"faq_d_upd_3_a": "每个下载的节点压缩包在安装前都会通过校验和与加密签名进行验证。如果验证失败,钱包会拒绝该更新。",
|
||||
"faq_d_upd_3_q": "这次更新安全吗?",
|
||||
"faq_d_upd_title": "更新节点",
|
||||
"faq_group_daemon": "守护进程",
|
||||
"faq_group_wallet": "钱包",
|
||||
"faq_intro": "关于您的钱包和 DragonX 节点的解答。请搜索,或在下方按主题浏览。",
|
||||
"faq_intro_lite": "关于您的轻量钱包的解答。请搜索,或在下方按主题浏览。",
|
||||
"faq_l_gs_1_a": "ObsidianDragonLite 是轻量版 DragonX 钱包。它不运行全节点,而是连接到 DragonX 轻量服务器,因此启动速度快,也无需下载整条区块链。\n\n它像完整版钱包一样管理您的密钥、余额和转账——请参阅下方的“轻量钱包”部分,了解服务器连接的工作原理及隐私权衡。",
|
||||
"faq_l_gs_1_q": "什么是 ObsidianDragonLite?",
|
||||
"faq_l_lite_1_a": "轻量钱包连接到 DragonX 轻量服务器,而不是运行自己的全节点。这使它启动迅速,占用的磁盘和带宽也更少,因为它自身不下载或验证整条区块链。您的私钥始终保存在您的设备上——绝不会发送到服务器。",
|
||||
"faq_l_lite_1_q": "轻量钱包与完整版钱包有什么区别?",
|
||||
"faq_l_lite_2_a": "打开设置并选择一个服务器。您可以随时切换服务器,钱包会从新服务器重新同步。如果某个服务器响应慢或无法访问,请尝试其他服务器。",
|
||||
"faq_l_lite_2_q": "如何选择或更改服务器?",
|
||||
"faq_l_lite_3_a": "您的密钥永远不会离开您的设备,隐私金额也始终保持加密。但轻量服务器确实能看到您的钱包查询了哪些地址以及您的 IP 地址,因此它可以将您的地址关联起来——这是以隐私换取便利的权衡。若追求最高隐私性,请使用全节点钱包,它会自行验证链上数据,不会向服务器暴露您的地址。",
|
||||
"faq_l_lite_3_q": "轻量钱包的隐私性和全节点一样吗?",
|
||||
"faq_l_lite_title": "轻量钱包",
|
||||
"faq_l_sec_1_a": "打开 设置 > 钱包,在安全部分设置密码。加密可保护磁盘上的私钥,这样即使有人拿到了文件,也无法花费您的币。\n\n请选择一个强密码,并妥善保管——一旦丢失,加密钱包将无法恢复。",
|
||||
"faq_no_results": "没有结果。请尝试其他搜索词。",
|
||||
"faq_open_tooltip": "帮助与常见问题",
|
||||
"faq_search_hint": "搜索常见问题…",
|
||||
"faq_title": "帮助与常见问题",
|
||||
"faq_w_addr_1_a": "透明地址(以 R 或 t 开头)的工作方式与大多数币种相同:金额和地址在区块链上是公开的。\n\n隐私地址(z 地址,以 zs 开头)使用零知识加密技术——金额、发送方和接收方都在链上加密。存放在隐私地址中的余额是私密的。",
|
||||
"faq_w_addr_1_q": "透明地址和隐私地址有什么区别?",
|
||||
"faq_w_addr_2_a": "请尽量使用隐私(z)地址——它们能保护您的余额和支付记录不被公开。DragonX 是一种隐私币,收款默认使用隐私地址。\n\n只有在您使用的服务无法处理隐私地址时,才使用透明地址。",
|
||||
"faq_w_addr_2_q": "应该使用哪种地址类型?",
|
||||
"faq_w_addr_3_a": "对于隐私地址,重复使用没有问题,也不会泄露历史记录。对于透明地址,每次付款使用新地址可以提升隐私性。可以在“接收”标签页创建新地址。",
|
||||
"faq_w_addr_3_q": "应该重复使用地址,还是新建一个?",
|
||||
"faq_w_addr_4_a": "屏蔽会将币从透明地址转移到隐私地址,使其变为私密。新挖出的币到账时是透明的,会自动为您屏蔽。\n\n您也可以手动屏蔽;屏蔽只是一种特殊交易,因此需要正常的确认时间才能完成。",
|
||||
"faq_w_addr_4_q": "什么是“屏蔽”?",
|
||||
"faq_w_addr_title": "地址与隐私",
|
||||
"faq_w_bal_1_a": "在钱包同步完成之前,您的余额是不完整的,可能显示为 0——因为它还没有扫描完您的所有交易。同步完成后余额会自动补齐。\n\n请留意窗口底部的状态栏:它显示连接状态和区块高度。同步完成后,余额即为准确值。",
|
||||
"faq_w_bal_1_q": "为什么我的余额是 0 或不更新?",
|
||||
"faq_w_bal_2_a": "在包含您交易的区块之上每挖出一个新区块,就会增加一个确认。确认数越多,代表付款结算得越牢固。收到的资金在获得第一个确认后即可使用。",
|
||||
"faq_w_bal_2_q": "什么是确认数?",
|
||||
"faq_w_bal_3_a": "总余额包括仍在确认中或暂时被锁定的资金(例如正在屏蔽过程中的币)。可用余额是您现在可以发送的部分。随着交易确认,两者会趋于一致。",
|
||||
"faq_w_bal_3_q": "总余额和可用余额有什么区别?",
|
||||
"faq_w_bal_title": "余额与同步",
|
||||
"faq_w_chat_1_a": "聊天是内置于钱包中的加密链上通讯功能。消息以私密的隐私备注形式发送,只有您和对方能够读取。",
|
||||
"faq_w_chat_1_q": "什么是聊天功能?",
|
||||
"faq_w_chat_2_a": "您的聊天身份是从钱包的助记词派生而来的,因此它会随钱包一起保留——恢复钱包,身份也会随之恢复。您无需另外创建账户或密码。",
|
||||
"faq_w_chat_2_q": "我的聊天身份是如何工作的?",
|
||||
"faq_w_chat_3_a": "在“联系人”标签页通过地址添加联系人,可选填写名称和头像。联系人可以仅在当前钱包中保留私有,也可以在您在此电脑上打开的所有钱包间共享。",
|
||||
"faq_w_chat_3_q": "联系人是如何工作的?",
|
||||
"faq_w_chat_4_a": "打开一个会话,使用其顶部的图标进行操作。隐藏(眼睛图标)会将其从列表中移除但保留全部消息——收到新消息会自动取消隐藏。删除(垃圾桶图标)会打开一个对话框:“删除”会清除本设备上的记录(如果对方再次给您发消息,会话会重新出现),而“删除并屏蔽”还会阻止对方之后的消息,直到您在会话列表上方的“已屏蔽”列表中取消屏蔽为止。这里的所有操作仅在本设备本地生效——消息仍保留在区块链上,对方也保留着自己的副本,因此这并非“撤回”。",
|
||||
"faq_w_chat_4_q": "如何隐藏、删除或屏蔽一个会话?",
|
||||
"faq_w_chat_title": "聊天与联系人",
|
||||
"faq_w_gs_1_a": "ObsidianDragon 是 DragonX(DRGX)的全节点钱包。它管理您的币,并且在桌面版本中会在后台运行 DragonX 全节点(守护进程),使您的钱包能够自行验证区块链,而无需信任第三方服务器。\n\n可以把它想象成两部分协同工作:钱包(密钥、余额、发送)和节点(区块链与网络)。这里的问题也按同样的方式划分——请参见上方的“钱包”和“守护进程”标签页。",
|
||||
"faq_w_gs_1_q": "什么是 ObsidianDragon?",
|
||||
"faq_w_gs_2_a": "首次启动时,设置向导会自动为您创建一个钱包。新钱包由一个助记词(一组单词)作为备份。请把这些词写下来并妥善离线保管——如果这台电脑丢失,这是恢复您资金的唯一方式。\n\n您可以随时在 设置 > 备份与数据 > 助记词 中查看或备份该助记词。",
|
||||
"faq_w_gs_2_q": "如何创建新钱包?",
|
||||
"faq_w_gs_3_a": "如果您已有助记词,请使用助记词恢复流程。(在全节点版本中,您也可以在启动前将现有的 wallet.dat 文件复制到钱包的数据目录中。)\n\n恢复后,钱包会重新扫描以找回您以往的交易,因此首次显示余额和历史记录可能需要一些时间。",
|
||||
"faq_w_gs_3_q": "如何恢复我已有的钱包?",
|
||||
"faq_w_gs_4_a": "向导可让您选择外观/主题、设置加密和 PIN 码,在全节点版本中还可以选择下载引导数据以加快首次同步。您可以跳过任意步骤,之后随时在设置中更改这些内容。",
|
||||
"faq_w_gs_4_q": "首次运行的设置向导做了什么?",
|
||||
"faq_w_gs_title": "新手入门",
|
||||
"faq_w_sec_1_a": "打开 设置 > 节点与安全,在安全部分设置密码。加密可保护磁盘上的私钥,这样即使有人拿到了文件,也无法花费您的币。\n\n请选择一个强密码,并妥善保管——一旦丢失,加密钱包将无法恢复。",
|
||||
"faq_w_sec_1_q": "如何加密我的钱包?",
|
||||
"faq_w_sec_2_a": "PIN 码用于锁定应用界面,让您离开时余额和操作被隐藏。这是加密之上的一层便捷锁定,可以在向导中或在完成钱包加密后于设置中设置。",
|
||||
"faq_w_sec_2_q": "什么是 PIN 码/锁屏?",
|
||||
"faq_w_sec_3_a": "不能。钱包加密无法被绕过。如果您仍保存着助记词,可以用它将钱包恢复到一个新钱包并设置新密码。如果密码和助记词都没有,资金将无法找回。",
|
||||
"faq_w_sec_3_q": "我忘记密码了——能找回吗?",
|
||||
"faq_w_sec_title": "安全与加密",
|
||||
"faq_w_seed_1_a": "前往 设置 > 备份与数据 > 助记词。按顺序把这些单词抄写在纸上,并存放在安全的离线地点。任何持有该助记词的人都能花费您的币,因此切勿将其保存在照片、邮件或云笔记中。",
|
||||
"faq_w_seed_1_q": "如何备份我的助记词?",
|
||||
"faq_w_seed_2_a": "较旧(传统)的钱包不是基于助记词的。钱包可以将传统钱包迁移为现代的助记词钱包:创建一个新的助记词钱包,并将您的资金转移过去。请在 设置 > 备份与数据 中查找“迁移到助记词钱包…”。此功能需要当前版本的守护进程。",
|
||||
"faq_w_seed_2_q": "我的钱包没有助记词——可以添加吗?",
|
||||
"faq_w_seed_3_a": "设置钱包时使用助记词恢复流程,并按顺序输入您的单词。之后钱包会重新扫描以重建余额和交易历史,首次可能需要一些时间。",
|
||||
"faq_w_seed_3_q": "如何用助记词恢复钱包?",
|
||||
"faq_w_seed_4_a": "助记词是主要备份,足以恢复一切。钱包文件的副本是一个便捷的次要备份,还能保留标签和设置。请将所有备份妥善离线私密保管。",
|
||||
"faq_w_seed_4_q": "我还需要备份 wallet.dat 吗?",
|
||||
"faq_w_seed_title": "助记词与备份",
|
||||
"faq_w_send_1_a": "打开“发送”标签页,粘贴收款方地址,输入金额并确认。如果钱包已加密,系统会要求您解锁以完成交易。\n\n发送到隐私地址是私密的;发送到透明地址是公开的。",
|
||||
"faq_w_send_1_q": "如何发送资金?",
|
||||
"faq_w_send_2_a": "DragonX 的手续费非常低,并且是自动设置的。手续费会在您确认前显示。通常无需手动更改。",
|
||||
"faq_w_send_2_q": "我需要支付多少手续费?",
|
||||
"faq_w_send_3_a": "可以——向隐私(z)地址发送时,您可以附加一条加密备注。只有收款方能够读取它。透明地址不支持备注。",
|
||||
"faq_w_send_3_q": "我可以在付款时附加消息吗?",
|
||||
"faq_w_send_4_a": "交易在被打包进区块并获得确认之前处于「待处理」状态。这通常需要一两分钟。屏蔽交易还需要您的钱包已完成同步才能生成证明。\n\n如果一笔发款长时间处于待处理状态,请检查您的钱包是否已连接并同步(见窗口底部的状态栏)。",
|
||||
"faq_w_send_4_q": "我发送了一笔付款,但显示待处理——为什么?",
|
||||
"faq_w_send_title": "发送与接收",
|
||||
"faq_w_set_1_a": "打开 设置 → 外观 以选择主题和强调色。内置多种明暗皮肤,模糊等特效也可在低性能设备上关闭。",
|
||||
"faq_w_set_1_q": "如何更改主题?",
|
||||
"faq_w_set_2_a": "可以——设置 → 外观 中有语言选择器。钱包内置多种翻译;尚未翻译的部分会回退为英文。",
|
||||
"faq_w_set_2_q": "可以更改语言吗?",
|
||||
"faq_w_set_3_a": "使用设置 → 外观 中的字体缩放选项。它会缩放整个界面,在高分辨率(HiDPI)显示器上非常有用,否则应用可能显示得过小。",
|
||||
"faq_w_set_3_q": "文字太小(或太大)——可以缩放吗?",
|
||||
"faq_w_set_4_a": "在设置中启用价格获取。钱包会在余额旁显示大致的法币价值,并可显示简易行情图。价格仅供参考。",
|
||||
"faq_w_set_4_q": "如何显示法币价格?",
|
||||
"faq_w_set_title": "设置与外观",
|
||||
"favorite_address": "收藏地址",
|
||||
"fee": "手续费",
|
||||
"fee_high": "高",
|
||||
@@ -593,6 +878,63 @@
|
||||
"general": "常规",
|
||||
"generating": "正在生成",
|
||||
"go_to_receive": "前往接收",
|
||||
"grpa_current_block_paren": "(当前:%d)",
|
||||
"grpa_days_ago": "%lld 天前",
|
||||
"grpa_dbg_addrman": "对等节点地址追踪与管理",
|
||||
"grpa_dbg_alert": "警报系统消息",
|
||||
"grpa_dbg_bench": "操作的基准计时",
|
||||
"grpa_dbg_coindb": "币数据库读写操作",
|
||||
"grpa_dbg_db": "Berkeley DB 操作",
|
||||
"grpa_dbg_estimatefee": "手续费估算算法",
|
||||
"grpa_dbg_http": "HTTP RPC 服务器活动",
|
||||
"grpa_dbg_libevent": "Libevent 网络库",
|
||||
"grpa_dbg_lock": "锁竞争调试",
|
||||
"grpa_dbg_mempool": "交易内存池活动",
|
||||
"grpa_dbg_net": "网络连接与消息",
|
||||
"grpa_dbg_paymentdisclosure": "支付披露协议",
|
||||
"grpa_dbg_pow": "工作量证明挖矿活动",
|
||||
"grpa_dbg_proxy": "SOCKS5 代理连接",
|
||||
"grpa_dbg_prune": "区块修剪操作",
|
||||
"grpa_dbg_rand": "随机数生成",
|
||||
"grpa_dbg_reindex": "区块链重新索引进度",
|
||||
"grpa_dbg_rpc": "RPC 命令处理",
|
||||
"grpa_dbg_selectcoins": "交易的币选择",
|
||||
"grpa_dbg_tor": "Tor 集成与线路信息",
|
||||
"grpa_dbg_zmq": "ZeroMQ 通知系统",
|
||||
"grpa_dbg_zrpc": "隐蔽(z-addr)RPC 操作",
|
||||
"grpa_enter_private_key_to_import": "请输入要导入的私钥。",
|
||||
"grpa_error_prefix": "错误:",
|
||||
"grpa_hr_ago": "%lld 小时前",
|
||||
"grpa_invalid_response_from_daemon": "守护进程返回了无效响应",
|
||||
"grpa_invalid_suffix": "(无效)",
|
||||
"grpa_min_ago": "%lld 分钟前",
|
||||
"grpa_sec_ago": "%lld 秒前",
|
||||
"grpa_seed_demo_chat": "种子演示聊天",
|
||||
"grpa_showing_first_100_of": "…显示 %d 项中的前 100 项",
|
||||
"grpa_tab_about": "关于",
|
||||
"grpa_tab_appearance": "外观",
|
||||
"grpa_tab_backup_data": "备份与数据",
|
||||
"grpa_tab_chat": "聊天",
|
||||
"grpa_tab_explorer": "浏览器",
|
||||
"grpa_tab_node_security": "节点与安全",
|
||||
"grpa_tab_wallet": "钱包",
|
||||
"grpa_unexpected_getblockhash_result": "意外的 getblockhash 结果",
|
||||
"grpb_copy": "复制",
|
||||
"grpb_max": "最大",
|
||||
"grpb_new_badge_suffix": " [新]",
|
||||
"grpb_preview_msg_payment_through": "付款到账了吗?🙂",
|
||||
"grpb_preview_msg_sending_rest": "现在把剩下的发过去 👍",
|
||||
"grpb_preview_msg_yep_confirmed": "到了——刚刚确认 ✅",
|
||||
"grpb_selected_suffix": "\n(已选)",
|
||||
"grpb_tooltip_address_balance": "%s\n余额:%.8f %s%s",
|
||||
"grpb_undo_clear": "撤销清除",
|
||||
"grpc_benchmark_inconclusive": "基准测试无结果:未记录到任何算力样本。请检查矿池连接后重试。",
|
||||
"grpc_benchmark_takes_secs": "基准测试约需 %d 秒并会中断挖矿。再次点击以开始。",
|
||||
"grpc_bootstrap_failed": "引导失败",
|
||||
"grpc_bootstrap_not_initialized": "引导未初始化",
|
||||
"grpc_hashrate_fee": "%s %s%% 手续费",
|
||||
"grpc_key_not_available": "此地址无可用密钥",
|
||||
"grpc_na": "不适用",
|
||||
"height": "高度",
|
||||
"help": "帮助",
|
||||
"hidden_tag": " (已隐藏)",
|
||||
@@ -766,6 +1108,7 @@
|
||||
"loading": "加载中...",
|
||||
"loading_addresses": "正在加载地址...",
|
||||
"loading_stall_body": "守护进程已初始化 %.0f 秒。更新后或首次启动时(加载区块索引或重新扫描)这可能是正常现象——就绪后会自动连接。",
|
||||
"loading_stall_hint": "卡住了?前往 设置 → 重启节点,或查看控制台。",
|
||||
"loading_stall_title": "耗时超出预期",
|
||||
"loading_transactions": "正在加载交易",
|
||||
"local_hashrate": "本地算力",
|
||||
@@ -816,9 +1159,27 @@
|
||||
"memo_optional": "备注(可选)",
|
||||
"memo_upper": "备注",
|
||||
"memo_z_only": "注意:备注仅在发送到屏蔽 (z) 地址时可用",
|
||||
"merge_addr_created": "屏蔽地址已创建。",
|
||||
"merge_advanced": "高级",
|
||||
"merge_back": "返回",
|
||||
"merge_batch_fmt": "每次最多合并 %d 笔输入——重复执行以完成剩余部分。",
|
||||
"merge_confirm_btn": "确认",
|
||||
"merge_confirm_fmt": "将约 %s DRGX 从 %d 笔输入合并到 %s?",
|
||||
"merge_create_zaddr": "创建屏蔽地址",
|
||||
"merge_creating": "正在创建地址…",
|
||||
"merge_description": "将多个 UTXO 合并到一个屏蔽地址。这可以帮助减小钱包大小并提高隐私性。",
|
||||
"merge_fee_hint": "此交易的网络手续费。",
|
||||
"merge_funds": "合并资金",
|
||||
"merge_max_inputs": "每批最大输入数",
|
||||
"merge_no_spendable": "暂无可合并的可用输入。",
|
||||
"merge_progress": "正在合并…这可能需要几分钟。您可以关闭此窗口。",
|
||||
"merge_scope_fmt": "%d 笔透明输入 + %d 笔屏蔽输入 ・ 约 %s DRGX 可用",
|
||||
"merge_scope_loading": "正在检查您的输入…",
|
||||
"merge_send_failed": "合并失败:",
|
||||
"merge_source": "合并",
|
||||
"merge_src_both": "两者",
|
||||
"merge_src_shielded": "屏蔽",
|
||||
"merge_src_transparent": "透明",
|
||||
"merge_started": "合并操作已开始",
|
||||
"merge_title": "合并到地址",
|
||||
"mig_adopt": "将其设为我的钱包",
|
||||
@@ -1058,6 +1419,13 @@
|
||||
"peers_upper": "节点",
|
||||
"peers_version": "版本",
|
||||
"pending": "待处理",
|
||||
"picker_dat_count": "此文件夹中有 %d 个钱包文件",
|
||||
"picker_dat_none": "此文件夹中没有直接存放的钱包文件",
|
||||
"picker_empty": "此文件夹没有子文件夹或钱包文件。",
|
||||
"picker_home": "主目录",
|
||||
"picker_select": "扫描此文件夹",
|
||||
"picker_title": "选择要扫描的文件夹",
|
||||
"picker_up": "上一级",
|
||||
"pin_change_desc": "更改您的解锁 PIN 码。您需要当前 PIN 码和新 PIN 码。",
|
||||
"pin_confirm_new_label": "确认新 PIN 码:",
|
||||
"pin_current_label": "当前 PIN 码:",
|
||||
@@ -1194,6 +1562,7 @@
|
||||
"save_z_transactions": "将 Z 交易保存到列表",
|
||||
"sb_auth_failed": "认证失败 — 请检查 rpcuser/rpcpassword",
|
||||
"sb_block": "区块: %d",
|
||||
"sb_block_db_unreadable": "区块数据库无法读取——需要重建",
|
||||
"sb_building_witnesses": "正在设置见证",
|
||||
"sb_building_witnesses_pct": "正在重建见证 %.0f%%",
|
||||
"sb_connecting_daemon": "正在连接 dragonxd...",
|
||||
@@ -1209,6 +1578,7 @@
|
||||
"sb_dragonxd_stopped": "dragonxd 已停止",
|
||||
"sb_dragonxd_stopping": "正在停止 dragonxd...",
|
||||
"sb_extracting_sapling": "正在提取 Sapling 参数...",
|
||||
"sb_finishing_repair": "正在完成钱包修复——重新扫描中…",
|
||||
"sb_importing_keys": "正在导入密钥",
|
||||
"sb_loading_config": "正在加载配置...",
|
||||
"sb_mining_hs": "%.1f H/s",
|
||||
@@ -1218,6 +1588,7 @@
|
||||
"sb_net_mhs": "网络: %.2f MH/s",
|
||||
"sb_no_conf": "未找到 DRAGONX.conf",
|
||||
"sb_peers": "节点: %zu",
|
||||
"sb_plaintext_remote_blocked": "拒绝以明文形式向远程主机发送 RPC 凭据。请在 DRAGONX.conf 中添加 rpcallowplaintext=1 以允许此操作,或启用 rpctls=1 使用 TLS。",
|
||||
"sb_rescanning": "重新扫描",
|
||||
"sb_rescanning_pct": "重新扫描 %.0f%%",
|
||||
"sb_restarting_daemon": "正在重启守护进程...",
|
||||
@@ -1229,6 +1600,7 @@
|
||||
"sb_waiting_config": "等待守护进程配置...",
|
||||
"sb_waiting_daemon": "等待 dragonxd 启动...",
|
||||
"sb_waiting_daemon_err": "等待 dragonxd — %s",
|
||||
"sb_wallet_needs_recovery": "可进行钱包修复",
|
||||
"sb_warming_up": "正在预热...",
|
||||
"sb_witness_cache": "正在重建见证",
|
||||
"scale_effects": "缩放与效果",
|
||||
@@ -1238,6 +1610,45 @@
|
||||
"screenshot_sweep_full": "完整 UI 遍历",
|
||||
"search_icons": "搜索图标...",
|
||||
"search_placeholder": "搜索...",
|
||||
"sec_changing_passphrase": "正在更改密码短语…",
|
||||
"sec_changing_pin": "正在更改 PIN…",
|
||||
"sec_couldnt_lock_wallet": "无法锁定钱包——它仍处于解锁状态。请检查守护进程连接。",
|
||||
"sec_encrypted_backup_suffix": "\n加密备份:wallet.dat.encrypted.bak",
|
||||
"sec_encrypting_wallet": "正在加密钱包…",
|
||||
"sec_encryption_did_not_complete": "钱包加密未完成——你的钱包尚未加密。请打开设置以完成加密。",
|
||||
"sec_encryption_failed_prefix": "加密失败:",
|
||||
"sec_failed_prefix": "失败:",
|
||||
"sec_failed_to_create_vault": "创建保险库失败",
|
||||
"sec_importing_keys_rescanning": "正在导入密钥并重新扫描区块链——期间钱包仍可使用",
|
||||
"sec_incorrect_current_pin": "当前 PIN 错误",
|
||||
"sec_incorrect_passphrase_decrypt": "密码短语错误",
|
||||
"sec_incorrect_passphrase_pin_setup": "密码短语错误",
|
||||
"sec_incorrect_pin_remove": "PIN 错误",
|
||||
"sec_internal_error_change_pin": "内部错误",
|
||||
"sec_internal_error_remove_pin": "内部错误",
|
||||
"sec_mode_passphrase": " 密码短语",
|
||||
"sec_not_connected_to_daemon": "未连接到守护进程",
|
||||
"sec_not_connected_to_daemon_pin": "未连接到守护进程",
|
||||
"sec_passphrase_changed_successfully": "密码短语更改成功",
|
||||
"sec_pin_changed_successfully": "PIN 更改成功",
|
||||
"sec_pin_removed": "PIN 已移除",
|
||||
"sec_pin_set_successfully": "PIN 设置成功",
|
||||
"sec_restart_daemon_for_encryption": "请重启守护进程以使加密生效。",
|
||||
"sec_too_many_attempts_wait": "尝试次数过多。请等待 %.0f 秒…",
|
||||
"sec_total_elapsed_fmt": "总耗时:%d 分 %02d 秒",
|
||||
"sec_unlock_button": "解锁",
|
||||
"sec_unlock_failed_prefix": "解锁失败:",
|
||||
"sec_unlocking_fmt": "正在解锁%s",
|
||||
"sec_use_passphrase_instead": "改用密码短语",
|
||||
"sec_use_pin_instead": "改用 PIN",
|
||||
"sec_verifying_passphrase": "正在验证密码短语…",
|
||||
"sec_verifying_pin": "正在验证 PIN…",
|
||||
"sec_wallet_decrypted_all_keys_imported": "钱包解密成功!所有密钥已导入。",
|
||||
"sec_wallet_encrypted_and_pin_set": "钱包已加密并已设置 PIN",
|
||||
"sec_wallet_encrypted_but_pin_vault_failed": "钱包已加密,但 PIN 保险库创建失败",
|
||||
"sec_wallet_encrypted_restarting_daemon": "钱包已加密。正在重启守护进程…",
|
||||
"sec_wallet_encrypted_successfully": "钱包加密成功",
|
||||
"sec_wallet_locked_title": "钱包已锁定",
|
||||
"security": "安全",
|
||||
"seed_backup_button": "助记词",
|
||||
"seed_backup_close": "关闭",
|
||||
@@ -1261,6 +1672,7 @@
|
||||
"send_amount": "金额",
|
||||
"send_amount_details": "金额详情",
|
||||
"send_amount_upper": "金额",
|
||||
"send_available_note": "可用",
|
||||
"send_cannot_send_now": "现在无法发送 — 请检查连接、同步和可用余额。",
|
||||
"send_clear_fields": "清除所有表单字段?",
|
||||
"send_contacts_button": "从联系人中选择",
|
||||
@@ -1448,17 +1860,34 @@
|
||||
"status": "状态",
|
||||
"stop_external": "停止外部守护进程",
|
||||
"stop_mining": "停止挖矿",
|
||||
"stratum_allowip_hint": "允许来自指定 IP 或 CIDR 的矿工连接(留空 = 仅限本机)",
|
||||
"stratum_expose_warn": "会向您允许的网络开放挖矿端口——仅在受信任的局域网中使用。",
|
||||
"stratum_host": "托管矿池(stratum)",
|
||||
"stratum_host_hint": "矿工可通过 RandomX stratum 矿机连接本机的 22769 端口(RPC 端口 + 1000)。需重启节点以生效。",
|
||||
"stratum_host_section": "矿池托管",
|
||||
"submitting_transaction": "正在提交交易...",
|
||||
"success": "成功",
|
||||
"summary": "摘要",
|
||||
"sweep_button": "归集",
|
||||
"sweep_caveat": "导入密钥以签署一笔将其所有资金转移到您地址的交易。密钥将保留在钱包中,余额为零。",
|
||||
"sweep_current_theme_only": "仅当前主题",
|
||||
"sweep_dest_label": "将归集资金发送到",
|
||||
"sweep_dest_new": "新的屏蔽地址(推荐)",
|
||||
"sweep_done": "完成 — 资金已归集到您的地址。",
|
||||
"sweep_to": "归集到:",
|
||||
"sweep_toggle": "归集到我的钱包(不保留密钥)",
|
||||
"sweep_tx": "交易:",
|
||||
"swin_connection_failed": "连接失败:",
|
||||
"swin_connection_successful": "连接成功!\ndragonxd 版本:",
|
||||
"swin_invalid_suffix": "(无效)",
|
||||
"swin_no_history_file_found": "未找到历史文件",
|
||||
"swin_rescan_failed": "重新扫描失败:",
|
||||
"swin_rescan_started_from_block": "重新扫描已开始,起始区块 ",
|
||||
"swin_rescan_to": " 至 ",
|
||||
"swin_rpc_client_not_initialized": "RPC 客户端未初始化",
|
||||
"swin_settings_saved": "设置已保存",
|
||||
"swin_theme_list_refreshed": "主题列表已刷新",
|
||||
"swin_ztx_history_cleared": "Z 交易历史已清除",
|
||||
"switch_corrupt_body": "此钱包似乎已损坏——节点无法打开它。请从备份恢复、重新创建,或尝试修复。",
|
||||
"switch_corrupt_repair": "尝试修复(salvage)",
|
||||
"switch_progress_background": "在后台继续",
|
||||
@@ -1619,6 +2048,8 @@
|
||||
"tt_simple_bg": "使用简单渐变作为背景\\n快捷键:Ctrl+Up",
|
||||
"tt_simple_bg_alt": "使用主题背景图像的渐变版本\\n快捷键:Ctrl+Up",
|
||||
"tt_stop_external": "适用于连接到在此钱包\\n外部启动的守护进程",
|
||||
"tt_stratum_host": "在此节点上运行 RandomX stratum 矿池服务器,让其他 RandomX 矿工可以连接到本机。需要 v1.3.0 及以上版本的节点,并需重启节点以生效。",
|
||||
"tt_sweep_current_theme_only": "仅扫描当前主题,而不是遍历每个主题",
|
||||
"tt_test_conn": "验证与守护进程的 RPC 连接",
|
||||
"tt_theme_effects": "每个主题的闪烁、发光、色调循环",
|
||||
"tt_theme_hotkey": "快捷键:Ctrl+左/右箭头切换主题",
|
||||
@@ -1627,6 +2058,7 @@
|
||||
"tt_ui_opacity": "卡片和侧边栏不透明度(100% = 完全不透明,越低越透明)",
|
||||
"tt_validate": "检查 DragonX 地址是否有效",
|
||||
"tt_verbose": "将详细连接诊断、守护进程状态\\n和端口所有者信息记录到控制台选项卡",
|
||||
"tt_wallet_size_warn": "屏蔽钱包会随每个票据的见证数据增长——将多个票据合并到一个地址可以缩小体积。请先备份。",
|
||||
"tt_wallets_button": "列出您的钱包文件并在它们之间切换",
|
||||
"tt_website": "打开 DragonX 网站",
|
||||
"tt_window_opacity": "背景不透明度(越低 = 桌面透过窗口可见)",
|
||||
@@ -1693,8 +2125,68 @@
|
||||
"view_on_explorer": "在浏览器中查看",
|
||||
"waiting_for_daemon": "等待守护进程连接...",
|
||||
"wallet": "钱包",
|
||||
"wallet_degraded_notify": "您的钱包以受限功能模式打开:现有资金安全且可用,但无法创建新地址或进行屏蔽操作。请备份助记词并用它恢复钱包以完全修复。",
|
||||
"wallet_diagnostics_hdr": "诊断",
|
||||
"wallet_empty": "您的钱包是空的",
|
||||
"wallet_empty_hint": "切换到接收页面获取您的地址并开始接收资金。",
|
||||
"wallet_options_hdr": "选项",
|
||||
"wallet_rebuild_failed": "重建未能生成有效的钱包,因此未做任何更改。您的钱包未受影响。",
|
||||
"wallet_rebuild_install_failed": "无法安装重建后的钱包;您当前的钱包已保留原样。",
|
||||
"wallet_rebuild_no_helper": "此版本不提供钱包重建助手。请使用还原功能,或手动重建。",
|
||||
"wallet_rebuild_no_source": "找不到可读取的钱包用于重建。未做任何更改。",
|
||||
"wallet_rebuild_ok": "钱包数据库已重建——节点正在加载并重新扫描以恢复您的余额。",
|
||||
"wallet_rebuild_started": "正在修复您的钱包文件——请不要关闭此窗口。",
|
||||
"wallet_recovered_body": "没有任何内容被删除。您的原始钱包仍以带日期的备份文件形式保存在您的电脑上,下面的每个选项都只会复制或重命名文件——绝不会删除文件。您的密钥永远不会重新生成,只会被重新读取。\n\n现在加载的修复副本可能会缺少最近的一些交易,因此在您完成下方操作并重新扫描之前,余额可能会显示偏低。",
|
||||
"wallet_recovered_dismiss": "暂不处理——保留修复副本",
|
||||
"wallet_recovered_dismiss_sub": "不会更改任何文件。您可以随时从状态栏重新打开此提示;无论如何,您的原始文件都会安全地保留备份。",
|
||||
"wallet_recovered_notify": "您的钱包文件需要修复——原始文件已安全备份。打开应用查看您的选项。",
|
||||
"wallet_recovered_open_folder": "显示文件",
|
||||
"wallet_recovered_open_folder_sub": "打开钱包数据文件夹,方便您自行查看备份文件——不会更改任何内容。",
|
||||
"wallet_recovered_rebuild": "自动修复(推荐)",
|
||||
"wallet_recovered_rebuild_sub": "从您最完整的钱包文件中重新读取每条可恢复的记录,并写入一个全新的干净文件,然后重启。这是最彻底的选项——无论如何,您当前的文件都会保留为带日期的备份。",
|
||||
"wallet_recovered_restore": "改为还原原始文件",
|
||||
"wallet_recovered_restore_sub": "将您最大的未改动备份原样放回,然后重新扫描——速度稍快,但完整程度仅限于该文件本身。无论如何,您当前的文件都会保留为带日期的备份。",
|
||||
"wallet_recovered_safety": "您的币是安全的。",
|
||||
"wallet_recovered_title": "您的钱包文件需要快速修复",
|
||||
"wallet_recovered_warn": "应用启动时发现您的钱包文件未通过一致性检查——这通常发生在应用更新或非正常关闭之后。应用已保护您的数据:它将旧文件另存一旁,并加载了一个修复副本,因此您不会被卡住。",
|
||||
"wallet_recovery_decide_later": "稍后决定",
|
||||
"wallet_recovery_decide_later_tip": "关闭此提示并保留当前已加载的副本。不会更改任何内容,您可以随时进行修复——状态栏会保留“可修复钱包”链接。",
|
||||
"wallet_recovery_details_label": "显示技术细节",
|
||||
"wallet_recovery_done": "完成",
|
||||
"wallet_recovery_failure_body": "修复已运行,但结果未通过验证(无法读取,或没有地址),因此在替换任何内容之前已被自动丢弃。您的钱包与点击前完全一样——磁盘上没有任何改动。",
|
||||
"wallet_recovery_failure_title": "修复未成功",
|
||||
"wallet_recovery_files_detail": "您的原始钱包仍在这里——应用已将其重命名为数据文件夹中带日期的备份(wallet.<数字>.bak),未删除任何内容。“修复”会从您最完整的钱包中读取每条记录到一个全新的干净文件中。“还原”会将您最大的备份原样复制回来。当前加载的副本也会保留为备份。",
|
||||
"wallet_recovery_files_label": "我的文件会怎样?",
|
||||
"wallet_recovery_notnow_short": "暂不",
|
||||
"wallet_recovery_other_options": "其他选项",
|
||||
"wallet_recovery_rebuild_card": "自动修复",
|
||||
"wallet_recovery_rebuild_card_desc": "将每条可恢复的记录读取到一个干净的文件中,然后重启。这是最彻底的选项。",
|
||||
"wallet_recovery_recommended": "推荐",
|
||||
"wallet_recovery_repair_go": "修复",
|
||||
"wallet_recovery_rescan_body": "正在重新扫描区块链以重建您的余额和交易历史。这可能需要几分钟——您无需进行任何操作,请勿在此过程中重启节点。",
|
||||
"wallet_recovery_rescan_elapsed": "已运行 %s",
|
||||
"wallet_recovery_rescan_size": "已重建的钱包目前为 %s,且仍在填充中",
|
||||
"wallet_recovery_rescan_slow": "这比平时耗时更长,对于较大的钱包这是正常现象。可以放心让它继续运行——您可以在“高级 ▸ 控制台”下查看进度。",
|
||||
"wallet_recovery_rescan_title": "正在完成钱包修复",
|
||||
"wallet_recovery_restore_card": "还原原始文件",
|
||||
"wallet_recovery_restore_card_desc": "将您最大的未改动备份原样放回——完整程度仅限于该文件本身。",
|
||||
"wallet_recovery_restore_go": "还原",
|
||||
"wallet_recovery_success_body": "应用已加载您修复后的钱包,正在重新扫描以统计余额——这可能需要几分钟。它会读取所能读取的每条记录,但在极少数损坏文件的情况下,恢复的地址可能会比之前略少——或偶尔略多。重新扫描完成后,请检查您的余额和历史记录是否正确。",
|
||||
"wallet_recovery_success_restore": "您最大的备份文件已放回原位并正在加载,应用正在重新扫描。它是原样还原的,因此完整程度与该文件本身一致——重新扫描完成后请检查您的余额。",
|
||||
"wallet_recovery_try_other": "尝试其他选项",
|
||||
"wallet_recovery_whats_happened": "发生了什么?",
|
||||
"wallet_recovery_working_label": "处理中",
|
||||
"wallet_restore_bad_backup": "备份钱包文件似乎无法读取,因此未进行还原——您当前的钱包未被更改。请改用您自己的备份进行还原。",
|
||||
"wallet_restore_busy": "节点正忙于重启——请稍后再试。",
|
||||
"wallet_restore_copy_failed": "无法安装备份钱包;您当前的钱包保持不变。",
|
||||
"wallet_restore_move_failed": "无法将当前钱包移至一旁——未做任何更改。",
|
||||
"wallet_restore_no_backup": "未找到可还原的 wallet.<时间戳>.bak 文件。未做任何更改。",
|
||||
"wallet_restore_no_restart": "您的原始钱包已还原,但节点未重启——请从设置中启动节点。",
|
||||
"wallet_restore_ok": "原始钱包已还原。节点正在加载中。",
|
||||
"wallet_restore_started": "正在还原您的原始文件——请勿关闭此窗口。",
|
||||
"wallet_restore_stop_failed": "节点未能及时停止,因此未做任何更改。请重试。",
|
||||
"wallet_size_consolidate": "合并笔记…",
|
||||
"wallet_size_warn": "此钱包文件较大。合并您的笔记可以抑制其进一步增长。",
|
||||
"wallets_active": "活跃",
|
||||
"wallets_add": "添加文件夹",
|
||||
"wallets_add_folder": "同时扫描另一个文件夹中的钱包文件:",
|
||||
@@ -1737,6 +2229,7 @@
|
||||
"wallets_open_inplace_tt": "在原位置打开此钱包 — 链接到数据目录(不复制)",
|
||||
"wallets_remove_folder": "停止扫描此文件夹",
|
||||
"wallets_reveal": "打开文件夹",
|
||||
"wallets_scan_folder": "扫描其他文件夹以查找钱包…",
|
||||
"wallets_scanned_folders": "已扫描的文件夹:",
|
||||
"wallets_sort_addresses": "地址",
|
||||
"wallets_sort_asc": "升序(最早/最少/最小优先)",
|
||||
@@ -1806,19 +2299,30 @@
|
||||
"wiz_welcome_sub": "只需几项快速设置,即可启用您的全节点。",
|
||||
"wiz_welcome_title": "欢迎使用 ObsidianDragon!",
|
||||
"wizard_daemon_start_failed": "启动守护进程失败 — 将自动重试",
|
||||
"xmrig_active_tt": "当前已安装(使用中)的矿工程序",
|
||||
"xmrig_browse_releases": "浏览所有版本…",
|
||||
"xmrig_checking": "正在检查最新的矿工程序…",
|
||||
"xmrig_current": "当前:",
|
||||
"xmrig_downgrade_note": "安装不同版本的矿工程序会替换当前版本;请先停止挖矿,更改将在下次启动时生效。",
|
||||
"xmrig_download_install": "下载并安装",
|
||||
"xmrig_downloading": "正在下载…",
|
||||
"xmrig_install_this": "安装此版本",
|
||||
"xmrig_installed": "已安装:",
|
||||
"xmrig_installed_ok": "矿工程序已安装",
|
||||
"xmrig_installing": "正在安装…",
|
||||
"xmrig_latest": "最新:",
|
||||
"xmrig_latest_badge": "最新",
|
||||
"xmrig_latest_tt": "可用的最新版本",
|
||||
"xmrig_loading_releases": "正在加载发行版…",
|
||||
"xmrig_no_notes": "此版本没有发行说明。",
|
||||
"xmrig_none": "无",
|
||||
"xmrig_prerelease_tt": "预发布/测试版本",
|
||||
"xmrig_reinstall": "重新安装",
|
||||
"xmrig_released": "发布日期:",
|
||||
"xmrig_releases": "xmrig 版本",
|
||||
"xmrig_status_current": "此版本已安装",
|
||||
"xmrig_status_latest": "最新版本",
|
||||
"xmrig_status_older": "早于您已安装的版本",
|
||||
"xmrig_stop_mining_first": "更新矿工程序前请先停止挖矿。",
|
||||
"xmrig_unavailable_body": "此平台没有可用的矿工构建版本。",
|
||||
"xmrig_unavailable_title": "矿工更新不可用",
|
||||
|
||||
@@ -965,6 +965,11 @@ edition-label = { position = 120 }
|
||||
link-button = { width = 100, font = "button-sm" }
|
||||
close-button = { width = 120, font = "button", align = "center" }
|
||||
|
||||
[dialogs.faq]
|
||||
width = 860.0
|
||||
height = 660.0
|
||||
window = { width = 860, height = 660 }
|
||||
|
||||
[dialogs.settings]
|
||||
width = 600.0
|
||||
height = 550.0
|
||||
|
||||
+237
-172
File diff suppressed because it is too large
Load Diff
@@ -169,6 +169,7 @@ public:
|
||||
bool daemonAutoShieldActive() const { return daemon_autoshield_active_; }
|
||||
const std::string& daemonAutoShieldAddress() const { return daemon_autoshield_address_; }
|
||||
const std::string& daemonAutoShieldDisabledReason() const { return daemon_autoshield_disabled_reason_; }
|
||||
bool daemonAutoShieldSeedRecoverable() const { return daemon_autoshield_seed_recoverable_; }
|
||||
|
||||
// W7 QoL: a plaintext support-diagnostics snapshot (version, variant, daemon/RPC/wallet/log state)
|
||||
// for the "Copy diagnostics" action. Contains no secrets.
|
||||
@@ -393,6 +394,10 @@ public:
|
||||
|
||||
// Force refresh
|
||||
void refreshNow();
|
||||
// Called on window restore: drop the daemon-output backlog that accumulated while minimized (the
|
||||
// per-frame update loop was paused), so a background witness rebuild that started+finished during
|
||||
// the minimize isn't parsed in one batch and mistaken for a completed rescan (spurious toast).
|
||||
void skipDaemonOutputBacklog();
|
||||
void refreshMiningInfo();
|
||||
void refreshPeerInfo();
|
||||
void refreshMarketData();
|
||||
@@ -458,6 +463,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
void showAboutDialog() { show_about_ = true; }
|
||||
void showFaqDialog() { show_faq_ = true; }
|
||||
|
||||
// Legacy tab compat — maps int to NavPage
|
||||
void setCurrentTab(int tab);
|
||||
@@ -755,6 +761,22 @@ private:
|
||||
// wallets. In-memory only (resets on app restart).
|
||||
std::map<std::string, std::int64_t> chat_seen_watermark_;
|
||||
|
||||
// ── Per-frame render caches (avoid O(N) recompute every frame; see the respective call sites) ──
|
||||
// Chat nav-badge unread count — recomputed only when the store revision changes or after a short
|
||||
// interval (mute/hide/seen changes don't bump the store). See App::chatUnreadCount().
|
||||
mutable std::uint64_t chat_unread_rev_ = ~0ull; // ~0 forces the first compute
|
||||
mutable int chat_unread_cached_ = 0;
|
||||
mutable double chat_unread_computed_at_ = 0.0;
|
||||
// Sidebar unconfirmed-tx badge — recomputed only when the tx list changes (keyed on last_tx_update +
|
||||
// size), not every frame. See App::render().
|
||||
std::int64_t sb_unconf_key_ts_ = -1;
|
||||
std::size_t sb_unconf_key_n_ = 0;
|
||||
int sb_unconf_count_ = 0;
|
||||
// Daemon-memory probe is expensive (/proc scan on Linux, popen on macOS); throttle it to ~1.5s so the
|
||||
// Mining tab's per-frame read doesn't hammer the OS. See App::getDaemonMemoryUsageMB().
|
||||
mutable double daemon_mem_cached_mb_ = 0.0;
|
||||
mutable double daemon_mem_probe_at_ = 0.0;
|
||||
|
||||
// ── Chat note buffer (BOTH variants) ────────────────────────────────────────────────────────
|
||||
// Each chat message is a shielded tx that spends a note; its change needs a few confirmations before
|
||||
// it's spendable again (lite: backend ANCHOR_OFFSET+1 = 5; full node: z_sendmany minconf = 1), so
|
||||
@@ -796,6 +818,7 @@ private:
|
||||
// balance poll discards per-note data). Mirrors chat_fast_scan_in_flight_.
|
||||
bool chat_note_scan_in_flight_ = false;
|
||||
double chat_note_scan_last_ = 0.0; // ImGui time of the last note scan (rate limit)
|
||||
double chat_note_scan_ms_ = 0.0; // measured cost of the last note scan (adaptive back-off)
|
||||
// Most-recent chain tip, cached across refreshes: a lite refresh model that carries spendableOutputs
|
||||
// may NOT carry sync status that same cycle (tolerated partial refresh), so verifiedSelfNoteCount reads
|
||||
// this cache rather than requiring the current model to have both — else the budget flickers to 0.
|
||||
@@ -805,6 +828,9 @@ private:
|
||||
// Coordinator helpers (both variants unless noted; see app_network.cpp).
|
||||
void refreshChatNoteBudget(const wallet::LiteWalletAppRefreshModel& model); // lite: recompute caches on a fresh model
|
||||
void refreshChatNoteBudgetNode(); // full node: rate-limited z_listunspent worker scan
|
||||
// Feeds the SAME chat send-budget from an already-collected z_listunspent (the address refresh),
|
||||
// so the dedicated scan above is skipped while the address refresh is active (dedup — see #3).
|
||||
void updateChatNoteBudgetFromUnspent(const std::vector<services::NetworkRefreshService::UnspentNoteLite>& unspentNotes);
|
||||
void pumpChatNoteBuffer(); // per-frame: drain the queue / build the buffer
|
||||
int verifiedSelfNoteCount(const wallet::LiteWalletAppRefreshModel& model); // lite
|
||||
int pipelineSelfNoteCount(const wallet::LiteWalletAppRefreshModel& model); // lite
|
||||
@@ -826,6 +852,9 @@ public:
|
||||
int chatUnreadCount() const;
|
||||
// Mark a conversation read up to latestTs (called by the Chat tab while a thread is displayed).
|
||||
void markChatConversationSeen(const std::string& cid, std::int64_t latestTs);
|
||||
// Drop the seen-watermark for a conversation (used on revive-delete so a re-imported message — even one
|
||||
// whose stamped time predates the deleted thread's last message — still badges as unread).
|
||||
void forgetChatConversationSeen(const std::string& cid);
|
||||
private:
|
||||
// Provision the chat identity once the wallet seed is reachable+unlocked (per-tick, both
|
||||
// variants); derives via deriveChatIdentityFromSecret and wipes the secret. No-op when the
|
||||
@@ -873,6 +902,8 @@ private:
|
||||
void fastScanChatMemos();
|
||||
bool chat_fast_scan_in_flight_ = false; // guard against overlapping fast-scan RPCs
|
||||
float chat_fast_scan_accum_ = 0.0f; // seconds since the last fast-scan (dedicated ~2.5s poll)
|
||||
double chat_fast_scan_last_ = 0.0; // ImGui time of the last 0-conf fast scan (adaptive back-off)
|
||||
double chat_fast_scan_ms_ = 0.0; // measured cost of the last fast scan (z_listreceivedbyaddress)
|
||||
bool font_rebuild_requested_ = false; // set by requestFontRebuild(); consumed in preFrame()
|
||||
// Lite first-run welcome prompt: dismissed for the session once the user picks an action.
|
||||
bool lite_firstrun_dismissed_ = false;
|
||||
@@ -1024,6 +1055,7 @@ private:
|
||||
bool show_demo_window_ = false;
|
||||
bool show_settings_ = false;
|
||||
bool show_about_ = false;
|
||||
bool show_faq_ = false;
|
||||
bool show_import_key_ = false;
|
||||
bool show_export_key_ = false;
|
||||
bool show_backup_ = false;
|
||||
@@ -1139,6 +1171,14 @@ private:
|
||||
std::time_t sync_settle_until_ = 0; // hold the sync-throttle until this wall-clock time (0 = not settling)
|
||||
double last_balance_scan_ms_ = 0.0; // measured cost of the last z_gettotalbalance scan
|
||||
bool force_balance_refresh_ = false; // a wallet mutation forces the next balance poll through the throttle
|
||||
// Same adaptive back-off applied to the other two O(mapWallet) scans that hold the daemon's cs_main:
|
||||
// the address scan (z_listunspent) and the history scan (z_listreceivedbyaddress). Without this, a
|
||||
// large shielded wallet re-scans them every tab cadence (~seconds each), saturating cs_main and
|
||||
// starving block connection near the tip (where effectivelySyncing() reads false). See
|
||||
// addressRefreshDue() / txRefreshDue(); only the routine periodic poll is throttled — explicit
|
||||
// refreshes (tab switch, dirty set, in-flight send) call the refresh directly and bypass this.
|
||||
double last_address_scan_ms_ = 0.0; // measured cost of the last address scan
|
||||
double last_tx_scan_ms_ = 0.0; // measured cost of the last history scan
|
||||
// Auto-clear for secrets copied to the clipboard. Only a hash of the copied secret is kept.
|
||||
std::uint64_t clipboard_secret_hash_ = 0;
|
||||
double clipboard_clear_deadline_ = 0.0;
|
||||
@@ -1298,6 +1338,11 @@ private:
|
||||
// the per-second mining/rescan-status pollers are suppressed (the daemon holds cs_main for
|
||||
// the whole scan and would block them); completion is signalled by the rescan RPC callback.
|
||||
bool runtime_rescan_active_ = false;
|
||||
// True only for a rescan the WALLET/USER initiated (the Rescan button, a -rescan/salvage/zap/reindex
|
||||
// restart, key import, seed migration) — not an autonomous background witness rebuild the daemon does
|
||||
// on its own. Gates the "Blockchain rescan complete" toast so background rebuilds don't fire it;
|
||||
// cleared when the toast is shown.
|
||||
std::atomic<bool> user_initiated_rescan_{false}; // atomic: some rescan triggers run on worker threads
|
||||
// Set when a bootstrap completes; consumed once the daemon is connected to auto-run a rescan
|
||||
// that reconciles the preserved wallet.dat against the freshly-imported chain.
|
||||
bool post_bootstrap_rescan_pending_ = false;
|
||||
@@ -1489,6 +1534,11 @@ private:
|
||||
void applyRefreshPolicy(ui::NavPage page);
|
||||
bool effectivelySyncing() const; // syncing, or within the post-sync settle window (hysteresis)
|
||||
bool balanceRefreshDue() const; // adaptive: enough time elapsed given the last balance-scan cost?
|
||||
bool addressRefreshDue() const; // same adaptive back-off for the address scan (z_listunspent)
|
||||
bool txRefreshDue() const; // same adaptive back-off for the history scan (z_listreceivedbyaddress)
|
||||
// Shared duty-cycle rule: a scan may resume only once (lastScanMs / kScanDutyCycle) has elapsed since
|
||||
// lastUpdate, so any single O(mapWallet) scan can occupy at most ~kScanDutyCycle of wall-clock.
|
||||
bool scanRefreshDue(std::int64_t lastUpdate, double lastScanMs) const;
|
||||
bool currentPageNeedsWalletDataRefresh() const;
|
||||
bool shouldRunWalletTransactionRefresh() const;
|
||||
bool shouldRefreshTransactions() const;
|
||||
|
||||
+157
-25
@@ -882,13 +882,32 @@ bool App::effectivelySyncing() const
|
||||
// cheap wallet (sub-cadence cost) is unaffected — the tab's Core timer stays the real cadence; a ~20s
|
||||
// scan on a large wallet backs off to roughly every ~200s instead of every 2s, freeing cs_main for
|
||||
// block connection. A wallet mutation bypasses this via force_balance_refresh_.
|
||||
bool App::scanRefreshDue(std::int64_t lastUpdate, double lastScanMs) const
|
||||
{
|
||||
constexpr double kScanDutyCycle = 0.10;
|
||||
if (lastUpdate == 0) return true; // never fetched
|
||||
if (lastScanMs <= 0.0) return true; // no cost measured yet
|
||||
const double minInterval = (lastScanMs / 1000.0) / kScanDutyCycle;
|
||||
return std::difftime(std::time(nullptr), static_cast<std::time_t>(lastUpdate)) >= minInterval;
|
||||
}
|
||||
|
||||
bool App::balanceRefreshDue() const
|
||||
{
|
||||
constexpr double kBalanceDutyCycle = 0.10;
|
||||
if (state_.last_balance_update == 0) return true; // never fetched
|
||||
if (last_balance_scan_ms_ <= 0.0) return true; // no cost measured yet
|
||||
const double minInterval = (last_balance_scan_ms_ / 1000.0) / kBalanceDutyCycle;
|
||||
return std::difftime(std::time(nullptr), state_.last_balance_update) >= minInterval;
|
||||
return scanRefreshDue(state_.last_balance_update, last_balance_scan_ms_);
|
||||
}
|
||||
|
||||
// The address scan (z_listunspent) and history scan (z_listreceivedbyaddress) are just as O(mapWallet)
|
||||
// and cs_main-bound as the balance scan, so they get the same adaptive back-off. This is what keeps a
|
||||
// large synced wallet from re-scanning them every tab cadence — the near-tip starvation the balance-only
|
||||
// throttle missed (only the periodic poll is gated; explicit refreshes call the refresh directly).
|
||||
bool App::addressRefreshDue() const
|
||||
{
|
||||
return scanRefreshDue(state_.last_address_update, last_address_scan_ms_);
|
||||
}
|
||||
|
||||
bool App::txRefreshDue() const
|
||||
{
|
||||
return scanRefreshDue(state_.last_tx_update, last_tx_scan_ms_);
|
||||
}
|
||||
|
||||
bool App::currentPageNeedsWalletDataRefresh() const
|
||||
@@ -1365,6 +1384,7 @@ void App::switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed
|
||||
if (daemon_controller_) daemon_controller_->clearExternalDaemonDetected();
|
||||
if (salvage && daemon_controller_) daemon_controller_->setSalvageOnNextStart(true); // repair a corrupt wallet
|
||||
else if (needRescan && daemon_controller_) daemon_controller_->setRescanOnNextStart(true); // (salvage implies rescan)
|
||||
if (salvage || needRescan) user_initiated_rescan_ = true; // wallet-triggered repair/rescan → completion should toast
|
||||
// Start ONCE. Do NOT retry-spawn: a second start while the first is still shutting down leaves
|
||||
// two dragonxd holding wallet.dat against each other (BDB "Failed to rename … Error"). The
|
||||
// stopDaemonForWalletSwitch() wait already ensured the old node's process is gone, so a valid
|
||||
@@ -1438,18 +1458,26 @@ void App::processWalletSwitchRevert()
|
||||
int App::chatUnreadCount() const
|
||||
{
|
||||
if (!chat::hushChatFeatureEnabledAtBuild() || !chat_service_.hasIdentity()) return 0;
|
||||
int unread = 0;
|
||||
// This is called every frame from render() to size the Chat nav badge. Recompute only when the store
|
||||
// actually changed (new/removed messages bump revision()) or after a short interval (to pick up
|
||||
// mute/hide/seen changes, which don't bump the store). Previously it ran a full O(conversations x
|
||||
// messages) scan that COPIED and stable_sorted every conversation's messages, every frame.
|
||||
const auto& store = chat_service_.store();
|
||||
for (const auto& cid : store.conversationIds()) {
|
||||
if (settings_ && settings_->isChatMuted(cid)) continue; // muted conversations don't badge (Q10)
|
||||
if (settings_ && settings_->isChatHidden(cid)) continue; // hidden conversations don't badge
|
||||
std::int64_t seen = 0;
|
||||
const auto it = chat_seen_watermark_.find(cid);
|
||||
if (it != chat_seen_watermark_.end()) seen = it->second;
|
||||
for (const auto& m : store.conversation(cid))
|
||||
if (m.direction == chat::ChatDirection::Incoming && m.timestamp > seen) ++unread;
|
||||
const std::uint64_t rev = store.revision();
|
||||
const double now = ImGui::GetTime();
|
||||
if (rev != chat_unread_rev_ || now - chat_unread_computed_at_ > 0.25) {
|
||||
chat_unread_cached_ = store.countUnread(
|
||||
[this](const std::string& cid) { // excluded from the badge
|
||||
return settings_ && (settings_->isChatMuted(cid) || settings_->isChatHidden(cid));
|
||||
},
|
||||
[this](const std::string& cid) -> std::int64_t { // seen watermark
|
||||
const auto it = chat_seen_watermark_.find(cid);
|
||||
return it != chat_seen_watermark_.end() ? it->second : 0;
|
||||
});
|
||||
chat_unread_rev_ = rev;
|
||||
chat_unread_computed_at_ = now;
|
||||
}
|
||||
return unread;
|
||||
return chat_unread_cached_;
|
||||
}
|
||||
|
||||
void App::markChatConversationSeen(const std::string& cid, std::int64_t latestTs)
|
||||
@@ -1457,6 +1485,11 @@ void App::markChatConversationSeen(const std::string& cid, std::int64_t latestTs
|
||||
if (latestTs > 0) chat_seen_watermark_[cid] = latestTs;
|
||||
}
|
||||
|
||||
void App::forgetChatConversationSeen(const std::string& cid)
|
||||
{
|
||||
chat_seen_watermark_.erase(cid);
|
||||
}
|
||||
|
||||
void App::wipePendingTransactionHistoryCachePassphrase()
|
||||
{
|
||||
if (!pending_transaction_history_cache_passphrase_.empty()) {
|
||||
@@ -1680,7 +1713,10 @@ void App::refreshCoreData()
|
||||
transactions_dirty_ = true;
|
||||
last_tx_block_height_ = -1;
|
||||
invalidateShieldedHistoryScanProgress(true);
|
||||
ui::Notifications::instance().success("Blockchain rescan complete");
|
||||
if (user_initiated_rescan_) {
|
||||
ui::Notifications::instance().success("Blockchain rescan complete");
|
||||
user_initiated_rescan_ = false; // surfaced once; not for background rebuilds
|
||||
}
|
||||
}
|
||||
|
||||
NetworkRefreshService::applyConnectionInfoResult(state_, result.info);
|
||||
@@ -1735,11 +1771,19 @@ void App::refreshCoreData()
|
||||
// cost (0 when balance was skipped), and arm the settle window only on the
|
||||
// syncing→caught-up edge so a wallet synced from the start is never throttled at connect.
|
||||
if (result.balanceScanMs > 0.0) last_balance_scan_ms_ = result.balanceScanMs;
|
||||
// Sticky-behind hysteresis: hold the low-impact profile for kSyncSettleSeconds after the
|
||||
// LAST time we read "behind" — not only on the caught-up edge. While actually syncing we
|
||||
// re-arm every Core tick, so the profile stays engaged continuously; once we truly reach
|
||||
// the tip (within the 2-block tolerance in applyCoreRefreshResult) we stop re-arming and it
|
||||
// lapses after the window. This is what breaks the near-tip feedback loop: a single stale or
|
||||
// optimistic "caught up" reading can no longer immediately unleash the heavy O(mapWallet)
|
||||
// scans and re-starve the tail (which dropped us behind again → oscillation). With the
|
||||
// scans suppressed, kSyncProfile keeps sync detection itself cheap, so we keep getting fresh
|
||||
// "behind" readings and stay latched until genuinely caught up. Window must exceed the
|
||||
// worst-case Core-refresh interval so it can't lapse between two behind readings mid-catchup.
|
||||
const bool nowSyncing = state_.sync.syncing;
|
||||
if (nowSyncing) {
|
||||
sync_settle_until_ = 0;
|
||||
} else if (was_core_syncing_) {
|
||||
constexpr double kSyncSettleSeconds = 8.0;
|
||||
constexpr double kSyncSettleSeconds = 30.0;
|
||||
if (nowSyncing || was_core_syncing_) {
|
||||
sync_settle_until_ = std::time(nullptr) + static_cast<std::time_t>(kSyncSettleSeconds);
|
||||
}
|
||||
was_core_syncing_ = nowSyncing;
|
||||
@@ -1868,7 +1912,13 @@ void App::refreshAddressData()
|
||||
|
||||
return [this, previousAddressCount, previousWalletIdentity, result = std::move(result)]() mutable {
|
||||
const bool addrListOk = result.addressListOk; // capture before the move
|
||||
const double scanMs = result.scanMs; // feed the adaptive throttle (addressRefreshDue)
|
||||
if (scanMs > 0.0) last_address_scan_ms_ = scanMs;
|
||||
// #3: this scan already listed the wallet's unspent notes — feed the chat send-budget from it
|
||||
// (dedups the dedicated chat z_listunspent). Grab it before `result` is moved below.
|
||||
auto unspentNotes = std::move(result.unspentNotes);
|
||||
NetworkRefreshService::applyAddressRefreshResult(state_, std::move(result));
|
||||
updateChatNoteBudgetFromUnspent(unspentNotes);
|
||||
// Mark the address list as loaded ONLY if enumeration actually succeeded — a swallowed
|
||||
// z_listaddresses/getaddressesbyaccount failure returns a falsely-short list, and stamping it
|
||||
// would let the empty-wallet warning trust a spurious 0 count (see maybeWarnEmptyWallet…).
|
||||
@@ -1927,6 +1977,13 @@ void App::refreshTransactionData()
|
||||
transactionSnapshot.maxShieldedReceiveScans = shieldedReceiveScanBudget(current_page_);
|
||||
transactionSnapshot.shieldedScanTipTolerance =
|
||||
shieldedScanTipTolerance(transactionSnapshot.shieldedAddresses.size());
|
||||
// When fully synced, widen the re-scan tolerance so the O(mapWallet) per-address history scan runs
|
||||
// every several blocks instead of ~every 2 — the heaviest residual synced-state cs_main cost. The
|
||||
// balance poll still surfaces incoming funds on its own cadence; only the detailed history list lags a
|
||||
// few minutes. (During catch-up the tx refresh is suppressed by kSyncProfile, so this is synced-only.)
|
||||
if (!effectivelySyncing())
|
||||
transactionSnapshot.shieldedScanTipTolerance =
|
||||
std::max(transactionSnapshot.shieldedScanTipTolerance, 8);
|
||||
ui::NavPage tracePage = current_page_;
|
||||
|
||||
auto enqueued = network_refresh_.enqueue(services::NetworkRefreshService::Job::Transactions, *worker_, [this, currentBlocks,
|
||||
@@ -1937,6 +1994,7 @@ void App::refreshTransactionData()
|
||||
|
||||
return [this, result = std::move(result)]() mutable {
|
||||
bool shieldedScanComplete = result.shieldedScanComplete;
|
||||
if (result.scanMs > 0.0) last_tx_scan_ms_ = result.scanMs; // feed the adaptive throttle (txRefreshDue)
|
||||
std::size_t nextShieldedScanStartIndex = result.nextShieldedScanStartIndex;
|
||||
auto shieldedScanHeights = std::move(result.shieldedScanHeights);
|
||||
NetworkRefreshService::TransactionCacheUpdate cacheUpdate{
|
||||
@@ -2002,6 +2060,9 @@ void App::refreshRecentTransactionData()
|
||||
transactionSnapshot.maxShieldedReceiveScans = 1;
|
||||
transactionSnapshot.shieldedScanTipTolerance =
|
||||
shieldedScanTipTolerance(transactionSnapshot.shieldedAddresses.size());
|
||||
if (!effectivelySyncing()) // synced: widen the re-scan tolerance (see refreshTransactionData)
|
||||
transactionSnapshot.shieldedScanTipTolerance =
|
||||
std::max(transactionSnapshot.shieldedScanTipTolerance, 8);
|
||||
ui::NavPage tracePage = current_page_;
|
||||
|
||||
auto enqueued = network_refresh_.enqueue(services::NetworkRefreshService::Job::Transactions, *worker_, [this, currentBlocks,
|
||||
@@ -2993,6 +3054,10 @@ void App::provisionChatIdentityFromSecret(std::string secret)
|
||||
// Persistence: unlock the seed-derived chat DB with the SAME secret and rehydrate the store
|
||||
// with prior messages (decrypted at rest under a key only this seed can derive).
|
||||
chat_service_.setPersistence(&chat_db_);
|
||||
// A blocked conversation's messages are dropped at ingest (old + future) until it is unblocked.
|
||||
chat_service_.setBlockedPredicate([this](const std::string& cid) {
|
||||
return settings_ && settings_->isChatBlocked(cid);
|
||||
});
|
||||
if (chat_db_.unlockWithSecret(trimmed)) {
|
||||
chat_service_.loadFromDatabase();
|
||||
// Baseline unread: treat everything already in the store at load as read, so only messages
|
||||
@@ -3531,9 +3596,25 @@ void App::refreshChatNoteBudget(const wallet::LiteWalletAppRefreshModel& model)
|
||||
void App::refreshChatNoteBudgetNode() {
|
||||
if (lite_wallet_ || !chat::hushChatFeatureEnabledAtBuild() || !chat_service_.hasIdentity()) return;
|
||||
if (!state_.connected || !rpc_ || !worker_ || state_.isLocked()) return;
|
||||
// Never contend the daemon's cs_main while the node is catching up. z_listunspent is O(mapWallet)
|
||||
// and holds cs_main for its whole duration — seconds on a large wallet — so an ungated per-few-second
|
||||
// chat scan here starves block connection and keeps the node from ever pinning to the tip. Chat
|
||||
// send-readiness can wait until we're synced; this budget is only advisory (the send path re-checks).
|
||||
if (effectivelySyncing()) return;
|
||||
// Only pre-warm the send-budget while the user is actually in chat. Off the Chat tab this heavy
|
||||
// z_listunspent is unnecessary — and on a wallet-data tab, refreshAddressData's own z_listunspent
|
||||
// already feeds the budget for free (updateChatNoteBudgetFromUnspent), so this dedicated scan is a
|
||||
// fallback for the Chat tab (where the address refresh doesn't run).
|
||||
if (current_page_ != ui::NavPage::Chat) return;
|
||||
if (chat_note_scan_in_flight_) return; // one scan at a time
|
||||
const double now = ImGui::GetTime();
|
||||
if (now - chat_note_scan_last_ < 4.0) return; // rate limit; self-throttles via the in-flight flag too
|
||||
// Adaptive back-off keyed on the last scan's own cost: on a large wallet where the scan takes many
|
||||
// seconds, this stretches the interval so the scan can't occupy more than ~10% of wall-clock (same
|
||||
// duty-cycle discipline as the balance/address/tx polls). A cheap wallet stays at the 4s floor.
|
||||
double minInterval = 4.0;
|
||||
if (chat_note_scan_ms_ > 0.0)
|
||||
minInterval = std::max(4.0, (chat_note_scan_ms_ / 1000.0) / 0.10);
|
||||
if (now - chat_note_scan_last_ < minInterval) return;
|
||||
|
||||
chat_note_scan_last_ = now;
|
||||
chat_note_scan_in_flight_ = true;
|
||||
@@ -3544,6 +3625,7 @@ void App::refreshChatNoteBudgetNode() {
|
||||
int verified = 0, pipeline = 0;
|
||||
std::uint64_t verifiedZat = 0;
|
||||
bool ok = false;
|
||||
const auto scanStart = std::chrono::steady_clock::now();
|
||||
try {
|
||||
rpc::RPCClient::TraceScope trace("HushChat / note-buffer scan");
|
||||
nlohmann::json notes = rpc_->call("z_listunspent", nlohmann::json::array({0})); // 0 = include maturing
|
||||
@@ -3563,9 +3645,12 @@ void App::refreshChatNoteBudgetNode() {
|
||||
}
|
||||
}
|
||||
} catch (const std::exception&) {}
|
||||
return [this, scanGen, ok, verified, pipeline, verifiedZat]() {
|
||||
const double scanMs = std::chrono::duration<double, std::milli>(
|
||||
std::chrono::steady_clock::now() - scanStart).count();
|
||||
return [this, scanGen, ok, verified, pipeline, verifiedZat, scanMs]() {
|
||||
if (scanGen != chat_session_generation_) return; // wallet switched — drop (reset cleared the flag)
|
||||
chat_note_scan_in_flight_ = false;
|
||||
if (scanMs > 0.0) chat_note_scan_ms_ = scanMs; // feed the adaptive back-off
|
||||
if (!ok) return; // z_listunspent unavailable — leave caches as-is
|
||||
chat_note_model_seen_ = true;
|
||||
chat_verified_note_budget_ = verified;
|
||||
@@ -3580,6 +3665,36 @@ void App::refreshChatNoteBudgetNode() {
|
||||
});
|
||||
}
|
||||
|
||||
// #3: derive the chat send-budget from an already-collected z_listunspent (refreshAddressData's scan),
|
||||
// so we don't issue a duplicate z_listunspent. Runs on the main thread; the notes are pre-parsed to the
|
||||
// fields we need (amount / locked / TRUE confirmations), so there's no RPC and no worker hop. Mirrors the
|
||||
// apply in refreshChatNoteBudgetNode and stamps chat_note_scan_last_ so the dedicated scan backs off.
|
||||
void App::updateChatNoteBudgetFromUnspent(
|
||||
const std::vector<services::NetworkRefreshService::UnspentNoteLite>& unspentNotes) {
|
||||
if (lite_wallet_ || !chat::hushChatFeatureEnabledAtBuild() || !chat_service_.hasIdentity()) return;
|
||||
const int confsNeeded = chatConfsRequired();
|
||||
const std::uint64_t minVal = chatDrgxToZat(kChatMinFeeDrgx);
|
||||
int verified = 0, pipeline = 0;
|
||||
std::uint64_t verifiedZat = 0;
|
||||
for (const auto& nz : unspentNotes) {
|
||||
if (nz.locked) continue; // tied up by an in-flight send
|
||||
const std::uint64_t amtZat = chatDrgxToZat(nz.amount);
|
||||
if (amtZat < minVal) continue; // skip dust below a fee
|
||||
++pipeline; // unspent self-note (verified or maturing)
|
||||
if (nz.confirmations >= confsNeeded) { ++verified; verifiedZat += amtZat; }
|
||||
}
|
||||
chat_note_model_seen_ = true;
|
||||
chat_verified_note_budget_ = verified;
|
||||
chat_pipeline_note_count_ = pipeline;
|
||||
chat_verified_shielded_zat_ = verifiedZat;
|
||||
chat_note_scan_last_ = ImGui::GetTime(); // counts as a fresh scan → dedicated scan backs off
|
||||
if (chat_split_outstanding_ &&
|
||||
(chat_pipeline_note_count_ >= kChatRefillTrigger ||
|
||||
ImGui::GetTime() - chat_split_submitted_at_ > kChatSplitWatchdogSecs)) {
|
||||
chat_split_outstanding_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void App::enqueueChatSend(LiteOpKind kind, const chat::OutgoingChatMemos& memos, const std::string& echoLocalId) {
|
||||
QueuedChatOp op;
|
||||
op.kind = kind;
|
||||
@@ -3821,15 +3936,28 @@ void App::fastScanChatMemos()
|
||||
if (!chat::hushChatFeatureEnabledAtBuild() || !chat_service_.hasIdentity()) return;
|
||||
if (!state_.connected || !rpc_ || !worker_) return;
|
||||
if (chat_fast_scan_in_flight_) return; // don't stack RPCs if a previous scan is still running
|
||||
// z_listreceivedbyaddress is O(mapWallet) (the daemon iterates every wtx even when filtered to one
|
||||
// address) — seconds on a large wallet — so this 0-conf poll must NOT run while the node is catching
|
||||
// up, and even when synced it backs off to ~10% wall-clock keyed on its own cost. The block-tip
|
||||
// harvest still ingests every message on confirmation; this only trades away mempool-speed delivery.
|
||||
if (effectivelySyncing()) return;
|
||||
// Mempool-speed 0-conf delivery is only worth its heavy cost while the user is watching chat. Off the
|
||||
// Chat tab, incoming messages still arrive via the confirmed block-tip harvest (+ toast) within ~1 block.
|
||||
if (current_page_ != ui::NavPage::Chat) return;
|
||||
const double nowT = ImGui::GetTime();
|
||||
if (chat_fast_scan_ms_ > 0.0 &&
|
||||
nowT - chat_fast_scan_last_ < (chat_fast_scan_ms_ / 1000.0) / 0.10) return;
|
||||
const std::string addr = chatReplyZaddr();
|
||||
if (addr.empty()) return;
|
||||
|
||||
chat_fast_scan_last_ = nowT;
|
||||
chat_fast_scan_in_flight_ = true;
|
||||
const int scanGen = chat_session_generation_; // guard: drop the result if the wallet switches/locks
|
||||
worker_->post([this, addr, scanGen]() -> rpc::RPCWorker::MainCb {
|
||||
std::vector<chat::HushChatTransactionMetadata> metadata;
|
||||
int rawMemoCount = 0; // received notes carrying a memo at the reply addr (0-conf visibility signal)
|
||||
std::string scanError;
|
||||
const auto scanStart = std::chrono::steady_clock::now();
|
||||
try {
|
||||
rpc::RPCClient::TraceScope trace("HushChat / 0-conf fast scan");
|
||||
nlohmann::json received = rpc_->call("z_listreceivedbyaddress", {addr, 0}); // 0 = include mempool
|
||||
@@ -3860,12 +3988,15 @@ void App::fastScanChatMemos()
|
||||
}
|
||||
} catch (const std::exception& e) { scanError = e.what(); }
|
||||
const int parsedCount = static_cast<int>(metadata.size());
|
||||
return [this, scanGen, metadata = std::move(metadata), rawMemoCount, parsedCount, scanError]() mutable {
|
||||
const double scanMs = std::chrono::duration<double, std::milli>(
|
||||
std::chrono::steady_clock::now() - scanStart).count();
|
||||
return [this, scanGen, metadata = std::move(metadata), rawMemoCount, parsedCount, scanError, scanMs]() mutable {
|
||||
// The wallet was switched/locked between post and now — this metadata belongs to the previous
|
||||
// session. resetChatSession already reset the in-flight flag, so just drop; clearing it here
|
||||
// would clobber a new session's own in-flight scan (mirrors the broadcast/identity guards).
|
||||
if (scanGen != chat_session_generation_) return;
|
||||
chat_fast_scan_in_flight_ = false;
|
||||
if (scanMs > 0.0) chat_fast_scan_ms_ = scanMs; // feed the adaptive back-off
|
||||
// Diagnostic (console App/chat channel): log when the memo-note count at the reply address
|
||||
// CHANGES, so a stable mempool doesn't spam every 2.5s. Reveals whether inbound 0-conf messages
|
||||
// are reaching us at all, and how many parse as chat vs are unrelated memos.
|
||||
@@ -4831,7 +4962,7 @@ void App::beginAdoptSeedWallet()
|
||||
|
||||
// 3. Rescan on next start (only if the swap happened) and bring the daemon back up —
|
||||
// unless we're quitting, in which case don't resurrect it.
|
||||
if (swapDone && daemon_controller_) daemon_controller_->setRescanOnNextStart(true);
|
||||
if (swapDone && daemon_controller_) { daemon_controller_->setRescanOnNextStart(true); user_initiated_rescan_ = true; }
|
||||
if (!shutting_down_) {
|
||||
// We stopped the daemon ourselves (port_free) — clear the adopted-external latch so the
|
||||
// relaunched process is treated as owned (stop/isRunning/exit behave normally afterward).
|
||||
@@ -5130,7 +5261,7 @@ void App::rebuildWalletDatabase()
|
||||
fs::rename(datadir + "/database", datadir + "/database.prerebuild-" + std::string(ts) + ".bak", e2);
|
||||
for (const auto& e : fs::directory_iterator(datadir, e2))
|
||||
if (e.path().filename().string().rfind("__db.", 0) == 0) { std::error_code e3; fs::remove(e.path(), e3); }
|
||||
if (daemon_controller_) daemon_controller_->setRescanOnNextStart(true);
|
||||
if (daemon_controller_) { daemon_controller_->setRescanOnNextStart(true); user_initiated_rescan_ = true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5558,6 +5689,7 @@ void App::runtimeRescan(int startHeight)
|
||||
runtime_rescan_active_ = true;
|
||||
state_.sync.rescanning = true;
|
||||
rescan_confirmed_active_ = true;
|
||||
user_initiated_rescan_ = true; // user clicked Rescan → completion should toast
|
||||
state_.sync.rescan_progress = 0.0f;
|
||||
state_.sync.rescan_status = "Rescanning from block " + std::to_string(startHeight) + "...";
|
||||
transactions_dirty_ = true;
|
||||
|
||||
+44
-45
@@ -300,14 +300,14 @@ void App::restartDaemonAfterEncryption(const char* taskName, bool announceRestar
|
||||
});
|
||||
} else {
|
||||
ui::Notifications::instance().warning(
|
||||
"Please restart your daemon for encryption to take effect.");
|
||||
TR("sec_restart_daemon_for_encryption"));
|
||||
}
|
||||
}
|
||||
|
||||
void App::encryptWalletWithPassphrase(const std::string& passphrase) {
|
||||
if (!rpc_ || !rpc_->isConnected()) return;
|
||||
encrypt_in_progress_ = true;
|
||||
encrypt_status_ = "Encrypting wallet...";
|
||||
encrypt_status_ = TR("sec_encrypting_wallet");
|
||||
|
||||
if (worker_) {
|
||||
worker_->post([this, passphrase]() mutable -> rpc::RPCWorker::MainCb {
|
||||
@@ -317,7 +317,7 @@ void App::encryptWalletWithPassphrase(const std::string& passphrase) {
|
||||
if (result.encrypted) {
|
||||
return [this]() {
|
||||
encrypt_in_progress_ = false;
|
||||
encrypt_status_ = "Wallet encrypted. Restarting daemon...";
|
||||
encrypt_status_ = TR("sec_wallet_encrypted_restarting_daemon");
|
||||
DEBUG_LOGF("[App] Wallet encrypted — restarting daemon\n");
|
||||
|
||||
// Immediately update local encryption state so the
|
||||
@@ -336,7 +336,7 @@ void App::encryptWalletWithPassphrase(const std::string& passphrase) {
|
||||
}
|
||||
|
||||
ui::Notifications::instance().info(
|
||||
"Wallet encrypted successfully", 5.0f);
|
||||
TR("sec_wallet_encrypted_successfully"), 5.0f);
|
||||
|
||||
// The daemon shuts itself down after encryptwallet.
|
||||
// Update connection_status_ so the loading overlay
|
||||
@@ -348,11 +348,11 @@ void App::encryptWalletWithPassphrase(const std::string& passphrase) {
|
||||
std::string err = result.error;
|
||||
return [this, err]() {
|
||||
encrypt_in_progress_ = false;
|
||||
encrypt_status_ = "Encryption failed: " + err;
|
||||
encrypt_status_ = std::string(TR("sec_encryption_failed_prefix")) + err;
|
||||
DEBUG_LOGF("[App] encryptwallet failed: %s\n", err.c_str());
|
||||
|
||||
ui::Notifications::instance().error(
|
||||
"Encryption failed: " + err);
|
||||
std::string(TR("sec_encryption_failed_prefix")) + err);
|
||||
|
||||
// Return to passphrase entry on failure
|
||||
if (show_encrypt_dialog_ &&
|
||||
@@ -393,7 +393,7 @@ void App::processDeferredEncryption() {
|
||||
std::string pin = std::move(deferredEncryption.pin);
|
||||
|
||||
encrypt_in_progress_ = true;
|
||||
encrypt_status_ = "Encrypting wallet...";
|
||||
encrypt_status_ = TR("sec_encrypting_wallet");
|
||||
|
||||
if (worker_) {
|
||||
worker_->post([this, request = services::WalletSecurityController::DeferredEncryptionSnapshot{std::move(passphrase), std::move(pin)}]() mutable -> rpc::RPCWorker::MainCb {
|
||||
@@ -413,13 +413,13 @@ void App::processDeferredEncryption() {
|
||||
if (result.pinStored) {
|
||||
settings_->setPinEnabled(true);
|
||||
settings_->save();
|
||||
ui::Notifications::instance().info("Wallet encrypted & PIN set", 5.0f);
|
||||
ui::Notifications::instance().info(TR("sec_wallet_encrypted_and_pin_set"), 5.0f);
|
||||
} else {
|
||||
ui::Notifications::instance().warning(
|
||||
"Wallet encrypted but PIN vault failed");
|
||||
TR("sec_wallet_encrypted_but_pin_vault_failed"));
|
||||
}
|
||||
} else {
|
||||
ui::Notifications::instance().info("Wallet encrypted successfully", 5.0f);
|
||||
ui::Notifications::instance().info(TR("sec_wallet_encrypted_successfully"), 5.0f);
|
||||
}
|
||||
|
||||
wallet_security_.clearDeferredEncryption();
|
||||
@@ -432,9 +432,9 @@ void App::processDeferredEncryption() {
|
||||
std::string err = result.error;
|
||||
return [this, err]() {
|
||||
encrypt_in_progress_ = false;
|
||||
encrypt_status_ = "Encryption failed: " + err;
|
||||
encrypt_status_ = std::string(TR("sec_encryption_failed_prefix")) + err;
|
||||
DEBUG_LOGF("[App] Deferred encryptwallet failed: %s\n", err.c_str());
|
||||
ui::Notifications::instance().error("Encryption failed: " + err);
|
||||
ui::Notifications::instance().error(std::string(TR("sec_encryption_failed_prefix")) + err);
|
||||
wallet_security_.clearDeferredEncryption();
|
||||
};
|
||||
}
|
||||
@@ -531,7 +531,7 @@ void App::lockWallet() {
|
||||
if (!lock_failure_warned_) {
|
||||
lock_failure_warned_ = true;
|
||||
ui::Notifications::instance().warning(
|
||||
"Couldn't lock the wallet — it is still unlocked. Check the daemon connection.", 12.0f);
|
||||
TR("sec_couldnt_lock_wallet"), 12.0f);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -541,7 +541,7 @@ void App::lockWallet() {
|
||||
void App::changePassphrase(const std::string& oldPass, const std::string& newPass) {
|
||||
if (!rpc_ || !rpc_->isConnected() || !worker_) return;
|
||||
encrypt_in_progress_ = true;
|
||||
encrypt_status_ = "Changing passphrase...";
|
||||
encrypt_status_ = TR("sec_changing_passphrase");
|
||||
|
||||
auto* w = (fast_worker_ && fast_worker_->isRunning()) ? fast_worker_.get() : worker_.get();
|
||||
auto* r = (fast_rpc_ && fast_rpc_->isConnected()) ? fast_rpc_.get() : rpc_.get();
|
||||
@@ -574,9 +574,9 @@ void App::changePassphrase(const std::string& oldPass, const std::string& newPas
|
||||
memset(change_confirm_buf_, 0, sizeof(change_confirm_buf_));
|
||||
unlockTransactionHistoryCacheWithPassphrase(newPass);
|
||||
storeTransactionHistoryCacheIfAvailable();
|
||||
ui::Notifications::instance().info("Passphrase changed successfully");
|
||||
ui::Notifications::instance().info(TR("sec_passphrase_changed_successfully"));
|
||||
} else {
|
||||
encrypt_status_ = "Failed: " + err_msg;
|
||||
encrypt_status_ = std::string(TR("sec_failed_prefix")) + err_msg;
|
||||
}
|
||||
util::SecureVault::secureZero(newPass.data(), newPass.size());
|
||||
};
|
||||
@@ -637,8 +637,7 @@ void App::refreshWalletEncryptionState() {
|
||||
!encryption_incomplete_warned_) {
|
||||
encryption_incomplete_warned_ = true;
|
||||
ui::Notifications::instance().warning(
|
||||
"Wallet encryption did not complete — your wallet is NOT encrypted. "
|
||||
"Open Settings to finish encrypting it.", 30.0f);
|
||||
TR("sec_encryption_did_not_complete"), 30.0f);
|
||||
}
|
||||
if (state_.transactions.empty()) {
|
||||
loadTransactionHistoryCacheIfAvailable();
|
||||
@@ -951,7 +950,7 @@ void App::renderLockScreen() {
|
||||
ImU32 textCol = ui::material::OnSurface();
|
||||
|
||||
{
|
||||
const char* title = "Wallet Locked";
|
||||
const char* title = TR("sec_wallet_locked_title");
|
||||
ImVec2 ts = titleFont->CalcTextSizeA(titleFont->LegacySize, FLT_MAX, 0, title);
|
||||
dl->AddText(titleFont, titleFont->LegacySize,
|
||||
ImVec2(cardX + (cardW - ts.x) * 0.5f, cy), textCol, title);
|
||||
@@ -964,7 +963,7 @@ void App::renderLockScreen() {
|
||||
if (lock_lockout_timer_ < 0) lock_lockout_timer_ = 0;
|
||||
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg), "Too many attempts. Wait %.0f seconds...", lock_lockout_timer_);
|
||||
snprintf(msg, sizeof(msg), TR("sec_too_many_attempts_wait"), lock_lockout_timer_);
|
||||
ImVec2 ms = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, 0, msg);
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cardX + (cardW - ms.x) * 0.5f, cy), ui::material::Warning(), msg);
|
||||
@@ -977,10 +976,10 @@ void App::renderLockScreen() {
|
||||
// Mode toggle (PIN / Passphrase) — only show if PIN vault exists
|
||||
if (hasPinVault) {
|
||||
const char* modeIcon = lock_use_pin_ ? ICON_MD_DIALPAD : ICON_MD_PASSWORD;
|
||||
const char* modeText = lock_use_pin_ ? " PIN" : " Passphrase";
|
||||
const char* modeText = lock_use_pin_ ? " PIN" : TR("sec_mode_passphrase");
|
||||
const char* switchLabel = lock_use_pin_
|
||||
? "Use passphrase instead"
|
||||
: "Use PIN instead";
|
||||
? TR("sec_use_passphrase_instead")
|
||||
: TR("sec_use_pin_instead");
|
||||
|
||||
// Current mode indicator — icon with icon font, text with caption font
|
||||
ImFont* iconFont = ui::material::Type().iconSmall();
|
||||
@@ -1082,7 +1081,7 @@ void App::renderLockScreen() {
|
||||
if (lock_unlock_in_progress_) {
|
||||
// Animated spinner dots
|
||||
char msg[64];
|
||||
snprintf(msg, sizeof(msg), "Unlocking%s", ui::material::LoadingDots());
|
||||
snprintf(msg, sizeof(msg), TR("sec_unlocking_fmt"), ui::material::LoadingDots());
|
||||
ImVec2 ms = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, 0, msg);
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cardX + (cardW - ms.x) * 0.5f, cy),
|
||||
@@ -1106,7 +1105,7 @@ void App::renderLockScreen() {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
ImGui::BeginDisabled(!canSubmit);
|
||||
bool btnClicked = ui::material::TactileButton("Unlock", ImVec2(unlockW, unlockH));
|
||||
bool btnClicked = ui::material::TactileButton(TR("sec_unlock_button"), ImVec2(unlockW, unlockH));
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor(3);
|
||||
@@ -1160,7 +1159,7 @@ void App::renderLockScreen() {
|
||||
r->call("walletpassphrase", {passphrase, timeout});
|
||||
rpcOk = true;
|
||||
} else {
|
||||
rpcErr = "Not connected to daemon";
|
||||
rpcErr = TR("sec_not_connected_to_daemon");
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
rpcErr = e.what();
|
||||
@@ -1176,7 +1175,7 @@ void App::renderLockScreen() {
|
||||
// so route through applyUnlockFailure so the lockout curve applies
|
||||
// (this path previously bumped the counter but skipped the lockout math).
|
||||
return [this, rpcErr, passphrase = std::move(passphrase)]() mutable {
|
||||
applyUnlockFailure("Unlock failed: " + rpcErr);
|
||||
applyUnlockFailure(std::string(TR("sec_unlock_failed_prefix")) + rpcErr);
|
||||
util::SecureVault::secureZero(passphrase.data(), passphrase.size());
|
||||
};
|
||||
}
|
||||
@@ -1579,7 +1578,7 @@ void App::renderDecryptWalletDialog() {
|
||||
if (!passphrase.empty()) sodium_memzero(&passphrase[0], passphrase.size());
|
||||
if (!unlock.ok) {
|
||||
return [this]() {
|
||||
wallet_security_workflow_.failEntry("Incorrect passphrase");
|
||||
wallet_security_workflow_.failEntry(TR("sec_incorrect_passphrase_decrypt"));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1695,7 +1694,7 @@ void App::renderDecryptWalletDialog() {
|
||||
});
|
||||
|
||||
ui::Notifications::instance().info(
|
||||
"Importing keys & rescanning blockchain — wallet is usable while this runs",
|
||||
TR("sec_importing_keys_rescanning"),
|
||||
8.0f);
|
||||
};
|
||||
});
|
||||
@@ -1714,7 +1713,7 @@ void App::renderDecryptWalletDialog() {
|
||||
wallet_security_workflow_.finishImport();
|
||||
ui::Notifications::instance().error(
|
||||
err +
|
||||
"\nEncrypted backup: wallet.dat.encrypted.bak",
|
||||
TR("sec_encrypted_backup_suffix"),
|
||||
12.0f);
|
||||
};
|
||||
});
|
||||
@@ -1740,7 +1739,7 @@ void App::renderDecryptWalletDialog() {
|
||||
refreshPeerInfo();
|
||||
|
||||
ui::Notifications::instance().success(
|
||||
"Wallet decrypted successfully! All keys imported.",
|
||||
TR("sec_wallet_decrypted_all_keys_imported"),
|
||||
8.0f);
|
||||
DEBUG_LOGF("[App] Wallet decrypted successfully\n");
|
||||
};
|
||||
@@ -1862,7 +1861,7 @@ void App::renderDecryptWalletDialog() {
|
||||
int tMins = (int)(totalElapsed / 60);
|
||||
int tSecs = (int)(totalElapsed % 60);
|
||||
ImGui::Spacing();
|
||||
ImGui::TextDisabled("Total elapsed: %dm %02ds", tMins, tSecs);
|
||||
ImGui::TextDisabled(TR("sec_total_elapsed_fmt"), tMins, tSecs);
|
||||
}
|
||||
|
||||
// ---- Phase 2: Success ----
|
||||
@@ -1965,7 +1964,7 @@ void App::renderPinDialogs() {
|
||||
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
||||
if (ui::material::TactileButton(TR("settings_set_pin"), ImVec2(btnW, 40))) {
|
||||
pin_in_progress_ = true;
|
||||
pin_status_ = "Verifying passphrase...";
|
||||
pin_status_ = TR("sec_verifying_passphrase");
|
||||
|
||||
// Verify passphrase + store vault on worker thread to avoid
|
||||
// blocking the UI with Argon2id key derivation.
|
||||
@@ -1985,7 +1984,7 @@ void App::renderPinDialogs() {
|
||||
if (!passphrase.empty()) util::SecureVault::secureZero(&passphrase[0], passphrase.size());
|
||||
if (!pin.empty()) util::SecureVault::secureZero(&pin[0], pin.size());
|
||||
return [this]() {
|
||||
pin_status_ = "Incorrect passphrase";
|
||||
pin_status_ = TR("sec_incorrect_passphrase_pin_setup");
|
||||
pin_in_progress_ = false;
|
||||
};
|
||||
}
|
||||
@@ -2009,15 +2008,15 @@ void App::renderPinDialogs() {
|
||||
pin_status_.clear();
|
||||
pin_in_progress_ = false;
|
||||
show_pin_setup_ = false;
|
||||
ui::Notifications::instance().info("PIN set successfully");
|
||||
ui::Notifications::instance().info(TR("sec_pin_set_successfully"));
|
||||
} else {
|
||||
pin_status_ = "Failed to create vault";
|
||||
pin_status_ = TR("sec_failed_to_create_vault");
|
||||
pin_in_progress_ = false;
|
||||
}
|
||||
};
|
||||
});
|
||||
} else {
|
||||
pin_status_ = "Not connected to daemon";
|
||||
pin_status_ = TR("sec_not_connected_to_daemon_pin");
|
||||
pin_in_progress_ = false;
|
||||
}
|
||||
}
|
||||
@@ -2082,7 +2081,7 @@ void App::renderPinDialogs() {
|
||||
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
||||
if (ui::material::TactileButton(TR("settings_change_pin"), ImVec2(-1, 40))) {
|
||||
pin_in_progress_ = true;
|
||||
pin_status_ = "Changing PIN...";
|
||||
pin_status_ = TR("sec_changing_pin");
|
||||
std::string oldPin(pin_old_buf_);
|
||||
std::string newPinCopy = newPin;
|
||||
memset(pin_old_buf_, 0, sizeof(pin_old_buf_));
|
||||
@@ -2098,15 +2097,15 @@ void App::renderPinDialogs() {
|
||||
pin_status_.clear();
|
||||
pin_in_progress_ = false;
|
||||
show_pin_change_ = false;
|
||||
ui::Notifications::instance().info("PIN changed successfully");
|
||||
ui::Notifications::instance().info(TR("sec_pin_changed_successfully"));
|
||||
} else {
|
||||
pin_status_ = "Incorrect current PIN";
|
||||
pin_status_ = TR("sec_incorrect_current_pin");
|
||||
pin_in_progress_ = false;
|
||||
}
|
||||
};
|
||||
});
|
||||
} else {
|
||||
pin_status_ = "Internal error";
|
||||
pin_status_ = TR("sec_internal_error_change_pin");
|
||||
pin_in_progress_ = false;
|
||||
}
|
||||
}
|
||||
@@ -2147,7 +2146,7 @@ void App::renderPinDialogs() {
|
||||
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
||||
if (ui::material::TactileButton(TR("settings_remove_pin"), ImVec2(-1, 40))) {
|
||||
pin_in_progress_ = true;
|
||||
pin_status_ = "Verifying PIN...";
|
||||
pin_status_ = TR("sec_verifying_pin");
|
||||
std::string oldPin(pin_old_buf_);
|
||||
memset(pin_old_buf_, 0, sizeof(pin_old_buf_));
|
||||
|
||||
@@ -2167,15 +2166,15 @@ void App::renderPinDialogs() {
|
||||
pin_status_.clear();
|
||||
pin_in_progress_ = false;
|
||||
show_pin_remove_ = false;
|
||||
ui::Notifications::instance().info("PIN removed");
|
||||
ui::Notifications::instance().info(TR("sec_pin_removed"));
|
||||
} else {
|
||||
pin_status_ = "Incorrect PIN";
|
||||
pin_status_ = TR("sec_incorrect_pin_remove");
|
||||
pin_in_progress_ = false;
|
||||
}
|
||||
};
|
||||
});
|
||||
} else {
|
||||
pin_status_ = "Internal error";
|
||||
pin_status_ = TR("sec_internal_error_remove_pin");
|
||||
pin_in_progress_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,6 +310,8 @@ void App::buildSweepCatalog()
|
||||
[](App& a) { ui::BootstrapDownloadDialog::show(&a); }, [](App&) { ui::BootstrapDownloadDialog::hide(); });
|
||||
add("modal-backup", ui::NavPage::Overview,
|
||||
[](App& a) { a.show_backup_ = true; }, [](App& a) { a.show_backup_ = false; a.backup_status_.clear(); });
|
||||
add("modal-faq", ui::NavPage::Overview,
|
||||
[](App& a) { a.show_faq_ = true; }, [](App& a) { a.show_faq_ = false; });
|
||||
// Encrypt-wallet dialog — the redesigned passphrase-entry phase (never fires the async encrypt).
|
||||
add("modal-encrypt", ui::NavPage::Settings,
|
||||
[](App& a) { a.encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry; a.show_encrypt_dialog_ = true; },
|
||||
@@ -708,9 +710,22 @@ void App::startSweepImpl(bool full)
|
||||
if (sweep_current_theme_only_)
|
||||
sweep_skins_.assign(1, ui::schema::SkinManager::instance().activeSkinId());
|
||||
|
||||
// DEV/TEST hook (dormant unless the env is set): DRAGONX_SWEEP_ONLY="send,receive" restricts the
|
||||
// sweep to the named surfaces and the dark skin, so a slow large-window run captures just the tab
|
||||
// under review instead of all surfaces x every skin.
|
||||
const char* sweepOnly = std::getenv("DRAGONX_SWEEP_ONLY");
|
||||
std::string sweepOnlyStr = sweepOnly ? sweepOnly : "";
|
||||
if (!sweepOnlyStr.empty()) sweep_skins_.assign(1, std::string("dark"));
|
||||
|
||||
sweep_full_ = full;
|
||||
if (full) { capture_mode_ = true; installDemoWalletData(); }
|
||||
buildSweepCatalog();
|
||||
if (!sweepOnlyStr.empty()) {
|
||||
std::vector<SweepTarget> keep;
|
||||
for (const auto& t : sweep_targets_)
|
||||
if (sweepOnlyStr.find(t.name) != std::string::npos) keep.push_back(t);
|
||||
sweep_targets_.swap(keep);
|
||||
}
|
||||
if (sweep_targets_.empty()) { if (full) { clearDemoWalletData(); capture_mode_ = false; sweep_full_ = false; } return; }
|
||||
|
||||
sweep_dir_ = full ? screenshotFullDir() : screenshotDir();
|
||||
|
||||
@@ -89,6 +89,7 @@ bool ChatDatabase::unlockWithSecret(const std::string& secret)
|
||||
lock();
|
||||
return false;
|
||||
}
|
||||
loadTombstones();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -97,11 +98,13 @@ void ChatDatabase::lock()
|
||||
sodium_memzero(key_.data(), key_.size());
|
||||
key_ready_ = false;
|
||||
wallet_tag_.clear();
|
||||
tombstones_.clear();
|
||||
}
|
||||
|
||||
bool ChatDatabase::append(const ChatMessage& message)
|
||||
{
|
||||
if (!key_ready_ || !ensureOpen()) return false;
|
||||
if (isTombstoned(message)) return false; // locally deleted — don't re-persist on a chain re-scan
|
||||
|
||||
std::vector<unsigned char> nonce;
|
||||
std::vector<unsigned char> cipher;
|
||||
@@ -193,13 +196,79 @@ std::vector<ChatMessage> ChatDatabase::load()
|
||||
void ChatDatabase::clearWallet()
|
||||
{
|
||||
if (wallet_tag_.empty() || !ensureOpen()) return;
|
||||
for (const char* sql : {"DELETE FROM chat_messages WHERE wallet_tag = ?",
|
||||
"DELETE FROM chat_deleted WHERE wallet_tag = ?"}) {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) continue;
|
||||
sqlite3_bind_text(stmt, 1, wallet_tag_.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
tombstones_.clear();
|
||||
}
|
||||
|
||||
bool ChatDatabase::deleteMessages(const std::vector<ChatMessage>& messages, bool tombstone)
|
||||
{
|
||||
if (!key_ready_ || !ensureOpen()) return false;
|
||||
if (messages.empty()) return true;
|
||||
|
||||
if (!exec("BEGIN")) return false;
|
||||
bool ok = true;
|
||||
for (const auto& m : messages) {
|
||||
const std::string dedup = dedupHash(m.txid, m.payload_position);
|
||||
|
||||
sqlite3_stmt* del = nullptr;
|
||||
if (sqlite3_prepare_v2(db_, "DELETE FROM chat_messages WHERE wallet_tag = ? AND dedup_hash = ?",
|
||||
-1, &del, nullptr) == SQLITE_OK) {
|
||||
sqlite3_bind_text(del, 1, wallet_tag_.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(del, 2, dedup.c_str(), -1, SQLITE_TRANSIENT);
|
||||
if (sqlite3_step(del) != SQLITE_DONE) ok = false;
|
||||
sqlite3_finalize(del);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (tombstone) {
|
||||
sqlite3_stmt* ins = nullptr;
|
||||
if (sqlite3_prepare_v2(db_,
|
||||
"INSERT OR IGNORE INTO chat_deleted (wallet_tag, dedup_hash) VALUES (?, ?)",
|
||||
-1, &ins, nullptr) == SQLITE_OK) {
|
||||
sqlite3_bind_text(ins, 1, wallet_tag_.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(ins, 2, dedup.c_str(), -1, SQLITE_TRANSIENT);
|
||||
if (sqlite3_step(ins) != SQLITE_DONE) ok = false;
|
||||
sqlite3_finalize(ins);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!exec(ok ? "COMMIT" : "ROLLBACK")) ok = false;
|
||||
// Only reflect the tombstones in the in-memory cache once they are durably committed.
|
||||
if (ok && tombstone)
|
||||
for (const auto& m : messages) tombstones_.insert(dedupHash(m.txid, m.payload_position));
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ChatDatabase::isTombstoned(const ChatMessage& message) const
|
||||
{
|
||||
if (!key_ready_ || tombstones_.empty()) return false;
|
||||
return tombstones_.count(dedupHash(message.txid, message.payload_position)) > 0;
|
||||
}
|
||||
|
||||
void ChatDatabase::loadTombstones()
|
||||
{
|
||||
tombstones_.clear();
|
||||
if (!key_ready_ || !ensureOpen()) return;
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
if (sqlite3_prepare_v2(db_, "DELETE FROM chat_messages WHERE wallet_tag = ?", -1, &stmt, nullptr)
|
||||
!= SQLITE_OK) {
|
||||
if (sqlite3_prepare_v2(db_, "SELECT dedup_hash FROM chat_deleted WHERE wallet_tag = ?",
|
||||
-1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return;
|
||||
}
|
||||
sqlite3_bind_text(stmt, 1, wallet_tag_.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const auto* h = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
|
||||
if (h) tombstones_.insert(h);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
@@ -260,11 +329,18 @@ bool ChatDatabase::exec(const char* sql)
|
||||
|
||||
bool ChatDatabase::createSchema()
|
||||
{
|
||||
return exec("CREATE TABLE IF NOT EXISTS chat_messages ("
|
||||
if (!exec("CREATE TABLE IF NOT EXISTS chat_messages ("
|
||||
"wallet_tag TEXT NOT NULL, "
|
||||
"dedup_hash TEXT NOT NULL, "
|
||||
"nonce BLOB NOT NULL, "
|
||||
"payload BLOB NOT NULL, "
|
||||
"PRIMARY KEY (wallet_tag, dedup_hash))"))
|
||||
return false;
|
||||
// Tombstones for locally-deleted messages (dedup_hash only — the same keyed, non-revealing hash the
|
||||
// message rows use). A chain re-scan checks this so a deleted message never re-imports.
|
||||
return exec("CREATE TABLE IF NOT EXISTS chat_deleted ("
|
||||
"wallet_tag TEXT NOT NULL, "
|
||||
"dedup_hash TEXT NOT NULL, "
|
||||
"nonce BLOB NOT NULL, "
|
||||
"payload BLOB NOT NULL, "
|
||||
"PRIMARY KEY (wallet_tag, dedup_hash))");
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
struct sqlite3;
|
||||
@@ -54,8 +55,20 @@ public:
|
||||
|
||||
void clearWallet(); // delete the unlocked wallet's rows
|
||||
|
||||
// Per-conversation local delete. Removes the given messages' rows; when `tombstone` is true it also
|
||||
// records their (txid,position) dedup keys so a chain re-scan never re-imports them — this backs the
|
||||
// "delete, but a NEW message revives the thread" path. With `tombstone` false the rows are simply
|
||||
// removed (used by "delete & block", where a settings-level cid block suppresses re-import until the
|
||||
// user unblocks, at which point the history re-imports from chain). Atomic; no-op while locked.
|
||||
bool deleteMessages(const std::vector<ChatMessage>& messages, bool tombstone);
|
||||
|
||||
// True if this message's (txid,position) was locally deleted with a tombstone. Checked against an
|
||||
// in-memory cache loaded on unlock — O(1), no SQL. False while locked.
|
||||
bool isTombstoned(const ChatMessage& message) const;
|
||||
|
||||
private:
|
||||
bool ensureOpen();
|
||||
void loadTombstones(); // populate tombstones_ from chat_deleted for the unlocked wallet
|
||||
bool exec(const char* sql);
|
||||
bool createSchema();
|
||||
std::string dedupHash(const std::string& txid, std::size_t position) const;
|
||||
@@ -74,6 +87,7 @@ private:
|
||||
std::array<unsigned char, 32> key_{}; // AEAD storage key (seed-derived)
|
||||
std::string wallet_tag_; // seed-derived row partition (a keyed hash, hex)
|
||||
bool key_ready_ = false;
|
||||
std::unordered_set<std::string> tombstones_; // dedup_hash cache of locally-deleted messages
|
||||
};
|
||||
|
||||
} // namespace dragonx::chat
|
||||
|
||||
@@ -28,6 +28,10 @@ int ChatService::ingest(const std::vector<HushChatTransactionMetadata>& metadata
|
||||
std::int64_t fallbackTimestamp,
|
||||
std::vector<std::string>* newIncomingCids) {
|
||||
if (!has_identity_) return 0;
|
||||
// Persistence is attached but not unlocked (e.g. the DB failed to open with the seed): we can't
|
||||
// consult tombstones, so ingesting now would resurface locally-deleted messages into the store.
|
||||
// Skip until the DB is usable — chat is degraded anyway without its store.
|
||||
if (db_ && !db_->hasKey()) return 0;
|
||||
|
||||
const std::string myPubKey = chatIdentityPublicKeyHex(identity_);
|
||||
|
||||
@@ -62,6 +66,13 @@ int ChatService::ingest(const std::vector<HushChatTransactionMetadata>& metadata
|
||||
}
|
||||
message.payload_position = meta.payload_position;
|
||||
|
||||
// Suppress locally-removed conversations before the (relatively costly) decrypt. A blocked cid
|
||||
// is dropped outright (old + future messages) until unblocked; a tombstoned (txid,position) was
|
||||
// deleted with "revive on new message", so only that exact message is skipped — a new message
|
||||
// in the same conversation has a different txid and flows through normally.
|
||||
if (blocked_pred_ && blocked_pred_(message.conversation_id)) continue;
|
||||
if (db_ && db_->isTombstoned(message)) continue;
|
||||
|
||||
if (meta.type == HushChatHeaderType::ContactRequest) {
|
||||
message.kind = ChatMessageKind::ContactRequest;
|
||||
message.body = meta.payload_memo; // plaintext request text
|
||||
@@ -92,10 +103,28 @@ int ChatService::ingest(const std::vector<HushChatTransactionMetadata>& metadata
|
||||
void ChatService::loadFromDatabase() {
|
||||
if (!db_) return;
|
||||
for (const auto& message : db_->load()) {
|
||||
// Never surface a blocked conversation, even if a prior "delete & block" failed to remove its
|
||||
// rows (defense-in-depth): the ingest guard already drops live scans, this covers the reload path.
|
||||
if (blocked_pred_ && blocked_pred_(message.conversation_id)) continue;
|
||||
store_.append(message);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatService::deleteConversation(const std::string& conversationId, bool block) {
|
||||
// Delete the persisted rows FIRST and only mutate the in-memory view if that succeeds. Doing it the
|
||||
// other way round means a failed DB write (disk full / locked) would empty the store while the rows
|
||||
// survive — and on the next reload the conversation silently reappears with no tombstone.
|
||||
// Revive-on-new-message => tombstone the removed rows so a re-scan won't re-import them.
|
||||
// Block => remove the rows without a tombstone; the caller's blocked predicate suppresses re-import
|
||||
// until unblocked, at which point the conversation re-imports from chain.
|
||||
if (db_) {
|
||||
std::vector<ChatMessage> msgs = store_.conversation(conversationId); // snapshot (copy)
|
||||
if (!db_->deleteMessages(msgs, /*tombstone=*/!block)) return false;
|
||||
}
|
||||
store_.eraseConversation(conversationId);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ChatService::identityPublicKeyHex() const {
|
||||
if (!has_identity_) return {};
|
||||
return chatIdentityPublicKeyHex(identity_);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "chat_store.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -55,6 +56,19 @@ public:
|
||||
// the seed-derived key) into the in-memory store. No-op without an unlocked database.
|
||||
void loadFromDatabase();
|
||||
|
||||
// Set a predicate returning true for a BLOCKED conversation id. ingest() drops those messages
|
||||
// entirely (they are never stored or persisted) until the predicate stops returning true — this is
|
||||
// how "delete & block" suppresses old and future messages. Typically wired to Settings::isChatBlocked.
|
||||
void setBlockedPredicate(std::function<bool(const std::string&)> pred) { blocked_pred_ = std::move(pred); }
|
||||
|
||||
// Locally delete a conversation: remove its messages from the store and the database. When `block`
|
||||
// is false, the removed messages are tombstoned so a chain re-scan won't re-import them, but a
|
||||
// genuinely NEW message (new txid) revives the thread. When `block` is true, the rows are removed
|
||||
// WITHOUT a tombstone (the caller also records the cid as blocked via the predicate above); unblocking
|
||||
// later lets the conversation re-import from chain. Returns false (leaving the store untouched) if the
|
||||
// persisted rows couldn't be removed, so the caller can avoid a store/DB divergence.
|
||||
bool deleteConversation(const std::string& conversationId, bool block);
|
||||
|
||||
// --- Outgoing (compose) ---
|
||||
// My chat public key (hex), or "" without an identity — goes in an outgoing header's "p".
|
||||
std::string identityPublicKeyHex() const;
|
||||
@@ -90,6 +104,7 @@ private:
|
||||
bool has_identity_ = false;
|
||||
ChatStore store_;
|
||||
ChatDatabase* db_ = nullptr; // optional; not owned
|
||||
std::function<bool(const std::string&)> blocked_pred_; // true => cid is blocked (drop its messages)
|
||||
};
|
||||
|
||||
} // namespace dragonx::chat
|
||||
|
||||
@@ -13,6 +13,7 @@ std::string ChatStore::dedupKey(const ChatMessage& message) {
|
||||
bool ChatStore::append(const ChatMessage& message) {
|
||||
if (!seen_.insert(dedupKey(message)).second) return false;
|
||||
messages_.push_back(message);
|
||||
++revision_;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,12 +39,24 @@ const ChatMessage* ChatStore::updateDelivery(const std::string& txid, ChatDelive
|
||||
for (auto& message : messages_) {
|
||||
if (message.txid == txid) {
|
||||
message.delivery = delivery;
|
||||
++revision_;
|
||||
return &message;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ChatStore::countUnread(const std::function<bool(const std::string&)>& excluded,
|
||||
const std::function<std::int64_t(const std::string&)>& seenFor) const {
|
||||
int unread = 0;
|
||||
for (const auto& m : messages_) {
|
||||
if (m.direction != ChatDirection::Incoming) continue;
|
||||
if (excluded && excluded(m.conversation_id)) continue;
|
||||
if (m.timestamp > seenFor(m.conversation_id)) ++unread;
|
||||
}
|
||||
return unread;
|
||||
}
|
||||
|
||||
std::vector<std::string> ChatStore::conversationIds() const {
|
||||
std::vector<std::string> ids;
|
||||
std::unordered_set<std::string> seenIds;
|
||||
@@ -53,9 +66,27 @@ std::vector<std::string> ChatStore::conversationIds() const {
|
||||
return ids;
|
||||
}
|
||||
|
||||
std::vector<ChatMessage> ChatStore::eraseConversation(const std::string& conversationId) {
|
||||
std::vector<ChatMessage> removed;
|
||||
std::vector<ChatMessage> kept;
|
||||
kept.reserve(messages_.size());
|
||||
for (auto& m : messages_) {
|
||||
if (m.conversation_id == conversationId) {
|
||||
seen_.erase(dedupKey(m));
|
||||
removed.push_back(std::move(m));
|
||||
} else {
|
||||
kept.push_back(std::move(m));
|
||||
}
|
||||
}
|
||||
messages_.swap(kept);
|
||||
if (!removed.empty()) ++revision_;
|
||||
return removed;
|
||||
}
|
||||
|
||||
void ChatStore::clear() {
|
||||
messages_.clear();
|
||||
seen_.clear();
|
||||
++revision_;
|
||||
}
|
||||
|
||||
} // namespace dragonx::chat
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "chat_message.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
@@ -38,15 +40,32 @@ public:
|
||||
return out;
|
||||
}
|
||||
|
||||
// Remove every message in a conversation from the in-memory view and return the removed messages
|
||||
// (so the caller can delete/tombstone their persisted rows). Re-appends are prevented by the ingest
|
||||
// guard (blocked-cid predicate / DB tombstone), not here.
|
||||
std::vector<ChatMessage> eraseConversation(const std::string& conversationId);
|
||||
|
||||
std::size_t size() const { return messages_.size(); }
|
||||
bool empty() const { return messages_.empty(); }
|
||||
void clear();
|
||||
|
||||
// Monotonic counter bumped on every mutation (append / updateDelivery change / eraseConversation /
|
||||
// clear). Callers memoize expensive per-frame reads (conversation-list build, unread count) against it
|
||||
// so they only rebuild when the store actually changed.
|
||||
std::uint64_t revision() const { return revision_; }
|
||||
|
||||
// Count unread incoming messages in a SINGLE pass over the store: an incoming message counts when its
|
||||
// cid is not `excluded` and its timestamp is newer than `seenFor(cid)`. Avoids the per-conversation
|
||||
// copy+sort that conversation() does — the count doesn't need ordering.
|
||||
int countUnread(const std::function<bool(const std::string&)>& excluded,
|
||||
const std::function<std::int64_t(const std::string&)>& seenFor) const;
|
||||
|
||||
private:
|
||||
static std::string dedupKey(const ChatMessage& message);
|
||||
|
||||
std::vector<ChatMessage> messages_;
|
||||
std::unordered_set<std::string> seen_;
|
||||
std::uint64_t revision_ = 0;
|
||||
};
|
||||
|
||||
} // namespace dragonx::chat
|
||||
|
||||
@@ -108,6 +108,20 @@ void loadClamped(const json& j, const char* key, T& field, T lo, T hi)
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<Settings::LiteServerPreference> Settings::defaultLiteServers()
|
||||
{
|
||||
return {
|
||||
{"https://lite.dragonx.is", "DragonX Lite", true},
|
||||
{"https://lite1.dragonx.is", "DragonX Lite 1", true},
|
||||
{"https://lite2.dragonx.is", "DragonX Lite 2", true},
|
||||
{"https://lite3.dragonx.is", "DragonX Lite 3", true},
|
||||
{"https://lite4.dragonx.is", "DragonX Lite 4", true},
|
||||
{"https://lite5.dragonx.is", "DragonX Lite 5", true},
|
||||
{"https://lite6.dragonx.is", "DragonX Lite 6", true},
|
||||
{"https://lite7.dragonx.is", "DragonX Lite 7", true}
|
||||
};
|
||||
}
|
||||
|
||||
std::string Settings::getDefaultPath()
|
||||
{
|
||||
// Single per-platform, per-variant config dir (util::Platform::getConfigDir handles the
|
||||
@@ -157,6 +171,13 @@ bool Settings::load(const std::string& path)
|
||||
for (const auto& c : j["hidden_chat_cids"])
|
||||
if (c.is_string()) hidden_chat_cids_.push_back(c.get<std::string>());
|
||||
}
|
||||
if (j.contains("blocked_chat_convs") && j["blocked_chat_convs"].is_array()) {
|
||||
blocked_chat_convs_.clear();
|
||||
for (const auto& c : j["blocked_chat_convs"])
|
||||
if (c.is_object() && c.contains("cid") && c["cid"].is_string())
|
||||
blocked_chat_convs_.push_back({c["cid"].get<std::string>(),
|
||||
c.value("name", std::string())});
|
||||
}
|
||||
// Chat-tab customization (re-clamped through the setters so hand-edited JSON stays in range).
|
||||
loadScalar(j, "chat_emoji_color", chat_emoji_color_);
|
||||
loadScalar(j, "chat_poll_rate_sec", chat_poll_rate_sec_); setChatPollRateSec(chat_poll_rate_sec_);
|
||||
@@ -252,6 +273,8 @@ bool Settings::load(const std::string& path)
|
||||
loadScalar(j, "keep_daemon_running", keep_daemon_running_);
|
||||
loadScalar(j, "stop_external_daemon", stop_external_daemon_);
|
||||
loadScalar(j, "max_connections", max_connections_);
|
||||
loadScalar(j, "stratum_host_enabled", stratum_host_enabled_);
|
||||
loadScalar(j, "stratum_allowip", stratum_allowip_);
|
||||
if (j.contains("lite_wallet") && j["lite_wallet"].is_object()) {
|
||||
const auto& lite = j["lite_wallet"];
|
||||
if (lite.contains("server_selection_mode")) {
|
||||
@@ -298,6 +321,16 @@ bool Settings::load(const std::string& path)
|
||||
}
|
||||
lite_servers_.push_back(preference);
|
||||
}
|
||||
// Surface newly-shipped default servers (e.g. lite6/lite7) on an existing
|
||||
// install whose saved list predates them. Servers are hidden (see
|
||||
// lite_hidden_servers_), never deleted, so appending a missing default won't
|
||||
// resurrect one the user intentionally removed.
|
||||
for (const auto& def : defaultLiteServers()) {
|
||||
bool present = false;
|
||||
for (const auto& s : lite_servers_)
|
||||
if (s.url == def.url) { present = true; break; }
|
||||
if (!present) lite_servers_.push_back(def);
|
||||
}
|
||||
}
|
||||
if (lite.contains("rollout_override") && lite["rollout_override"].is_string()) {
|
||||
const auto v = lite["rollout_override"].get<std::string>();
|
||||
@@ -459,6 +492,13 @@ bool Settings::save(const std::string& path)
|
||||
j["hidden_chat_cids"] = json::array();
|
||||
for (const auto& c : hidden_chat_cids_)
|
||||
j["hidden_chat_cids"].push_back(c);
|
||||
j["blocked_chat_convs"] = json::array();
|
||||
for (const auto& b : blocked_chat_convs_) {
|
||||
json o;
|
||||
o["cid"] = b.cid;
|
||||
o["name"] = b.name;
|
||||
j["blocked_chat_convs"].push_back(o);
|
||||
}
|
||||
j["chat_emoji_color"] = chat_emoji_color_;
|
||||
j["chat_poll_rate_sec"] = chat_poll_rate_sec_;
|
||||
j["chat_bubble_style"] = chat_bubble_style_;
|
||||
@@ -525,6 +565,8 @@ bool Settings::save(const std::string& path)
|
||||
j["keep_daemon_running"] = keep_daemon_running_;
|
||||
j["stop_external_daemon"] = stop_external_daemon_;
|
||||
j["max_connections"] = max_connections_;
|
||||
j["stratum_host_enabled"] = stratum_host_enabled_;
|
||||
j["stratum_allowip"] = stratum_allowip_;
|
||||
{
|
||||
json lite = json::object();
|
||||
lite["server_selection_mode"] = liteServerSelectionPreferenceModeName(lite_server_selection_mode_);
|
||||
|
||||
+32
-8
@@ -144,6 +144,26 @@ public:
|
||||
hidden_chat_cids_.end());
|
||||
}
|
||||
|
||||
// Blocked chat conversations (by cid). Unlike hide (reversible, keeps messages), block DELETES the
|
||||
// local history AND suppresses every message for the cid — old and future — until you unblock, at
|
||||
// which point the conversation re-imports from the chain. The last-known peer name is kept so the
|
||||
// "Blocked" list can label the entry (its messages are gone from the local store).
|
||||
struct BlockedChatConv { std::string cid; std::string name; };
|
||||
bool isChatBlocked(const std::string& cid) const {
|
||||
for (const auto& b : blocked_chat_convs_) if (b.cid == cid) return true;
|
||||
return false;
|
||||
}
|
||||
void setChatBlocked(const std::string& cid, const std::string& name, bool blocked) {
|
||||
const bool already = isChatBlocked(cid);
|
||||
if (blocked && !already) blocked_chat_convs_.push_back({cid, name});
|
||||
else if (!blocked && already)
|
||||
blocked_chat_convs_.erase(
|
||||
std::remove_if(blocked_chat_convs_.begin(), blocked_chat_convs_.end(),
|
||||
[&](const BlockedChatConv& b) { return b.cid == cid; }),
|
||||
blocked_chat_convs_.end());
|
||||
}
|
||||
const std::vector<BlockedChatConv>& blockedChatConversations() const { return blocked_chat_convs_; }
|
||||
|
||||
// ── Chat-tab customization (chat settings modal + Settings → Chat & Contacts) ──────
|
||||
bool getChatEmojiColor() const { return chat_emoji_color_; }
|
||||
void setChatEmojiColor(bool v) { chat_emoji_color_ = v; }
|
||||
@@ -400,6 +420,11 @@ public:
|
||||
// Daemon — maximum peer connections (0 = daemon default)
|
||||
int getMaxConnections() const { return max_connections_; }
|
||||
void setMaxConnections(int v) { max_connections_ = std::max(0, v); }
|
||||
// Host a RandomX stratum pool from the node (v1.3.0+ daemons). Empty allow-IP = loopback only (safe).
|
||||
bool getStratumHost() const { return stratum_host_enabled_; }
|
||||
void setStratumHost(bool v) { stratum_host_enabled_ = v; }
|
||||
const std::string& getStratumAllowIp() const { return stratum_allowip_; }
|
||||
void setStratumAllowIp(const std::string& v) { stratum_allowip_ = v; }
|
||||
|
||||
// Lite wallet server selection
|
||||
LiteServerSelectionPreferenceMode getLiteServerSelectionMode() const { return lite_server_selection_mode_; }
|
||||
@@ -414,6 +439,9 @@ public:
|
||||
void setLitePersistSelectedServer(bool persist) { lite_persist_selected_server_ = persist; }
|
||||
const std::vector<LiteServerPreference>& getLiteServers() const { return lite_servers_; }
|
||||
void setLiteServers(const std::vector<LiteServerPreference>& servers) { lite_servers_ = servers; }
|
||||
// Servers shipped as defaults. Also merged into an existing install's saved list on load
|
||||
// (see Settings::load), so upgrades surface newly-added servers (e.g. lite6/lite7).
|
||||
static std::vector<LiteServerPreference> defaultLiteServers();
|
||||
|
||||
// User-defined portfolio entries (Market tab). "All funds" is implicit, not stored here.
|
||||
const std::vector<PortfolioEntry>& getPortfolioEntries() const { return portfolio_entries_; }
|
||||
@@ -554,6 +582,7 @@ private:
|
||||
std::string chat_reply_zaddr_;
|
||||
std::vector<std::string> muted_chat_cids_; // muted chat conversations by cid (Q10)
|
||||
std::vector<std::string> hidden_chat_cids_; // hidden chat conversations by cid
|
||||
std::vector<BlockedChatConv> blocked_chat_convs_; // blocked chat conversations (cid + last-known name)
|
||||
// Chat-tab customization (chat settings modal + Settings → Chat & Contacts).
|
||||
bool chat_emoji_color_ = true; // true = color (needs FreeType; falls back to mono if absent), false = monochrome
|
||||
float chat_poll_rate_sec_ = 2.5f; // 0-conf chat fast-scan cadence (full node)
|
||||
@@ -617,6 +646,8 @@ private:
|
||||
bool keep_daemon_running_ = false;
|
||||
bool stop_external_daemon_ = false;
|
||||
int max_connections_ = 0; // 0 = daemon default
|
||||
bool stratum_host_enabled_ = false; // host a RandomX stratum pool from the node (v1.3.0+ daemons)
|
||||
std::string stratum_allowip_; // -stratumallowip filter (empty = daemon default: loopback only)
|
||||
|
||||
// Lite wallet server preferences. These are user/server settings only;
|
||||
// wallet secrets, wallet files, and lifecycle state are never stored here.
|
||||
@@ -627,14 +658,7 @@ private:
|
||||
bool lite_persist_selected_server_ = true;
|
||||
std::string lite_rollout_override_ = "auto"; // auto|force_on|force_off
|
||||
std::string lite_install_id_; // random local-only id; rollout-bucket source
|
||||
std::vector<LiteServerPreference> lite_servers_ = {
|
||||
{"https://lite.dragonx.is", "DragonX Lite", true},
|
||||
{"https://lite1.dragonx.is", "DragonX Lite 1", true},
|
||||
{"https://lite2.dragonx.is", "DragonX Lite 2", true},
|
||||
{"https://lite3.dragonx.is", "DragonX Lite 3", true},
|
||||
{"https://lite4.dragonx.is", "DragonX Lite 4", true},
|
||||
{"https://lite5.dragonx.is", "DragonX Lite 5", true}
|
||||
};
|
||||
std::vector<LiteServerPreference> lite_servers_ = defaultLiteServers();
|
||||
std::set<std::string> lite_hidden_servers_; // server URLs hidden from the Network tab
|
||||
|
||||
std::vector<PortfolioEntry> portfolio_entries_; // Market tab custom portfolio groups
|
||||
|
||||
@@ -26,6 +26,7 @@ void DaemonController::syncSettings(const config::Settings* settings)
|
||||
if (!settings) return;
|
||||
daemon_->setDebugCategories(settings->getDebugCategories());
|
||||
daemon_->setMaxConnections(settings->getMaxConnections());
|
||||
daemon_->setStratumHosting(settings->getStratumHost(), settings->getStratumAllowIp());
|
||||
|
||||
std::string walletFile = settings->getActiveWalletFile();
|
||||
// The Wallets dialog opens an out-of-datadir wallet by linking it into the datadir under a
|
||||
|
||||
@@ -545,6 +545,15 @@ bool EmbeddedDaemon::start(const std::string& binary_path)
|
||||
args.push_back("-maxconnections=" + std::to_string(max_connections_));
|
||||
}
|
||||
|
||||
// Host a RandomX stratum pool from this node (-stratum). Only v1.3.0+ daemons implement it; older
|
||||
// ones ignore the unknown flag (no fatal arg check), and the Settings toggle is gated on daemon
|
||||
// version, so this is only enabled against a daemon that supports it. Without -stratumallowip the
|
||||
// daemon serves loopback only (safe default); a subnet opens it to that LAN.
|
||||
if (stratum_enabled_) {
|
||||
args.push_back("-stratum");
|
||||
if (!stratum_allowip_.empty()) args.push_back("-stratumallowip=" + stratum_allowip_);
|
||||
}
|
||||
|
||||
// Active wallet file (multi-wallet). The daemon loads <datadir>/<name>. Only pass it for a
|
||||
// non-default name so the common case's command line is unchanged; skip during an isolated
|
||||
// start (seed migration manages its own throwaway wallet).
|
||||
@@ -807,11 +816,21 @@ void EmbeddedDaemon::drainOutput()
|
||||
}
|
||||
|
||||
size_t currentSize = static_cast<size_t>(fileSize.QuadPart);
|
||||
// Truncation / rotation detection. dragonxd shrinks debug.log on startup (ShrinkDebugFile keeps
|
||||
// only the tail once it has grown large), so the file can become SMALLER than where we left off.
|
||||
// Our offset was set to the PRE-shrink size at spawn, so without this reset it stays stranded ahead
|
||||
// of the freshly-truncated file and we read NOTHING for the whole session — no block height (status
|
||||
// bar shows "Block: 0") and, worse, no witness-rebuild progress, since the "Setting Initial Sapling
|
||||
// Witness …" lines the warmup progress bar parses only exist in debug.log on Windows. On a shrink,
|
||||
// restart from the new beginning; the parser is monotonic and converges as it reaches current output.
|
||||
if (currentSize < debug_log_offset_) {
|
||||
debug_log_offset_ = 0;
|
||||
}
|
||||
if (currentSize <= debug_log_offset_) {
|
||||
CloseHandle(hFile);
|
||||
return; // No new data
|
||||
}
|
||||
|
||||
|
||||
// Seek to where we left off
|
||||
LARGE_INTEGER seekPos;
|
||||
seekPos.QuadPart = static_cast<LONGLONG>(debug_log_offset_);
|
||||
|
||||
@@ -182,6 +182,7 @@ public:
|
||||
* @brief Set maximum peer connections (0 = use daemon default)
|
||||
*/
|
||||
void setMaxConnections(int v) { max_connections_ = v; }
|
||||
void setStratumHosting(bool enabled, const std::string& allowIp) { stratum_enabled_ = enabled; stratum_allowip_ = allowIp; }
|
||||
|
||||
/**
|
||||
* @brief Request a blockchain rescan on the next daemon start
|
||||
@@ -311,6 +312,8 @@ private:
|
||||
std::atomic<bool> should_stop_{false};
|
||||
std::set<std::string> debug_categories_;
|
||||
int max_connections_ = 0; // 0 = daemon default
|
||||
bool stratum_enabled_ = false; // -stratum: host a RandomX pool (v1.3.0+; older daemons ignore it)
|
||||
std::string stratum_allowip_; // -stratumallowip subnet (empty = daemon default: loopback only)
|
||||
std::string wallet_file_; // -wallet=<name> for the active wallet; empty/"wallet.dat" = default
|
||||
std::atomic<int> crash_count_{0}; // consecutive crash counter
|
||||
std::atomic<bool> rescan_on_next_start_{false}; // -rescan flag for next start
|
||||
|
||||
@@ -68,8 +68,9 @@ bool AddressBook::load()
|
||||
}
|
||||
|
||||
DEBUG_LOGF("Address book loaded: %zu entries\n", entries_.size());
|
||||
++revision_;
|
||||
return true;
|
||||
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("Error loading address book: %s\n", e.what());
|
||||
return false;
|
||||
@@ -121,6 +122,7 @@ bool AddressBook::addEntry(const AddressBookEntry& entry)
|
||||
}
|
||||
|
||||
entries_.push_back(entry);
|
||||
++revision_;
|
||||
return save();
|
||||
}
|
||||
|
||||
@@ -136,6 +138,7 @@ bool AddressBook::updateEntry(size_t index, const AddressBookEntry& entry)
|
||||
}
|
||||
|
||||
entries_[index] = entry;
|
||||
++revision_;
|
||||
return save();
|
||||
}
|
||||
|
||||
@@ -146,6 +149,7 @@ bool AddressBook::removeEntry(size_t index)
|
||||
}
|
||||
|
||||
entries_.erase(entries_.begin() + index);
|
||||
++revision_;
|
||||
return save();
|
||||
}
|
||||
|
||||
@@ -159,7 +163,7 @@ int AddressBook::reattachLegacyScopes(const std::string& scopeId)
|
||||
e.scope = scopeId;
|
||||
++rescoped;
|
||||
}
|
||||
if (rescoped > 0) save();
|
||||
if (rescoped > 0) { ++revision_; save(); }
|
||||
return rescoped;
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -121,11 +122,18 @@ public:
|
||||
*/
|
||||
size_t size() const { return entries_.size(); }
|
||||
|
||||
/**
|
||||
* @brief Monotonic counter bumped on every content change (add/update/remove/load/sweep). Consumers
|
||||
* (e.g. the chat conversation-list memo) key their caches off this so an IN-PLACE edit — a rename or
|
||||
* address change that keeps size() constant — still invalidates them.
|
||||
*/
|
||||
std::uint64_t revision() const { return revision_; }
|
||||
|
||||
/**
|
||||
* @brief UI-sweep ONLY: replace the in-memory entries WITHOUT persisting to disk, so the sweep can
|
||||
* seed demo contacts and restore the real book without a disk write. Do not use outside the sweep.
|
||||
*/
|
||||
void sweepSetEntries(std::vector<AddressBookEntry> e) { entries_ = std::move(e); }
|
||||
void sweepSetEntries(std::vector<AddressBookEntry> e) { entries_ = std::move(e); ++revision_; }
|
||||
|
||||
/**
|
||||
* @brief Check if empty
|
||||
@@ -134,6 +142,7 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<AddressBookEntry> entries_;
|
||||
std::uint64_t revision_ = 0;
|
||||
std::string file_path_;
|
||||
};
|
||||
|
||||
|
||||
+54
-5
@@ -1263,6 +1263,36 @@ int main(int argc, char* argv[])
|
||||
SDL_SetWindowMinimumSize(window, (int)(1024 * currentDpiScale), (int)(720 * currentDpiScale));
|
||||
}
|
||||
|
||||
// DEV/TEST hook (dormant unless the env is set): DRAGONX_WIN_GEOM="WxH" forces an exact window
|
||||
// size, placing it on the largest display that can hold it and bypassing the primary-monitor
|
||||
// clamp. Lets a headless WSLg sweep render at sizes wider than the 1280 primary (e.g. 2560x1440
|
||||
// on the mirrored 4K/1440p outputs). Needs the x11 backend so absolute positioning takes effect.
|
||||
int wantW = 0, wantH = 0;
|
||||
if (const char* geom = std::getenv("DRAGONX_WIN_GEOM"))
|
||||
sscanf(geom, "%dx%d", &wantW, &wantH);
|
||||
if (wantW > 0 && wantH > 0) {
|
||||
int count = 0;
|
||||
SDL_DisplayID* disp = SDL_GetDisplays(&count);
|
||||
SDL_DisplayID best = 0; SDL_Rect bestUsable{0, 0, 0, 0};
|
||||
for (int i = 0; i < count; ++i) {
|
||||
SDL_Rect u;
|
||||
if (!SDL_GetDisplayUsableBounds(disp[i], &u)) continue;
|
||||
bool fits = (u.w >= wantW && u.h >= wantH);
|
||||
bool bestFits = (bestUsable.w >= wantW && bestUsable.h >= wantH);
|
||||
// Prefer a display that fits; among those, the smallest; else the largest available.
|
||||
if ((fits && !bestFits) ||
|
||||
(fits && bestFits && (long)u.w * u.h < (long)bestUsable.w * bestUsable.h) ||
|
||||
(!fits && !bestFits && (long)u.w * u.h > (long)bestUsable.w * bestUsable.h)) {
|
||||
bestUsable = u; best = disp[i];
|
||||
}
|
||||
}
|
||||
if (disp) SDL_free(disp);
|
||||
if (best) {
|
||||
SDL_SetWindowMinimumSize(window, 320, 240);
|
||||
SDL_SetWindowPosition(window, bestUsable.x + 10, bestUsable.y + 10);
|
||||
SDL_SetWindowSize(window, wantW, wantH);
|
||||
}
|
||||
} else {
|
||||
// Clamp to the current display's work area — runs on EVERY startup (this clamp used to live
|
||||
// inside the HiDPI branch, so a size saved on a larger/disconnected monitor could open the
|
||||
// window off-screen or bigger than the screen on a same-DPI cold start).
|
||||
@@ -1281,6 +1311,7 @@ int main(int argc, char* argv[])
|
||||
DEBUG_LOGF("Startup: window fitted %dx%d -> %dx%d (scale %.2f)\n",
|
||||
curW, curH, newW, newH, currentDpiScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
winlog("STARTUP savedSize=%dx%d currentDpiScale=%.3f", savedWinW, savedWinH, currentDpiScale);
|
||||
@@ -1469,6 +1500,7 @@ int main(int argc, char* argv[])
|
||||
// WINDOW_RESIZED events during the transition can't corrupt savedSizeForScale / lastKnownW/H.
|
||||
int dpiSettleFrames = 0;
|
||||
SDL_DisplayID lastLoggedDisplay = 0; // [WINLOG] throttle: log MOVED only when the display changes
|
||||
Uint64 minimizedLastTickMs = 0; // real-clock tick for the minimized "keep syncing" update
|
||||
{
|
||||
float s = dragonx::ui::material::Typography::instance().getDpiScale();
|
||||
int w = 0, h = 0;
|
||||
@@ -1552,6 +1584,7 @@ int main(int argc, char* argv[])
|
||||
// Window restored from minimized — trigger immediate data refresh
|
||||
if (waitEvent.type == SDL_EVENT_WINDOW_RESTORED &&
|
||||
waitEvent.window.windowID == SDL_GetWindowID(window)) {
|
||||
app.skipDaemonOutputBacklog(); // drop the minimized-period backlog (avoids a spurious "rescan complete" toast)
|
||||
app.refreshNow();
|
||||
}
|
||||
// Handle DPI change that arrived while idle (same logic as poll loop)
|
||||
@@ -1681,6 +1714,7 @@ int main(int argc, char* argv[])
|
||||
// Window restored from minimized — trigger immediate data refresh
|
||||
if (event.type == SDL_EVENT_WINDOW_RESTORED &&
|
||||
event.window.windowID == SDL_GetWindowID(window)) {
|
||||
app.skipDaemonOutputBacklog(); // drop the minimized-period backlog (avoids a spurious "rescan complete" toast)
|
||||
app.refreshNow();
|
||||
}
|
||||
// Handle DPI/display scale changes (e.g. window dragged to a
|
||||
@@ -1701,13 +1735,28 @@ int main(int argc, char* argv[])
|
||||
|
||||
// Check if window is minimized
|
||||
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) {
|
||||
// Still check shouldQuit while minimized to avoid hang
|
||||
if (app.shouldQuit()) {
|
||||
running = false;
|
||||
}
|
||||
SDL_Delay(10);
|
||||
// Keep the wallet syncing while minimized: run the logic update (drains RPC results, ticks the
|
||||
// refresh scheduler, keeps the daemon connection/reconnect + sync status live) but skip the
|
||||
// ImGui frame + GPU present since nothing is visible. app.update() only reads
|
||||
// GetIO()/GetTime()/IsAnyItemActive() — all valid outside a frame — so it's safe without a
|
||||
// NewFrame; feed it a real-clock DeltaTime (NewFrame, which normally sets it, is skipped) and
|
||||
// let app.update() clamp it. Throttled to ~5 Hz so CPU stays near-idle (refresh cadences are
|
||||
// seconds-scale). shouldQuit is still checked so a quit request never hangs behind minimize.
|
||||
Uint64 nowMs = SDL_GetTicks();
|
||||
float minDelta = (minimizedLastTickMs == 0) ? 0.001f
|
||||
: (float)(nowMs - minimizedLastTickMs) / 1000.0f;
|
||||
minimizedLastTickMs = nowMs;
|
||||
ImGui::GetIO().DeltaTime = (minDelta > 0.0f) ? minDelta : 0.001f;
|
||||
try {
|
||||
app.update();
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("[Main] minimized app.update() threw: %s\n", e.what());
|
||||
} catch (...) {}
|
||||
if (app.shouldQuit()) running = false;
|
||||
SDL_Delay(200);
|
||||
continue;
|
||||
}
|
||||
minimizedLastTickMs = 0; // visible again — reset the minimized clock
|
||||
|
||||
// --- PerfLog: begin frame ---
|
||||
dragonx::util::PerfLog::instance().beginFrame();
|
||||
|
||||
@@ -297,7 +297,31 @@ NetworkRefreshService::CoreRefreshResult NetworkRefreshService::collectCoreRefre
|
||||
bool blockOk = false;
|
||||
double balanceScanMs = 0.0;
|
||||
|
||||
if (includeBalance) {
|
||||
// getblockchaininfo FIRST — it's cheap, and the sync state it returns gates everything else
|
||||
// (the balance/address/tx throttles and kSyncProfile). Running it BEFORE the O(mapWallet) balance
|
||||
// scan keeps sync detection from being delayed behind (or, under contention, starved by) that
|
||||
// scan — the failure mode where the wallet kept reading "synced" while actually falling behind,
|
||||
// so kSyncProfile never engaged. See effectivelySyncing()'s sticky-behind latch.
|
||||
try {
|
||||
blockInfo = rpc.call("getblockchaininfo", json::array());
|
||||
blockOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("BlockchainInfo error: %s\n", e.what());
|
||||
}
|
||||
|
||||
// If getblockchaininfo shows we're behind (same 2-block tolerance as applyCoreRefreshResult), skip
|
||||
// the balance scan this cycle regardless of includeBalance: the balance is incomplete mid-sync
|
||||
// anyway, and skipping it lets the sync-state update — and hence kSyncProfile — take effect at the
|
||||
// end of THIS (now-cheap) task instead of being delayed ~20s behind the scan. This is what lets the
|
||||
// sticky-behind latch engage promptly the first time the node falls behind.
|
||||
bool behind = false;
|
||||
if (blockOk && blockInfo.is_object()) {
|
||||
const long long b = blockInfo.value("blocks", 0LL);
|
||||
const long long lc = blockInfo.value("longestchain", 0LL);
|
||||
if (lc > 0 && b < lc - 2) behind = true;
|
||||
}
|
||||
|
||||
if (includeBalance && !behind) {
|
||||
// z_gettotalbalance is O(mapWallet) and holds the daemon's cs_main for its whole duration —
|
||||
// seconds on a large shielded wallet. Time it so the caller can throttle how often it polls
|
||||
// (balanceRefreshDue()), keeping balance scans from starving block connection.
|
||||
@@ -315,13 +339,6 @@ NetworkRefreshService::CoreRefreshResult NetworkRefreshService::collectCoreRefre
|
||||
std::chrono::steady_clock::now() - balanceStart).count();
|
||||
}
|
||||
|
||||
try {
|
||||
blockInfo = rpc.call("getblockchaininfo", json::array());
|
||||
blockOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("BlockchainInfo error: %s\n", e.what());
|
||||
}
|
||||
|
||||
auto result = parseCoreRefreshResult(totalBalance, spendableBalance, balanceOk, blockInfo, blockOk);
|
||||
result.balanceScanMs = balanceScanMs;
|
||||
return result;
|
||||
@@ -603,6 +620,10 @@ NetworkRefreshService::AddressRefreshResult NetworkRefreshService::collectAddres
|
||||
const AddressRefreshSnapshot& snapshot)
|
||||
{
|
||||
AddressRefreshResult result;
|
||||
// Time the whole scan — z_listunspent (and per-address z_getbalance fallback) hold the daemon's
|
||||
// cs_main for the duration, seconds on a large shielded wallet. The measured cost feeds the
|
||||
// caller's adaptive throttle so the address poll can't starve block connection.
|
||||
const auto scanStart = std::chrono::steady_clock::now();
|
||||
|
||||
try {
|
||||
json zList = rpc.call("z_listaddresses", json::array());
|
||||
@@ -644,6 +665,22 @@ NetworkRefreshService::AddressRefreshResult NetworkRefreshService::collectAddres
|
||||
try {
|
||||
json unspent = rpc.call("z_listunspent", json::array({0, 9999999, false})); // minconf=0 → include 0-conf change
|
||||
applyShieldedBalancesFromUnspent(result.shieldedAddresses, unspent);
|
||||
// Retain a minimal view so a downstream consumer (chat note-budget) can reuse this scan instead
|
||||
// of issuing its own z_listunspent. rawconfirmations is the TRUE depth; `confirmations` is
|
||||
// dPoW-clamped to 1 and understates it.
|
||||
if (unspent.is_array()) {
|
||||
result.unspentNotes.reserve(unspent.size());
|
||||
for (const auto& nz : unspent) {
|
||||
if (!nz.is_object()) continue;
|
||||
UnspentNoteLite lite;
|
||||
lite.amount = nz.value("amount", 0.0);
|
||||
lite.locked = nz.value("locked", false);
|
||||
lite.confirmations = (nz.contains("rawconfirmations") && nz["rawconfirmations"].is_number_integer())
|
||||
? nz["rawconfirmations"].get<int>()
|
||||
: nz.value("confirmations", 0);
|
||||
result.unspentNotes.push_back(lite);
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("z_listunspent unavailable (%s), falling back to z_getbalance\n", e.what());
|
||||
for (auto& info : result.shieldedAddresses) {
|
||||
@@ -673,6 +710,8 @@ NetworkRefreshService::AddressRefreshResult NetworkRefreshService::collectAddres
|
||||
DEBUG_LOGF("listunspent error: %s\n", e.what());
|
||||
}
|
||||
|
||||
result.scanMs = std::chrono::duration<double, std::milli>(
|
||||
std::chrono::steady_clock::now() - scanStart).count();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -864,6 +903,10 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectTr
|
||||
result.blockHeight = currentBlockHeight;
|
||||
result.shieldedAddressCount = snapshot.shieldedAddresses.size();
|
||||
result.shieldedScanHeights = snapshot.shieldedScanHeights;
|
||||
// Time the whole scan — the per-address z_listreceivedbyaddress pass is O(mapWallet) and holds the
|
||||
// daemon's cs_main. The measured cost feeds the caller's adaptive throttle (txRefreshDue()) so the
|
||||
// routine full history rescan can't starve block connection on a large wallet.
|
||||
const auto scanStart = std::chrono::steady_clock::now();
|
||||
|
||||
std::set<std::string> knownTxids;
|
||||
HushChatMemoOutputMap hushChatReceivedOutputs;
|
||||
@@ -1044,6 +1087,8 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectTr
|
||||
}
|
||||
|
||||
sortTransactionsNewestFirst(result.transactions);
|
||||
result.scanMs = std::chrono::duration<double, std::milli>(
|
||||
std::chrono::steady_clock::now() - scanStart).count();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1063,6 +1108,22 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectRe
|
||||
? &hushChatReceivedOutputs
|
||||
: nullptr;
|
||||
|
||||
// Index result.transactions by (txid, type) once so the two replace-loops below
|
||||
// can find-and-replace in O(1) instead of a nested linear scan over the full
|
||||
// (potentially thousands-large) tx list on every 'recent' refresh cycle. The map
|
||||
// holds the index of the FIRST occurrence of each key (preserving the linear
|
||||
// scan's break-on-first-match), and is kept in sync on every append so a later
|
||||
// entry in the same cycle still finds an earlier appended one — exactly as the
|
||||
// re-scanned vector did before.
|
||||
auto txKey = [](const TransactionInfo& tx) {
|
||||
return tx.txid + '\x1f' + tx.type;
|
||||
};
|
||||
std::unordered_map<std::string, std::size_t> byKey;
|
||||
byKey.reserve(result.transactions.size());
|
||||
for (std::size_t i = 0; i < result.transactions.size(); ++i) {
|
||||
byKey.emplace(txKey(result.transactions[i]), i); // keep first-occurrence index
|
||||
}
|
||||
|
||||
try {
|
||||
std::set<std::string> recentTxids;
|
||||
std::vector<TransactionInfo> recentTransactions;
|
||||
@@ -1070,15 +1131,13 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectRe
|
||||
appendTransparentTransactions(recentTransactions, recentTxids, transactions, snapshot.miningAddresses);
|
||||
|
||||
for (auto& recent : recentTransactions) {
|
||||
bool replaced = false;
|
||||
for (auto& existing : result.transactions) {
|
||||
if (existing.txid == recent.txid && existing.type == recent.type) {
|
||||
existing = recent;
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
auto it = byKey.find(txKey(recent));
|
||||
if (it != byKey.end()) {
|
||||
result.transactions[it->second] = recent;
|
||||
} else {
|
||||
byKey.emplace(txKey(recent), result.transactions.size());
|
||||
result.transactions.push_back(std::move(recent));
|
||||
}
|
||||
if (!replaced) result.transactions.push_back(std::move(recent));
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("recent listtransactions error: %s\n", e.what());
|
||||
@@ -1104,15 +1163,13 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectRe
|
||||
snapshot.miningAddresses,
|
||||
hushChatReceivedOutputsPtr);
|
||||
for (auto& scanned : scannedTransactions) {
|
||||
bool replaced = false;
|
||||
for (auto& existing : result.transactions) {
|
||||
if (existing.txid == scanned.txid && existing.type == scanned.type) {
|
||||
existing = scanned;
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
auto it = byKey.find(txKey(scanned));
|
||||
if (it != byKey.end()) {
|
||||
result.transactions[it->second] = scanned;
|
||||
} else {
|
||||
byKey.emplace(txKey(scanned), result.transactions.size());
|
||||
result.transactions.push_back(std::move(scanned));
|
||||
}
|
||||
if (!replaced) result.transactions.push_back(std::move(scanned));
|
||||
}
|
||||
if (currentBlockHeight >= 0) result.shieldedScanHeights[address] = currentBlockHeight;
|
||||
++result.shieldedAddressesScanned;
|
||||
|
||||
@@ -147,6 +147,14 @@ public:
|
||||
std::string errorMessage;
|
||||
};
|
||||
|
||||
// Minimal per-note view of a z_listunspent entry — just what a downstream consumer needs to derive
|
||||
// spendable-note budgets without re-scanning. Kept UI/feature-agnostic (no chat specifics here).
|
||||
struct UnspentNoteLite {
|
||||
double amount = 0.0; // note value, DRGX
|
||||
bool locked = false; // tied up by an in-flight send
|
||||
int confirmations = 0; // TRUE depth (rawconfirmations when present, else confirmations)
|
||||
};
|
||||
|
||||
struct AddressRefreshResult {
|
||||
std::vector<AddressInfo> shieldedAddresses;
|
||||
std::vector<AddressInfo> transparentAddresses;
|
||||
@@ -154,6 +162,12 @@ public:
|
||||
// lists may be falsely short. Consumers that treat an empty list as authoritative (e.g. the
|
||||
// empty-wallet warning) must not trust a 0 count unless this is true.
|
||||
bool addressListOk = true;
|
||||
// Wall-clock spent in the address scan (dominated by z_listunspent — O(mapWallet), holds the
|
||||
// daemon's cs_main). Lets the caller throttle how often it polls (addressRefreshDue()).
|
||||
double scanMs = 0.0;
|
||||
// The wallet's unspent notes from this same z_listunspent scan, so a consumer (e.g. the chat
|
||||
// note-budget) can be fed for free instead of running its own duplicate z_listunspent.
|
||||
std::vector<UnspentNoteLite> unspentNotes;
|
||||
};
|
||||
|
||||
struct AddressRefreshSnapshot {
|
||||
@@ -205,6 +219,9 @@ public:
|
||||
std::size_t shieldedAddressCount = 0;
|
||||
std::unordered_map<std::string, int> shieldedScanHeights;
|
||||
bool shieldedScanComplete = true;
|
||||
// Wall-clock spent in the history scan (z_listreceivedbyaddress — O(mapWallet), holds cs_main).
|
||||
// Lets the caller throttle the routine full rescan by its measured cost (txRefreshDue()).
|
||||
double scanMs = 0.0;
|
||||
};
|
||||
|
||||
struct OperationStatusPollResult {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "theme_effects.h"
|
||||
#include "low_spec.h"
|
||||
#include "../schema/ui_schema.h"
|
||||
#include "../layout.h"
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -58,6 +59,7 @@ void ThemeEffects::beginFrame() {
|
||||
|
||||
void ThemeEffects::loadFromTheme() {
|
||||
auto& S = schema::UI();
|
||||
const float dp = Layout::dpiScale();
|
||||
auto eff = [&](const char* name) {
|
||||
return S.drawElement("effects", name);
|
||||
};
|
||||
@@ -98,7 +100,7 @@ void ThemeEffects::loadFromTheme() {
|
||||
// ---- Shimmer ----
|
||||
shimmer_.enabled = eff("shimmer-enabled").sizeOr(0.0f) > 0.5f;
|
||||
shimmer_.speed = eff("shimmer-speed").sizeOr(0.12f);
|
||||
shimmer_.width = eff("shimmer-width").sizeOr(80.0f);
|
||||
shimmer_.width = eff("shimmer-width").sizeOr(80.0f) * dp;
|
||||
shimmer_.alpha = eff("shimmer-alpha").sizeOr(0.06f);
|
||||
shimmer_.angle = eff("shimmer-angle").sizeOr(30.0f);
|
||||
// Shimmer color: read from the schema's color resolver
|
||||
@@ -124,7 +126,7 @@ void ThemeEffects::loadFromTheme() {
|
||||
glow_pulse_.speed = eff("glow-pulse-speed").sizeOr(2.0f);
|
||||
glow_pulse_.minAlpha = eff("glow-pulse-min-alpha").sizeOr(0.0f);
|
||||
glow_pulse_.maxAlpha = eff("glow-pulse-max-alpha").sizeOr(0.15f);
|
||||
glow_pulse_.radius = eff("glow-pulse-radius").sizeOr(4.0f);
|
||||
glow_pulse_.radius = eff("glow-pulse-radius").sizeOr(4.0f) * dp;
|
||||
auto glowColorElem = eff("glow-pulse-color");
|
||||
if (!glowColorElem.color.empty()) {
|
||||
glow_pulse_.color = S.resolveColor(glowColorElem.color, IM_COL32(255, 218, 0, 255));
|
||||
@@ -136,7 +138,7 @@ void ThemeEffects::loadFromTheme() {
|
||||
edge_trace_.enabled = eff("edge-trace-enabled").sizeOr(0.0f) > 0.5f;
|
||||
edge_trace_.speed = eff("edge-trace-speed").sizeOr(0.3f);
|
||||
edge_trace_.length = eff("edge-trace-length").sizeOr(0.20f);
|
||||
edge_trace_.thickness = eff("edge-trace-thickness").sizeOr(1.5f);
|
||||
edge_trace_.thickness = eff("edge-trace-thickness").sizeOr(1.5f) * dp;
|
||||
edge_trace_.alpha = eff("edge-trace-alpha").sizeOr(0.6f);
|
||||
auto edgeColorElem = eff("edge-trace-color");
|
||||
if (!edgeColorElem.color.empty()) {
|
||||
@@ -149,7 +151,7 @@ void ThemeEffects::loadFromTheme() {
|
||||
ember_rise_.enabled = eff("ember-rise-enabled").sizeOr(0.0f) > 0.5f;
|
||||
ember_rise_.count = (int)eff("ember-rise-count").sizeOr(8.0f);
|
||||
ember_rise_.speed = eff("ember-rise-speed").sizeOr(0.4f);
|
||||
ember_rise_.particleSize = eff("ember-rise-particle-size").sizeOr(1.5f);
|
||||
ember_rise_.particleSize = eff("ember-rise-particle-size").sizeOr(1.5f) * dp;
|
||||
ember_rise_.alpha = eff("ember-rise-alpha").sizeOr(0.5f);
|
||||
auto emberColorElem = eff("ember-rise-color");
|
||||
if (!emberColorElem.color.empty()) {
|
||||
@@ -165,7 +167,7 @@ void ThemeEffects::loadFromTheme() {
|
||||
// accent (e.g. Obsidian) are unaffected; Jade turns it on as its hero.
|
||||
gradient_border_.panels = eff("gradient-border-panels").sizeOr(0.0f) > 0.5f;
|
||||
gradient_border_.speed = eff("gradient-border-speed").sizeOr(0.15f);
|
||||
gradient_border_.thickness = eff("gradient-border-thickness").sizeOr(1.5f);
|
||||
gradient_border_.thickness = eff("gradient-border-thickness").sizeOr(1.5f) * dp;
|
||||
gradient_border_.alpha = eff("gradient-border-alpha").sizeOr(0.6f);
|
||||
auto gbColorA = eff("gradient-border-color-a");
|
||||
if (!gbColorA.color.empty()) {
|
||||
@@ -194,7 +196,7 @@ void ThemeEffects::loadFromTheme() {
|
||||
sandstorm_.count = (int)eff("sandstorm-count").sizeOr(80.0f);
|
||||
sandstorm_.speed = eff("sandstorm-speed").sizeOr(0.35f);
|
||||
sandstorm_.windAngle = eff("sandstorm-wind-angle").sizeOr(15.0f);
|
||||
sandstorm_.particleSize = eff("sandstorm-particle-size").sizeOr(1.5f);
|
||||
sandstorm_.particleSize = eff("sandstorm-particle-size").sizeOr(1.5f) * dp;
|
||||
sandstorm_.alpha = eff("sandstorm-alpha").sizeOr(0.35f);
|
||||
sandstorm_.gustSpeed = eff("sandstorm-gust-speed").sizeOr(0.07f);
|
||||
sandstorm_.gustStrength = eff("sandstorm-gust-strength").sizeOr(0.4f);
|
||||
@@ -694,6 +696,7 @@ void ThemeEffects::drawEdgeTrace(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
||||
void ThemeEffects::drawEmberRise(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax) const {
|
||||
if (!enabled_ || !ember_rise_.enabled) return;
|
||||
|
||||
const float dp = Layout::dpiScale();
|
||||
float w = pMax.x - pMin.x;
|
||||
float h = pMax.y - pMin.y;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
@@ -707,7 +710,7 @@ void ThemeEffects::drawEmberRise(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax) const
|
||||
// Deterministic pseudo-random x position per particle
|
||||
// Simple hash: sin of large prime multiples
|
||||
float xHash = std::sin((float)(i + 1) * 127.1f) * 0.5f + 0.5f;
|
||||
float xDrift = std::sin(time_ * 0.7f + i * 2.4f) * 4.0f; // gentle sway
|
||||
float xDrift = std::sin(time_ * 0.7f + i * 2.4f) * 4.0f * dp; // gentle sway
|
||||
|
||||
float x = pMin.x + w * xHash + xDrift;
|
||||
float y = pMax.y - phase * (h + 8.0f); // rise from bottom past top
|
||||
@@ -745,6 +748,7 @@ void ThemeEffects::drawEmberRise(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax) const
|
||||
void ThemeEffects::drawViewportEmbers(ImDrawList* dl) const {
|
||||
if (!enabled_ || !ember_rise_.enabled) return;
|
||||
|
||||
const float dp = Layout::dpiScale();
|
||||
ImGuiViewport* vp = ImGui::GetMainViewport();
|
||||
float vpW = vp->WorkSize.x;
|
||||
float vpH = vp->WorkSize.y;
|
||||
@@ -765,7 +769,7 @@ void ThemeEffects::drawViewportEmbers(ImDrawList* dl) const {
|
||||
float xHash = std::sin((float)(i + 1) * 127.1f) * 43758.5453f;
|
||||
xHash = xHash - (int)xHash; // fractional part
|
||||
if (xHash < 0) xHash += 1.0f;
|
||||
float xDrift = std::sin(time_ * 0.5f + i * 1.7f) * 8.0f;
|
||||
float xDrift = std::sin(time_ * 0.5f + i * 1.7f) * 8.0f * dp;
|
||||
|
||||
float x = vpX + vpW * xHash + xDrift;
|
||||
float y = vpY + vpH * (1.0f - phase); // rise from bottom to top
|
||||
|
||||
@@ -925,7 +925,7 @@ inline void DrawStatCard(ImDrawList* dl,
|
||||
// Draw a full-height rounded rect with card rounding (left corners)
|
||||
// and clip to stripe width so the shape follows the corner radius.
|
||||
if ((card.accentCol & IM_COL32_A_MASK) != 0) {
|
||||
float stripeW = 4.0f;
|
||||
float stripeW = 4.0f * Layout::dpiScale();
|
||||
dl->PushClipRect(cMin, ImVec2(cMin.x + stripeW, cMax.y), true);
|
||||
dl->AddRectFilled(cMin, cMax, card.accentCol, rnd,
|
||||
ImDrawFlags_RoundCornersLeft);
|
||||
@@ -1282,7 +1282,8 @@ inline bool DrawDialogTitleBar(const char* title, bool* p_open, ImU32 accent_col
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
ImVec2 winPos = ImGui::GetWindowPos();
|
||||
float winWidth = ImGui::GetWindowWidth();
|
||||
float barHeight = 36.0f;
|
||||
const float dp = Layout::dpiScale();
|
||||
float barHeight = 36.0f * dp;
|
||||
|
||||
// Get accent color from theme if not provided
|
||||
if (!accent_col) {
|
||||
@@ -1306,15 +1307,15 @@ inline bool DrawDialogTitleBar(const char* title, bool* p_open, ImU32 accent_col
|
||||
ImFont* titleFont = Type().subtitle1();
|
||||
ImGui::PushFont(titleFont);
|
||||
ImVec2 titleSize = ImGui::CalcTextSize(title);
|
||||
float titleX = barMin.x + 16.0f;
|
||||
float titleX = barMin.x + 16.0f * dp;
|
||||
float titleY = barMin.y + (barHeight - titleSize.y) * 0.5f;
|
||||
DrawTextShadow(dl, ImVec2(titleX, titleY), OnSurface(), title);
|
||||
ImGui::PopFont();
|
||||
|
||||
// Close button (X) on right side
|
||||
if (p_open) {
|
||||
float btnSize = 24.0f;
|
||||
float btnX = barMax.x - btnSize - 12.0f;
|
||||
float btnSize = 24.0f * dp;
|
||||
float btnX = barMax.x - btnSize - 12.0f * dp;
|
||||
float btnY = barMin.y + (barHeight - btnSize) * 0.5f;
|
||||
ImVec2 btnMin(btnX, btnY);
|
||||
ImVec2 btnMax(btnX + btnSize, btnY + btnSize);
|
||||
@@ -1327,7 +1328,7 @@ inline bool DrawDialogTitleBar(const char* title, bool* p_open, ImU32 accent_col
|
||||
|
||||
// Button background on hover
|
||||
if (hovered) {
|
||||
dl->AddRectFilled(btnMin, btnMax, IM_COL32(255, 255, 255, held ? 40 : 25), 4.0f);
|
||||
dl->AddRectFilled(btnMin, btnMax, IM_COL32(255, 255, 255, held ? 40 : 25), 4.0f * dp);
|
||||
}
|
||||
|
||||
// Draw X icon
|
||||
@@ -1348,7 +1349,7 @@ inline bool DrawDialogTitleBar(const char* title, bool* p_open, ImU32 accent_col
|
||||
}
|
||||
|
||||
// Reserve space for title bar so content starts below it
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + barHeight + 8.0f);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + barHeight + 8.0f * dp);
|
||||
|
||||
return closeClicked;
|
||||
}
|
||||
@@ -1648,7 +1649,7 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
|
||||
// Fill/border alpha govern every overlay dialog's card boundary — kept well above the default
|
||||
// glass panel so the card reads as a distinct surface over busy backdrops (tx lists, mining
|
||||
// tiles, chat) while staying translucent rather than opaque.
|
||||
cardGlass.rounding = 16.0f; cardGlass.fillAlpha = 60; cardGlass.borderAlpha = 90; cardGlass.borderWidth = 1.0f;
|
||||
cardGlass.rounding = 16.0f * dp; cardGlass.fillAlpha = 60; cardGlass.borderAlpha = 90; cardGlass.borderWidth = 1.0f;
|
||||
DrawGlassPanel(dl, cardMin, cardMax, cardGlass);
|
||||
}
|
||||
|
||||
@@ -1662,8 +1663,8 @@ inline bool BeginOverlayDialog(const OverlayDialogSpec& spec)
|
||||
|
||||
// Content child.
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardX, cardY));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, floating ? 20.0f : 16.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, floating ? ImVec2(28, 20) : ImVec2(28, 24));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, floating ? 20.0f * dp : 16.0f * dp);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, floating ? ImVec2(28 * dp, 20 * dp) : ImVec2(28 * dp, 24 * dp));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0)); // transparent (glass/blur behind)
|
||||
// A card with a known height is a fixed frame (fixed-height dialogs, and auto-height dialogs whose
|
||||
// content overflowed the viewport); otherwise the child auto-resizes to its content.
|
||||
|
||||
@@ -116,6 +116,8 @@ struct SettingsPageState {
|
||||
LowSpecSnapshot low_spec_snapshot;
|
||||
bool keep_daemon_running = false;
|
||||
bool stop_external_daemon = false;
|
||||
bool stratum_host = false; // O2: host a RandomX stratum pool from the node (v1.3.0+)
|
||||
char stratum_allowip[64] = ""; // -stratumallowip subnet (blank = loopback only)
|
||||
bool lite_lifecycle_expanded = false;
|
||||
int lite_lifecycle_operation = 0;
|
||||
char lite_wallet_path[256] = "";
|
||||
@@ -420,6 +422,9 @@ static void loadSettingsPageState(config::Settings* settings) {
|
||||
Layout::setUserFontScale(s_settingsState.font_scale); // sync with Layout on load
|
||||
s_settingsState.keep_daemon_running = settings->getKeepDaemonRunning();
|
||||
s_settingsState.stop_external_daemon = settings->getStopExternalDaemon();
|
||||
s_settingsState.stratum_host = settings->getStratumHost();
|
||||
std::snprintf(s_settingsState.stratum_allowip, sizeof(s_settingsState.stratum_allowip), "%s",
|
||||
settings->getStratumAllowIp().c_str());
|
||||
// Lite-server selection is managed entirely by the Network tab (not the Settings page).
|
||||
s_settingsState.mine_when_idle = settings->getMineWhenIdle();
|
||||
s_settingsState.mine_idle_delay = settings->getMineIdleDelay();
|
||||
@@ -483,6 +488,8 @@ static void saveSettingsPageState(config::Settings* settings) {
|
||||
settings->setFontScale(s_settingsState.font_scale);
|
||||
settings->setKeepDaemonRunning(s_settingsState.keep_daemon_running);
|
||||
settings->setStopExternalDaemon(s_settingsState.stop_external_daemon);
|
||||
settings->setStratumHost(s_settingsState.stratum_host);
|
||||
settings->setStratumAllowIp(s_settingsState.stratum_allowip);
|
||||
// Lite-server selection is owned by the Network tab; the Settings page no longer writes it.
|
||||
settings->setMineWhenIdle(s_settingsState.mine_when_idle);
|
||||
settings->setMineIdleDelay(s_settingsState.mine_idle_delay);
|
||||
@@ -573,11 +580,11 @@ enum SettingsTab { TAB_APPEARANCE = 0, TAB_WALLET, TAB_BACKUP, TAB_NODE, TAB_EXP
|
||||
static void renderSettingsTabBar(float availWidth) {
|
||||
using namespace material;
|
||||
struct T { int id; const char* label; const char* idstr; };
|
||||
static const T tabs[] = {
|
||||
{TAB_APPEARANCE, "Appearance", "##stabA"}, {TAB_WALLET, "Wallet", "##stabW"},
|
||||
{TAB_BACKUP, "Backup & Data", "##stabB"}, {TAB_NODE, "Node & Security", "##stabN"},
|
||||
{TAB_EXPLORER, "Explorer", "##stabE"}, {TAB_CHAT, "Chat", "##stabC"},
|
||||
{TAB_ABOUT, "About", "##stabT"},
|
||||
const T tabs[] = {
|
||||
{TAB_APPEARANCE, TR("grpa_tab_appearance"), "##stabA"}, {TAB_WALLET, TR("grpa_tab_wallet"), "##stabW"},
|
||||
{TAB_BACKUP, TR("grpa_tab_backup_data"), "##stabB"}, {TAB_NODE, TR("grpa_tab_node_security"), "##stabN"},
|
||||
{TAB_EXPLORER, TR("grpa_tab_explorer"), "##stabE"}, {TAB_CHAT, TR("grpa_tab_chat"), "##stabC"},
|
||||
{TAB_ABOUT, TR("grpa_tab_about"), "##stabT"},
|
||||
};
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
ImFont* f = Type().body2();
|
||||
@@ -804,7 +811,7 @@ void RenderSettingsPage(App* app) {
|
||||
if (!skin.valid) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
|
||||
ImGui::BeginDisabled(true);
|
||||
std::string lbl = skin.name + " (invalid)";
|
||||
std::string lbl = skin.name + TR("grpa_invalid_suffix");
|
||||
ImGui::Selectable(lbl.c_str(), false);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleColor();
|
||||
@@ -1186,18 +1193,6 @@ void RenderSettingsPage(App* app) {
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_save_ztx"));
|
||||
CB(TrId("auto_shield", "auto_shld"), &s_settingsState.auto_shield);
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_auto_shield"));
|
||||
// O1: when a v1.3.0+ node auto-shields coinbase itself, show its real state — the checkbox
|
||||
// above only governs the wallet's own fallback shielder (which defers to the node). Nothing
|
||||
// renders on pre-1.3.0 daemons (never probed), so their behaviour is unchanged.
|
||||
if (app && app->daemonAutoShieldProbed()) {
|
||||
if (app->daemonAutoShieldActive()) {
|
||||
ImGui::TextColored(ImVec4(0.40f, 0.78f, 0.40f, 1.0f), " %s", TR("autoshield_by_node"));
|
||||
if (!app->daemonAutoShieldAddress().empty())
|
||||
ImGui::TextDisabled(" %s", app->daemonAutoShieldAddress().c_str());
|
||||
} else if (!app->daemonAutoShieldDisabledReason().empty()) {
|
||||
ImGui::TextDisabled(" %s", app->daemonAutoShieldDisabledReason().c_str());
|
||||
}
|
||||
}
|
||||
CB(TrId("use_tor", "tor"), &s_settingsState.use_tor);
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_tor"));
|
||||
if (showDaemonOptions) {
|
||||
@@ -1207,12 +1202,37 @@ void RenderSettingsPage(App* app) {
|
||||
if (CB(TrId("stop_external", "stop_ext"), &s_settingsState.stop_external_daemon))
|
||||
saveSettingsPageState(app->settings());
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_stop_external"));
|
||||
// (Stratum pool hosting lives in the Node & Security tab — it's a node feature.)
|
||||
}
|
||||
if (CB(TrId("verbose_logging", "verbose"), &s_settingsState.verbose_logging)) {
|
||||
dragonx::util::Logger::instance().setVerbose(s_settingsState.verbose_logging);
|
||||
saveSettingsPageState(app->settings());
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_verbose"));
|
||||
|
||||
// O1: node coinbase auto-shield status (v1.3.0+). Rendered as a full-width note BELOW the
|
||||
// checkbox grid (not wedged between checkboxes) with friendly wording — the daemon's raw
|
||||
// technical reason is available on hover. Nothing renders on pre-1.3.0 daemons (never probed).
|
||||
if (app && app->daemonAutoShieldProbed() &&
|
||||
(app->daemonAutoShieldActive() || !app->daemonAutoShieldDisabledReason().empty())) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx, last + gp));
|
||||
ImGui::PushTextWrapPos((cx + cw) - ImGui::GetWindowPos().x); // wrap to the card content width
|
||||
if (app->daemonAutoShieldActive()) {
|
||||
ImGui::TextColored(ImVec4(0.40f, 0.78f, 0.40f, 1.0f), "%s", TR("autoshield_by_node"));
|
||||
if (!app->daemonAutoShieldAddress().empty())
|
||||
ImGui::TextDisabled(" %s", app->daemonAutoShieldAddress().c_str());
|
||||
} else {
|
||||
// Friendly, actionable wording. The seed-not-recoverable case is the common one and has a
|
||||
// clear fix (back up the seed); anything else gets a generic line. Raw daemon text on hover.
|
||||
ImGui::TextDisabled("%s", app->daemonAutoShieldSeedRecoverable()
|
||||
? TR("autoshield_off_generic")
|
||||
: TR("autoshield_off_backup_seed"));
|
||||
if (ImGui::IsItemHovered())
|
||||
material::Tooltip("%s", app->daemonAutoShieldDisabledReason().c_str());
|
||||
}
|
||||
ImGui::PopTextWrapPos();
|
||||
last = ImGui::GetCursorScreenPos().y; // grow the card to include the note
|
||||
}
|
||||
cardClose(0, last);
|
||||
}
|
||||
|
||||
@@ -1670,7 +1690,7 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(TR("lite_birthday_label"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(std::min(160.0f, liteInputW));
|
||||
ImGui::SetNextItemWidth(std::min(160.0f * dp, liteInputW));
|
||||
ImGui::InputInt("##LiteRestoreBirthday", &s_settingsState.lite_restore_birthday);
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_lite_restore_birthday"));
|
||||
if (s_settingsState.lite_restore_birthday < 0) s_settingsState.lite_restore_birthday = 0;
|
||||
@@ -1682,7 +1702,7 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(TR("lite_account_label"));
|
||||
ImGui::SameLine(leftX - sectionOrigin.x + liteLabelW);
|
||||
ImGui::SetNextItemWidth(std::min(160.0f, liteInputW));
|
||||
ImGui::SetNextItemWidth(std::min(160.0f * dp, liteInputW));
|
||||
ImGui::InputInt("##LiteRestoreAccount", &s_settingsState.lite_restore_account);
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_lite_restore_account"));
|
||||
if (s_settingsState.lite_restore_account < 0) s_settingsState.lite_restore_account = 0;
|
||||
@@ -1862,7 +1882,7 @@ void RenderSettingsPage(App* app) {
|
||||
while (!liteKey.empty() && (liteKey.front()==' '||liteKey.front()=='\t'||liteKey.front()=='\n'||liteKey.front()=='\r')) liteKey.erase(liteKey.begin());
|
||||
while (!liteKey.empty() && (liteKey.back()==' '||liteKey.back()=='\t'||liteKey.back()=='\n'||liteKey.back()=='\r')) liteKey.pop_back();
|
||||
if (liteKey.empty()) {
|
||||
s_settingsState.lite_backup_status = "Enter a private key to import.";
|
||||
s_settingsState.lite_backup_status = TR("grpa_enter_private_key_to_import");
|
||||
} else {
|
||||
const auto r = app->liteWallet()->importKey(liteKey);
|
||||
sodium_memzero(s_settingsState.lite_import_key, sizeof(s_settingsState.lite_import_key));
|
||||
@@ -2623,6 +2643,10 @@ void RenderSettingsPage(App* app) {
|
||||
if (material::ActionButton("##aboutbug", TR("report_bug"), ICON_MD_BUG_REPORT, AT::Secondary))
|
||||
util::Platform::openUrl("https://git.dragonx.is/dragonx/ObsidianDragon/issues");
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_report_bug"));
|
||||
lf.next(material::ActionButtonWidth(TR("faq"), ICON_MD_QUESTION_MARK));
|
||||
if (material::ActionButton("##aboutfaq", TR("faq"), ICON_MD_QUESTION_MARK, AT::Secondary))
|
||||
app->showFaqDialog();
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("faq_open_tooltip"));
|
||||
}
|
||||
const float rightBottom = ImGui::GetCursorScreenPos().y;
|
||||
|
||||
@@ -2649,6 +2673,33 @@ void RenderSettingsPage(App* app) {
|
||||
if (s_settingsState.current_tab == TAB_NODE)
|
||||
ImGui::Dummy(ImVec2(0, gap));
|
||||
|
||||
// ====================================================================
|
||||
// MINING POOL HOSTING — run this node's built-in RandomX stratum server so other miners can point
|
||||
// at this machine. It's a node feature, so it belongs here (not the Wallet tab). Full-node only, and
|
||||
// only on a daemon new enough to implement -stratum (v1.3.0+, version encoded
|
||||
// major*1e6+minor*1e4+rev*100+build). Takes effect on the next daemon start/restart; a blank allow-IP
|
||||
// keeps it loopback-only (safe), a subnet opens it to that LAN.
|
||||
// ====================================================================
|
||||
if (app->supportsFullNodeLifecycleActions() && s_settingsState.current_tab == TAB_NODE &&
|
||||
app->state().daemon_version >= 1030000) {
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("stratum_host_section"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
|
||||
if (ImGui::Checkbox(TR("stratum_host"), &s_settingsState.stratum_host))
|
||||
saveSettingsPageState(app->settings());
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_stratum_host"));
|
||||
if (s_settingsState.stratum_host) {
|
||||
ImGui::TextDisabled(" %s", TR("stratum_host_hint"));
|
||||
ImGui::SetNextItemWidth(220.0f * Layout::dpiScale());
|
||||
if (ImGui::InputTextWithHint("##stratumallowip", TR("stratum_allowip_hint"),
|
||||
s_settingsState.stratum_allowip, sizeof(s_settingsState.stratum_allowip)))
|
||||
saveSettingsPageState(app->settings());
|
||||
if (s_settingsState.stratum_allowip[0] != '\0')
|
||||
ImGui::TextColored(ImVec4(0.95f, 0.75f, 0.25f, 1.0f), " %s",
|
||||
TR("stratum_expose_warn"));
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// DEBUG OPTIONS — collapsible card (full-node only: holds the screenshot sweep + the dragonxd
|
||||
// daemon debug= categories written to DRAGONX.conf; lite has no daemon). Shown on the Node tab.
|
||||
@@ -2711,7 +2762,7 @@ void RenderSettingsPage(App* app) {
|
||||
if (chat::hushChatFeatureEnabledAtBuild()) {
|
||||
// Populate the Chat tab with demo conversations so the sweep captures its real UI.
|
||||
ImGui::SameLine();
|
||||
if (TactileButton("Seed demo chat", ImVec2(0, 0), S.resolveFont("button")))
|
||||
if (TactileButton(TR("grpa_seed_demo_chat"), ImVec2(0, 0), S.resolveFont("button")))
|
||||
app->seedChatDemoData();
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_seed_demo_chat"));
|
||||
}
|
||||
@@ -2738,29 +2789,29 @@ void RenderSettingsPage(App* app) {
|
||||
"paymentdisclosure", "pow", "proxy", "prune", "rand",
|
||||
"reindex", "rpc", "selectcoins", "tor", "zmq", "zrpc"
|
||||
};
|
||||
static const char* debugTips[] = {
|
||||
"Peer address tracking and management",
|
||||
"Alert system messages",
|
||||
"Benchmark timings for operations",
|
||||
"Coin database read/write operations",
|
||||
"Berkeley DB operations",
|
||||
"Fee estimation algorithm",
|
||||
"HTTP RPC server activity",
|
||||
"Libevent networking library",
|
||||
"Lock contention debugging",
|
||||
"Transaction memory pool activity",
|
||||
"Network connections and messages",
|
||||
"Payment disclosure protocol",
|
||||
"Proof-of-work mining activity",
|
||||
"SOCKS5 proxy connections",
|
||||
"Block pruning operations",
|
||||
"Random number generation",
|
||||
"Blockchain reindexing progress",
|
||||
"RPC command processing",
|
||||
"Coin selection for transactions",
|
||||
"Tor integration and circuit info",
|
||||
"ZeroMQ notification system",
|
||||
"Shielded (z-addr) RPC operations"
|
||||
const char* debugTips[] = {
|
||||
TR("grpa_dbg_addrman"),
|
||||
TR("grpa_dbg_alert"),
|
||||
TR("grpa_dbg_bench"),
|
||||
TR("grpa_dbg_coindb"),
|
||||
TR("grpa_dbg_db"),
|
||||
TR("grpa_dbg_estimatefee"),
|
||||
TR("grpa_dbg_http"),
|
||||
TR("grpa_dbg_libevent"),
|
||||
TR("grpa_dbg_lock"),
|
||||
TR("grpa_dbg_mempool"),
|
||||
TR("grpa_dbg_net"),
|
||||
TR("grpa_dbg_paymentdisclosure"),
|
||||
TR("grpa_dbg_pow"),
|
||||
TR("grpa_dbg_proxy"),
|
||||
TR("grpa_dbg_prune"),
|
||||
TR("grpa_dbg_rand"),
|
||||
TR("grpa_dbg_reindex"),
|
||||
TR("grpa_dbg_rpc"),
|
||||
TR("grpa_dbg_selectcoins"),
|
||||
TR("grpa_dbg_tor"),
|
||||
TR("grpa_dbg_zmq"),
|
||||
TR("grpa_dbg_zrpc")
|
||||
};
|
||||
constexpr int numCats = sizeof(debugCats) / sizeof(debugCats[0]);
|
||||
|
||||
@@ -3010,7 +3061,7 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::TextWrapped("%s", TR("rescan_bootstrapped_msg"));
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("%s", TR("rescan_from_height"));
|
||||
ImGui::SetNextItemWidth(160.0f);
|
||||
ImGui::SetNextItemWidth(160.0f * dp);
|
||||
ImGui::InputInt("##rescanHeight", &s_settingsState.rescan_start_height);
|
||||
if (s_settingsState.rescan_start_height < 0) s_settingsState.rescan_start_height = 0;
|
||||
} else {
|
||||
@@ -3024,12 +3075,12 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Spacing();
|
||||
|
||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||
if (material::TactileButton(TrId("cancel", "rescan_cancel").c_str(), ImVec2(btnW, 40))) {
|
||||
if (material::TactileButton(TrId("cancel", "rescan_cancel").c_str(), ImVec2(btnW, 40 * dp))) {
|
||||
s_settingsState.confirm_rescan = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(detecting);
|
||||
if (material::TactileButton(TrId("rescan", "rescan_confirm").c_str(), ImVec2(btnW, 40))) {
|
||||
if (material::TactileButton(TrId("rescan", "rescan_confirm").c_str(), ImVec2(btnW, 40 * dp))) {
|
||||
if (bootstrapped) {
|
||||
app->runtimeRescan(s_settingsState.rescan_start_height);
|
||||
} else {
|
||||
|
||||
+23
-19
@@ -219,7 +219,7 @@ inline void DrawGlassCutout(ImDrawList* dl, ImVec2 mn, ImVec2 mx,
|
||||
float lineW = s_cc.lineW;
|
||||
|
||||
// --- Outer glow pass: wider, softer dark edge on top-left ---
|
||||
float glowExpand = s_cc.glowExpand;
|
||||
float glowExpand = s_cc.glowExpand * Layout::dpiScale();
|
||||
int glowA = (int)s_cc.glowAlpha;
|
||||
float glowLineW = s_cc.glowLineW;
|
||||
{
|
||||
@@ -289,6 +289,7 @@ inline void DrawGlassBevelButton(ImDrawList* dl, ImVec2 mn, ImVec2 mx,
|
||||
// inner pass gives crisp bevel edge. All use AddRect with rounding so
|
||||
// every layer follows the rounded corners perfectly — no clip rects needed.
|
||||
{
|
||||
const float dp = Layout::dpiScale();
|
||||
float cx = (mn.x + mx.x) * 0.5f;
|
||||
float cy = (mn.y + mx.y) * 0.5f;
|
||||
|
||||
@@ -304,7 +305,7 @@ inline void DrawGlassBevelButton(ImDrawList* dl, ImVec2 mn, ImVec2 mx,
|
||||
|
||||
struct BevelPass { float expand; float lineW; float fadeStart; float fadeEnd; };
|
||||
BevelPass passes[] = {
|
||||
{ 0.5f, 0.75f, 0.30f, 0.55f }, // Outer glow (thin)
|
||||
{ 0.5f * dp, 0.75f, 0.30f, 0.55f }, // Outer glow (thin)
|
||||
{ 0.0f, 0.75f, 0.38f, 0.58f }, // Inner crisp bevel
|
||||
};
|
||||
|
||||
@@ -387,7 +388,7 @@ inline void DrawGlassBevelButton(ImDrawList* dl, ImVec2 mn, ImVec2 mx,
|
||||
}
|
||||
}
|
||||
if (depth > s_ic.threshold) {
|
||||
float baseInset = s_ic.inset;
|
||||
float baseInset = s_ic.inset * Layout::dpiScale();
|
||||
int shadowMax = (int)(s_ic.maxAlpha * depth);
|
||||
float fadeRatio = s_ic.fadeRatio;
|
||||
float bW = mx.x - mn.x - baseInset * 2.0f;
|
||||
@@ -487,7 +488,7 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
float fixedH = stripH; // collapse strip
|
||||
for (int i = 0; i < (int)NavPage::Count_; ++i)
|
||||
if (IsNavPageVisible(kNavItems[i].page) && kNavItems[i].section_label && showLabels)
|
||||
fixedH += olFsz + 2.0f + sectionLabelPadBot; // section label + pad below
|
||||
fixedH += olFsz + 2.0f * dp + sectionLabelPadBot; // section label + pad below
|
||||
fixedH += bottomPadding + stripH; // exit area
|
||||
|
||||
float baseFlexH = baseNavGap;
|
||||
@@ -525,7 +526,7 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
if (showLabels) {
|
||||
curY += sectionGap;
|
||||
if (nSectionLabels < 4) sectionLabelY[nSectionLabels++] = curY;
|
||||
curY += olFsz + 2.0f + sectionLabelPadBot;
|
||||
curY += olFsz + 2.0f * dp + sectionLabelPadBot;
|
||||
} else {
|
||||
curY += sectionGap * 0.4f;
|
||||
if (nSeparators < 4) separatorY[nSeparators++] = curY;
|
||||
@@ -653,7 +654,7 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
fx.drawShimmer(dl, indMin, indMax, btnRnd);
|
||||
fx.drawGradientBorderShift(dl, indMin, indMax, btnRnd);
|
||||
}
|
||||
DrawGlassCutout(dl, indMin, indMax, btnRnd, 1.5f);
|
||||
DrawGlassCutout(dl, indMin, indMax, btnRnd, 1.5f * dp);
|
||||
DrawGlassBevelButton(dl, indMin, indMax, btnRnd, btnDepth, 18);
|
||||
buttonRects.push_back({indMin, indMax, btnRnd});
|
||||
}
|
||||
@@ -678,16 +679,18 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
ImU32 textCol = selected ? Primary() : (pageNeedsUnlock ? OnSurfaceDisabled() : OnSurfaceMedium());
|
||||
|
||||
if (showLabels) {
|
||||
// Reserve room for a badge (if this item will draw one) so the
|
||||
// label centers in the space to the left of it instead of
|
||||
// running underneath the badge circle.
|
||||
bool itemHasBadge =
|
||||
(item.page == NavPage::History && status.unconfirmedTxCount > 0) ||
|
||||
(item.page == NavPage::Mining && status.miningActive) ||
|
||||
(item.page == NavPage::Peers && status.peerCount > 0) ||
|
||||
(item.page == NavPage::Chat && status.chatUnreadCount > 0);
|
||||
// The badge is a fixed top-right corner overlay, so it must NOT move
|
||||
// the icon+label — otherwise the text jumps sideways the moment a live
|
||||
// count toggles the badge on/off. Reserve clearance from whether the
|
||||
// page CAN show a badge (constant per item), never from the current
|
||||
// count, and keep the icon+label centered in the FULL button width so
|
||||
// the text position and size stay identical with or without a badge.
|
||||
bool itemBadgeCapable =
|
||||
item.page == NavPage::History ||
|
||||
item.page == NavPage::Mining ||
|
||||
item.page == NavPage::Chat;
|
||||
float badgeReserve = 0.0f;
|
||||
if (itemHasBadge) {
|
||||
if (itemBadgeCapable) {
|
||||
bool dotOnlyReserve = (item.page == NavPage::Mining);
|
||||
float badgeRReserve = dotOnlyReserve ? badgeRadiusDot : badgeRadiusNumber;
|
||||
float badgeInsetXReserve = sde("badge-inset-x", 6.0f);
|
||||
@@ -697,14 +700,17 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
ImFont* font = selected ? Type().subtitle2() : Type().body2();
|
||||
float lblFsz = ScaledFontSize(font);
|
||||
float btnW = indMax.x - indMin.x;
|
||||
float maxLabelW = btnW - iconS * 2.0f - iconLabelGap - Layout::spacingXs() * 2 - badgeReserve;
|
||||
// Clearance is symmetric (2x) because the group stays centered in the
|
||||
// full width: reserving on both sides keeps the label's right edge clear
|
||||
// of the right-side corner badge without shifting the center off-axis.
|
||||
float maxLabelW = btnW - iconS * 2.0f - iconLabelGap - Layout::spacingXs() * 2 - badgeReserve * 2.0f;
|
||||
ImVec2 labelSz = font->CalcTextSizeA(lblFsz, 1000.0f, 0.0f, NavLabel(item));
|
||||
if (labelSz.x > maxLabelW && maxLabelW > 0) {
|
||||
lblFsz *= maxLabelW / labelSz.x;
|
||||
labelSz = font->CalcTextSizeA(lblFsz, 1000.0f, 0.0f, NavLabel(item));
|
||||
}
|
||||
float totalW = iconS * 2.0f + iconLabelGap + labelSz.x;
|
||||
float btnCX = (indMin.x + indMax.x - badgeReserve) * 0.5f;
|
||||
float btnCX = (indMin.x + indMax.x) * 0.5f;
|
||||
float startX = btnCX - totalW * 0.5f;
|
||||
|
||||
DrawNavIcon(dl, item.page, startX + iconS, iconCY, iconS, textCol);
|
||||
@@ -732,8 +738,6 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
badgeCol = Warning(); badgeTextCol = OnWarning();
|
||||
} else if (item.page == NavPage::Mining && status.miningActive) {
|
||||
dotOnly = true; badgeCol = Success();
|
||||
} else if (item.page == NavPage::Peers && status.peerCount > 0) {
|
||||
badgeCount = status.peerCount;
|
||||
} else if (item.page == NavPage::Chat && status.chatUnreadCount > 0) {
|
||||
badgeCount = status.chatUnreadCount;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
// Arrow
|
||||
{
|
||||
float arrowCX = ImGui::GetContentRegionAvail().x * 0.5f;
|
||||
ImGui::SetCursorPosX(arrowCX - 8.0f);
|
||||
ImGui::SetCursorPosX(arrowCX - 8.0f * dp);
|
||||
ImFont* iconFont = Type().iconMed();
|
||||
float fsz = ScaledFontSize(iconFont);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
@@ -312,7 +312,8 @@ private:
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
float btnW = 120.0f;
|
||||
const float dp = Layout::dpiScale();
|
||||
float btnW = 120.0f * dp;
|
||||
ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x - btnW) * 0.5f);
|
||||
if (TactileButton(TR("close"), ImVec2(btnW, 0))) {
|
||||
s_open = false;
|
||||
|
||||
@@ -264,7 +264,7 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
||||
} else if (rows.empty()) {
|
||||
float cw = ImGui::GetContentRegionAvail().x;
|
||||
float ch = ImGui::GetContentRegionAvail().y;
|
||||
if (ch < 60) ch = 60;
|
||||
if (ch < 60.0f * dp) ch = 60.0f * dp;
|
||||
const char* emptyMsg = addr_search[0] ? TR("no_addresses_match") : TR("no_addresses_yet");
|
||||
ImVec2 msgSz = ImGui::CalcTextSize(emptyMsg);
|
||||
ImGui::SetCursorPosX((cw - msgSz.x) * 0.5f);
|
||||
|
||||
@@ -302,9 +302,9 @@ static void RenderBalanceClassic(App* app)
|
||||
float cardPadLg = (classicPadOverride >= 0.0f) ? classicPadOverride : Layout::spacingLg();
|
||||
|
||||
// Card height: must fit the Market card's content (overline + price + 24h)
|
||||
const float ovGap = S.drawElement("tabs.balance", "overline-value-gap").sizeOr(6.0f);
|
||||
const float valGap = S.drawElement("tabs.balance", "value-caption-gap").sizeOr(4.0f);
|
||||
const float tickGap = S.drawElement("tabs.balance.classic", "ticker-gap").sizeOr(4.0f);
|
||||
const float ovGap = S.drawElement("tabs.balance", "overline-value-gap").sizeOr(6.0f) * dp;
|
||||
const float valGap = S.drawElement("tabs.balance", "value-caption-gap").sizeOr(4.0f) * dp;
|
||||
const float tickGap = S.drawElement("tabs.balance.classic", "ticker-gap").sizeOr(4.0f) * dp;
|
||||
float marketContentH = cardPadLg
|
||||
+ ovFont->LegacySize + ovGap
|
||||
+ sub1->LegacySize + 2.0f * dp
|
||||
@@ -323,7 +323,7 @@ static void RenderBalanceClassic(App* app)
|
||||
// Helper: draw accent stripe on left edge, clipped to card rounded corners.
|
||||
// We draw a full-size rounded rect (left corners only) and clip it to the
|
||||
// stripe width so the shape itself follows the card rounding.
|
||||
const float accentW = S.drawElement("tabs.balance", "accent-width").sizeOr(4.0f);
|
||||
const float accentW = S.drawElement("tabs.balance", "accent-width").sizeOr(4.0f) * dp;
|
||||
auto drawAccent = [&](const ImVec2& cMin, const ImVec2& cMax, ImU32 col) {
|
||||
dl->PushClipRect(cMin, ImVec2(cMin.x + accentW, cMax.y), true);
|
||||
dl->AddRectFilled(cMin, cMax, col, cardSpec.rounding,
|
||||
@@ -446,7 +446,7 @@ static void RenderBalanceClassic(App* app)
|
||||
ImU32 bd = WithAlpha(fg, 90);
|
||||
snprintf(buf, sizeof(buf), "%s %s",
|
||||
TR("data_stale_prefix"), timeAgo(state.last_balance_update).c_str());
|
||||
ImVec2 pillSz = DrawPill(dl, ImVec2(cx, cy), buf, capFont, fg, bg, bd);
|
||||
ImVec2 pillSz = DrawPill(dl, ImVec2(cx, cy), buf, capFont, fg, bg, bd, ImVec2(4.0f * dp, 2.0f * dp));
|
||||
// Hover → explain what stale means and how old the data actually is.
|
||||
if (material::IsRectHovered(ImVec2(cx, cy), ImVec2(cx + pillSz.x, cy + pillSz.y)))
|
||||
Tooltip("%s", TR("data_stale_tooltip"));
|
||||
@@ -456,7 +456,7 @@ static void RenderBalanceClassic(App* app)
|
||||
// Hover glow
|
||||
if (material::IsRectHovered(cMin, cMax)) {
|
||||
dl->AddRect(cMin, cMax, IM_COL32(255, 255, 255, (int)S.drawElement("tabs.balance", "hover-glow-alpha").sizeOr(40.0f)),
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f));
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f) * dp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ static void RenderBalanceClassic(App* app)
|
||||
{
|
||||
float privPct = (s_dispTotal > 1e-9)
|
||||
? (float)(s_dispShielded / s_dispTotal * 100.0) : 0.0f;
|
||||
snprintf(buf, sizeof(buf), "%.0f%% of total · %d Z-addr",
|
||||
snprintf(buf, sizeof(buf), TR("baltab_pct_of_total_zaddr"),
|
||||
privPct, (int)state.z_addresses.size());
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy),
|
||||
WithAlpha(Success(), 160), buf);
|
||||
@@ -498,8 +498,8 @@ static void RenderBalanceClassic(App* app)
|
||||
snprintf(buf, sizeof(buf), "+%.4f", state.unconfirmed_balance);
|
||||
ImVec2 ts = capFont->CalcTextSizeA(
|
||||
capFont->LegacySize, 10000, 0, buf);
|
||||
float bp = S.drawElement("tabs.balance.classic", "unconfirmed-badge-padding").sizeOr(4.0f);
|
||||
float br = S.drawElement("tabs.balance.classic", "unconfirmed-badge-rounding").sizeOr(4.0f);
|
||||
float bp = S.drawElement("tabs.balance.classic", "unconfirmed-badge-padding").sizeOr(4.0f) * dp;
|
||||
float br = S.drawElement("tabs.balance.classic", "unconfirmed-badge-rounding").sizeOr(4.0f) * dp;
|
||||
ImVec2 bMin(cMax.x - ts.x - bp * 3,
|
||||
cMin.y + cardPadLg);
|
||||
ImVec2 bMax(cMax.x - bp, bMin.y + ts.y + bp);
|
||||
@@ -513,7 +513,7 @@ static void RenderBalanceClassic(App* app)
|
||||
// Hover glow + click to Receive
|
||||
if (material::IsRectHovered(cMin, cMax)) {
|
||||
dl->AddRect(cMin, cMax, IM_COL32(255, 255, 255, (int)S.drawElement("tabs.balance", "hover-glow-alpha").sizeOr(40.0f)),
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f));
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f) * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (ImGui::IsMouseClicked(0))
|
||||
app->setCurrentPage(NavPage::Receive);
|
||||
@@ -543,7 +543,7 @@ static void RenderBalanceClassic(App* app)
|
||||
OnSurfaceMedium(), DRAGONX_TICKER);
|
||||
cy += sub1->LegacySize + valGap;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%d T-addresses",
|
||||
snprintf(buf, sizeof(buf), TR("baltab_t_addresses_count"),
|
||||
(int)state.t_addresses.size());
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(cx, cy),
|
||||
OnSurfaceDisabled(), buf);
|
||||
@@ -551,7 +551,7 @@ static void RenderBalanceClassic(App* app)
|
||||
// Hover glow + click to Receive
|
||||
if (material::IsRectHovered(cMin, cMax)) {
|
||||
dl->AddRect(cMin, cMax, IM_COL32(255, 255, 255, (int)S.drawElement("tabs.balance", "hover-glow-alpha").sizeOr(40.0f)),
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f));
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f) * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (ImGui::IsMouseClicked(0))
|
||||
app->setCurrentPage(NavPage::Receive);
|
||||
@@ -589,7 +589,7 @@ static void RenderBalanceClassic(App* app)
|
||||
[](unsigned char c){ return (char)std::toupper(c); });
|
||||
float textW = std::max(pSz.x + tickGap + usdSz.x,
|
||||
ovFont->CalcTextSizeA(ovFont->LegacySize, 10000, 0, marketLabel.c_str()).x);
|
||||
float sparkGap = S.drawElement("tabs.balance.classic", "sparkline-gap").sizeOr(12.0f);
|
||||
float sparkGap = S.drawElement("tabs.balance.classic", "sparkline-gap").sizeOr(12.0f) * dp;
|
||||
float sparkLeft = cx + textW + sparkGap;
|
||||
float sparkRight = cMax.x - cardPadLg;
|
||||
|
||||
@@ -611,7 +611,7 @@ static void RenderBalanceClassic(App* app)
|
||||
bool pos = market.change_24h >= 0;
|
||||
ImU32 chgCol = pos ? Success()
|
||||
: Error();
|
||||
snprintf(buf, sizeof(buf), "%s%.1f%% 24h",
|
||||
snprintf(buf, sizeof(buf), TR("baltab_pct_change_24h"),
|
||||
pos ? "+" : "", market.change_24h);
|
||||
dl->AddText(capFont, capFont->LegacySize,
|
||||
ImVec2(cx, cy), chgCol, buf);
|
||||
@@ -633,7 +633,7 @@ static void RenderBalanceClassic(App* app)
|
||||
// Hover glow + click to Market
|
||||
if (material::IsRectHovered(cMin, cMax)) {
|
||||
dl->AddRect(cMin, cMax, IM_COL32(255, 255, 255, (int)S.drawElement("tabs.balance", "hover-glow-alpha").sizeOr(40.0f)),
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f));
|
||||
cardSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f) * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (ImGui::IsMouseClicked(0))
|
||||
app->setCurrentPage(NavPage::Market);
|
||||
@@ -790,20 +790,20 @@ static void RenderBalanceDonut(App* app) {
|
||||
ImFont* capFont = Type().caption();
|
||||
ImFont* body2 = Type().body2();
|
||||
|
||||
float legendDotR = S.drawElement("tabs.balance.donut", "legend-dot-radius").sizeOr(4.0f);
|
||||
float legendXOff = S.drawElement("tabs.balance.donut", "legend-x-offset").sizeOr(14.0f);
|
||||
float legendLineGap = S.drawElement("tabs.balance.donut", "legend-line-gap").sizeOr(6.0f);
|
||||
float legendSectionGap = S.drawElement("tabs.balance.donut", "legend-section-gap").sizeOr(10.0f);
|
||||
float legendDotR = S.drawElement("tabs.balance.donut", "legend-dot-radius").sizeOr(4.0f) * dp;
|
||||
float legendXOff = S.drawElement("tabs.balance.donut", "legend-x-offset").sizeOr(14.0f) * dp;
|
||||
float legendLineGap = S.drawElement("tabs.balance.donut", "legend-line-gap").sizeOr(6.0f) * dp;
|
||||
float legendSectionGap = S.drawElement("tabs.balance.donut", "legend-section-gap").sizeOr(10.0f) * dp;
|
||||
|
||||
// Shielded legend
|
||||
dl->AddCircleFilled(ImVec2(legendX + 5 * dp, legendY + capFont->LegacySize * 0.5f), legendDotR, Success());
|
||||
snprintf(buf, sizeof(buf), "Shielded %.8f", s_dispShielded);
|
||||
snprintf(buf, sizeof(buf), TR("baltab_shielded_amount"), s_dispShielded);
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(legendX + legendXOff, legendY), Success(), buf);
|
||||
legendY += capFont->LegacySize + legendLineGap;
|
||||
|
||||
// Transparent legend
|
||||
dl->AddCircleFilled(ImVec2(legendX + 5 * dp, legendY + capFont->LegacySize * 0.5f), legendDotR, Warning());
|
||||
snprintf(buf, sizeof(buf), "Transparent %.8f", s_dispTransparent);
|
||||
snprintf(buf, sizeof(buf), TR("baltab_transparent_amount"), s_dispTransparent);
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(legendX + legendXOff, legendY), Warning(), buf);
|
||||
legendY += capFont->LegacySize + legendSectionGap;
|
||||
|
||||
@@ -811,15 +811,15 @@ static void RenderBalanceDonut(App* app) {
|
||||
const auto& market = state.market;
|
||||
if (market.price_usd > 0) {
|
||||
if (market.price_usd >= 0.01)
|
||||
snprintf(buf, sizeof(buf), "Market: $%.4f", market.price_usd);
|
||||
snprintf(buf, sizeof(buf), TR("baltab_market_price_4dp"), market.price_usd);
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "Market: $%.8f", market.price_usd);
|
||||
snprintf(buf, sizeof(buf), TR("baltab_market_price_8dp"), market.price_usd);
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(legendX + legendXOff, legendY),
|
||||
OnSurfaceMedium(), buf);
|
||||
legendY += capFont->LegacySize + 4 * dp;
|
||||
|
||||
bool pos = market.change_24h >= 0;
|
||||
snprintf(buf, sizeof(buf), "%s%.1f%% 24h", pos ? "+" : "", market.change_24h);
|
||||
snprintf(buf, sizeof(buf), TR("baltab_pct_change_24h"), pos ? "+" : "", market.change_24h);
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(legendX + legendXOff, legendY),
|
||||
pos ? Success() : Error(), buf);
|
||||
}
|
||||
@@ -946,7 +946,7 @@ static void RenderBalanceConsolidated(App* app) {
|
||||
float divY = cardMin.y + cardH * S.drawElement("tabs.balance.consolidated", "divider-y-ratio").sizeOr(0.55f);
|
||||
dl->AddLine(ImVec2(cardMin.x + pad, divY), ImVec2(cardMax.x - pad, divY),
|
||||
IM_COL32(255, 255, 255, (int)S.drawElement("tabs.balance.consolidated", "divider-alpha").sizeOr(20.0f)),
|
||||
S.drawElement("tabs.balance.consolidated", "divider-thickness").sizeOr(1.0f));
|
||||
S.drawElement("tabs.balance.consolidated", "divider-thickness").sizeOr(1.0f) * dp);
|
||||
|
||||
// Bottom half: proportion bars
|
||||
float barY = divY + Layout::spacingSm();
|
||||
@@ -1122,7 +1122,7 @@ static void RenderBalanceDashboard(App* app) {
|
||||
|
||||
// Accent stripe — clipped to tile rounded corners
|
||||
{
|
||||
float aw = S.drawElement("tabs.balance", "accent-width").sizeOr(4.0f);
|
||||
float aw = S.drawElement("tabs.balance", "accent-width").sizeOr(4.0f) * dp;
|
||||
dl->PushClipRect(tMin, ImVec2(tMin.x + aw, tMax.y), true);
|
||||
dl->AddRectFilled(tMin, tMax, tiles[i].accent, tileSpec.rounding,
|
||||
ImDrawFlags_RoundCornersLeft);
|
||||
@@ -1159,7 +1159,7 @@ static void RenderBalanceDashboard(App* app) {
|
||||
// Click
|
||||
if (material::IsRectHovered(tMin, tMax)) {
|
||||
dl->AddRect(tMin, tMax, IM_COL32(255, 255, 255, (int)S.drawElement("tabs.balance", "hover-glow-alpha").sizeOr(40.0f)),
|
||||
tileSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f));
|
||||
tileSpec.rounding, 0, S.drawElement("tabs.balance", "hover-glow-thickness").sizeOr(1.5f) * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
if (ImGui::IsMouseClicked(0))
|
||||
app->setCurrentPage(tiles[i].nav);
|
||||
@@ -1208,7 +1208,7 @@ static void RenderBalanceVerticalStack(App* app) {
|
||||
// Font-content floor per row: icon + label + value must fit
|
||||
float vstackRowFontFloor = std::max(body2->LegacySize, capFont->LegacySize)
|
||||
+ Layout::spacingSm() * 2;
|
||||
float rowGap = S.drawElement("tabs.balance.vertical-stack", "row-gap").sizeOr(2.0f);
|
||||
float rowGap = S.drawElement("tabs.balance.vertical-stack", "row-gap").sizeOr(2.0f) * dp;
|
||||
float vstackFontFloor = vstackRowFontFloor * 4 + rowGap * 3;
|
||||
float vstackCardH = S.drawElement("tabs.balance.vertical-stack", "card-height").size;
|
||||
float stackH;
|
||||
@@ -1238,10 +1238,10 @@ static void RenderBalanceVerticalStack(App* app) {
|
||||
};
|
||||
|
||||
RowInfo rowInfos[4] = {
|
||||
{"Total Balance", ICON_MD_ACCOUNT_BALANCE_WALLET, S.resolveColor("var(--accent-total)", OnSurface()), s_dispTotal, 1.0f},
|
||||
{"Shielded", ICON_MD_SHIELD, S.resolveColor("var(--accent-shielded)", Success()), s_dispShielded, shieldRatio},
|
||||
{"Transparent", ICON_MD_CIRCLE, S.resolveColor("var(--accent-transparent)", Warning()), s_dispTransparent, transRatio},
|
||||
{"Market", ICON_MD_TRENDING_UP, S.resolveColor("var(--accent-action)", Primary()), state.market.price_usd, 0.0f},
|
||||
{TR("baltab_total_balance"), ICON_MD_ACCOUNT_BALANCE_WALLET, S.resolveColor("var(--accent-total)", OnSurface()), s_dispTotal, 1.0f},
|
||||
{TR("baltab_shielded"), ICON_MD_SHIELD, S.resolveColor("var(--accent-shielded)", Success()), s_dispShielded, shieldRatio},
|
||||
{TR("baltab_transparent"), ICON_MD_CIRCLE, S.resolveColor("var(--accent-transparent)", Warning()), s_dispTransparent, transRatio},
|
||||
{TR("baltab_market"), ICON_MD_TRENDING_UP, S.resolveColor("var(--accent-action)", Primary()), state.market.price_usd, 0.0f},
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -1296,7 +1296,7 @@ static void RenderBalanceVerticalStack(App* app) {
|
||||
// Proportion bar (for shielded/transparent rows — fills gap between label and amount)
|
||||
if (i == 1 || i == 2) {
|
||||
ImVec2 labelSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, rowInfos[i].label);
|
||||
float barGap = S.drawElement("tabs.balance.vertical-stack", "sparkline-gap").sizeOr(12.0f);
|
||||
float barGap = S.drawElement("tabs.balance.vertical-stack", "sparkline-gap").sizeOr(12.0f) * dp;
|
||||
float barPad = S.drawElement("tabs.balance.vertical-stack", "sparkline-pad").sizeOr(4.0f);
|
||||
float barH = std::max(
|
||||
S.drawElement("tabs.balance.vertical-stack", "bar-min-height").sizeOr(3.0f),
|
||||
@@ -1326,8 +1326,8 @@ static void RenderBalanceVerticalStack(App* app) {
|
||||
// Sparkline in the gap between label and 24h change
|
||||
if (state.market.price_history.size() >= 2) {
|
||||
ImVec2 labelSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, rowInfos[i].label);
|
||||
float sparkGap = S.drawElement("tabs.balance.vertical-stack", "sparkline-gap").sizeOr(12.0f);
|
||||
float sparkPad = S.drawElement("tabs.balance.vertical-stack", "sparkline-pad").sizeOr(4.0f);
|
||||
float sparkGap = S.drawElement("tabs.balance.vertical-stack", "sparkline-gap").sizeOr(12.0f) * dp;
|
||||
float sparkPad = S.drawElement("tabs.balance.vertical-stack", "sparkline-pad").sizeOr(4.0f) * dp;
|
||||
float sparkLeft = px + labelSz.x + sparkGap;
|
||||
float sparkRight = chgX - sparkGap;
|
||||
if (sparkLeft < sparkRight) {
|
||||
@@ -1387,8 +1387,8 @@ static void RenderBalanceVertical2x2(App* app) {
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
// Font-content floor per row: caption text + vertical padding
|
||||
float v2x2RowFontFloor = capFont->LegacySize + Layout::spacingSm() * 2;
|
||||
float rowGap = S.drawElement(cfgSec, "row-gap").sizeOr(2.0f);
|
||||
float colGap = S.drawElement(cfgSec, "col-gap").sizeOr(8.0f);
|
||||
float rowGap = S.drawElement(cfgSec, "row-gap").sizeOr(2.0f) * dp;
|
||||
float colGap = S.drawElement(cfgSec, "col-gap").sizeOr(8.0f) * dp;
|
||||
float v2x2FontFloor = v2x2RowFontFloor * 2 + rowGap;
|
||||
float cardHOverride = S.drawElement(cfgSec, "card-height").size;
|
||||
float stackH;
|
||||
@@ -1431,13 +1431,13 @@ static void RenderBalanceVertical2x2(App* app) {
|
||||
CellInfo cells[2][2] = {
|
||||
// Row 0: Total Balance (left), Shielded (right)
|
||||
{
|
||||
{"Total Balance", ICON_MD_ACCOUNT_BALANCE_WALLET, S.resolveColor("var(--accent-total)", OnSurface()), s_dispTotal, 1.0f, false, false},
|
||||
{"Shielded", ICON_MD_SHIELD, S.resolveColor("var(--accent-shielded)", Success()), s_dispShielded, shieldRatio, false, true},
|
||||
{TR("baltab_total_balance"), ICON_MD_ACCOUNT_BALANCE_WALLET, S.resolveColor("var(--accent-total)", OnSurface()), s_dispTotal, 1.0f, false, false},
|
||||
{TR("baltab_shielded"), ICON_MD_SHIELD, S.resolveColor("var(--accent-shielded)", Success()), s_dispShielded, shieldRatio, false, true},
|
||||
},
|
||||
// Row 1: Market (left), Transparent (right)
|
||||
{
|
||||
{"Market", ICON_MD_TRENDING_UP, S.resolveColor("var(--accent-action)", Primary()), state.market.price_usd, 0.0f, true, false},
|
||||
{"Transparent", ICON_MD_CIRCLE, S.resolveColor("var(--accent-transparent)", Warning()), s_dispTransparent, transRatio, false, true},
|
||||
{TR("baltab_market"), ICON_MD_TRENDING_UP, S.resolveColor("var(--accent-action)", Primary()), state.market.price_usd, 0.0f, true, false},
|
||||
{TR("baltab_transparent"), ICON_MD_CIRCLE, S.resolveColor("var(--accent-transparent)", Warning()), s_dispTransparent, transRatio, false, true},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1519,8 +1519,8 @@ static void RenderBalanceVertical2x2(App* app) {
|
||||
// Sparkline between label and 24h change
|
||||
if (state.market.price_history.size() >= 2) {
|
||||
ImVec2 labelSz = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, cell.label);
|
||||
float sparkGap = S.drawElement(cfgSec, "sparkline-gap").sizeOr(12.0f);
|
||||
float sparkPad = S.drawElement(cfgSec, "sparkline-pad").sizeOr(4.0f);
|
||||
float sparkGap = S.drawElement(cfgSec, "sparkline-gap").sizeOr(12.0f) * dp;
|
||||
float sparkPad = S.drawElement(cfgSec, "sparkline-pad").sizeOr(4.0f) * dp;
|
||||
float sparkLeft = px + labelSz.x + sparkGap;
|
||||
float sparkRight = chgX - sparkGap;
|
||||
if (sparkLeft < sparkRight) {
|
||||
@@ -1665,7 +1665,7 @@ static void RenderBalanceShield(App* app) {
|
||||
ImVec2 needleTip(gaugeCx + cosf(needleAngle) * needleLen,
|
||||
gaugeCy + sinf(needleAngle) * needleLen);
|
||||
dl->AddLine(ImVec2(gaugeCx, gaugeCy), needleTip, gaugeCol,
|
||||
S.drawElement("tabs.balance.shield", "needle-thickness").sizeOr(2.0f));
|
||||
S.drawElement("tabs.balance.shield", "needle-thickness").sizeOr(2.0f) * dp);
|
||||
|
||||
// Center text: percentage
|
||||
ImFont* sub1 = Type().subtitle1();
|
||||
@@ -1956,7 +1956,7 @@ static void RenderBalanceTwoRow(App* app) {
|
||||
}
|
||||
|
||||
RenderSyncBar(app, dl, vs);
|
||||
ImGui::Dummy(ImVec2(0, S.drawElement("tabs.balance.two-row", "sync-gap").sizeOr(2.0f)));
|
||||
ImGui::Dummy(ImVec2(0, S.drawElement("tabs.balance.two-row", "sync-gap").sizeOr(2.0f) * dp));
|
||||
|
||||
// Row 2: 3 mini-cards inline
|
||||
{
|
||||
@@ -1979,7 +1979,7 @@ static void RenderBalanceTwoRow(App* app) {
|
||||
S.drawElement("tabs.balance.two-row", "mini-rounding-min").sizeOr(4.0f),
|
||||
glassRound * S.drawElement("tabs.balance.two-row", "mini-rounding-ratio").sizeOr(0.5f));
|
||||
ImFont* capFont = Type().caption();
|
||||
float indicatorR = S.drawElement("tabs.balance.two-row", "indicator-radius").sizeOr(3.0f);
|
||||
float indicatorR = S.drawElement("tabs.balance.two-row", "indicator-radius").sizeOr(3.0f) * dp;
|
||||
int balDecimals = (int)S.drawElement("tabs.balance.two-row", "balance-decimals").sizeOr(4.0f);
|
||||
float twoRowPadOverride = S.drawElement("tabs.balance.two-row", "card-padding").size;
|
||||
float miniPad = (twoRowPadOverride >= 0.0f) ? twoRowPadOverride : Layout::spacingSm();
|
||||
@@ -2054,8 +2054,8 @@ static void RenderBalanceTwoRow(App* app) {
|
||||
|
||||
// Sparkline between price and percentage
|
||||
if (market.price_history.size() >= 2) {
|
||||
float sparkGap = S.drawElement("tabs.balance.two-row", "sparkline-gap").sizeOr(6.0f);
|
||||
float sparkPad = S.drawElement("tabs.balance.two-row", "sparkline-pad").sizeOr(4.0f);
|
||||
float sparkGap = S.drawElement("tabs.balance.two-row", "sparkline-gap").sizeOr(6.0f) * dp;
|
||||
float sparkPad = S.drawElement("tabs.balance.two-row", "sparkline-pad").sizeOr(4.0f) * dp;
|
||||
float sparkLeft = cx + priceSz.x + sparkGap;
|
||||
float sparkRightEdge = sparkRight - sparkGap;
|
||||
if (sparkLeft < sparkRightEdge) {
|
||||
@@ -2153,10 +2153,10 @@ static void RenderBalanceMinimal(App* app) {
|
||||
{
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImVec2 sepPos = ImGui::GetCursorScreenPos();
|
||||
float dashLen = S.drawElement("tabs.balance.minimal", "dash-length").sizeOr(6.0f);
|
||||
float gapLen = S.drawElement("tabs.balance.minimal", "dash-gap").sizeOr(4.0f);
|
||||
float dashLen = S.drawElement("tabs.balance.minimal", "dash-length").sizeOr(6.0f) * dp;
|
||||
float gapLen = S.drawElement("tabs.balance.minimal", "dash-gap").sizeOr(4.0f) * dp;
|
||||
float sepAlpha = S.drawElement("tabs.balance.minimal", "separator-alpha").sizeOr(25.0f);
|
||||
float sepThick = S.drawElement("tabs.balance.minimal", "separator-thickness").sizeOr(1.0f);
|
||||
float sepThick = S.drawElement("tabs.balance.minimal", "separator-thickness").sizeOr(1.0f) * dp;
|
||||
float x = sepPos.x;
|
||||
float endX = sepPos.x + availW;
|
||||
while (x < endX) {
|
||||
|
||||
@@ -59,7 +59,7 @@ static void handleBlockResponseUnified(const json& result, const std::string& er
|
||||
s_loading = false;
|
||||
|
||||
if (!error.empty()) {
|
||||
s_error = "Error: " + error;
|
||||
s_error = std::string(TR("grpa_error_prefix")) + error;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ static void handleBlockResponseUnified(const json& result, const std::string& er
|
||||
|
||||
s_has_data = true;
|
||||
} else {
|
||||
s_error = "Invalid response from daemon";
|
||||
s_error = TR("grpa_invalid_response_from_daemon");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ void BlockInfoDialog::render(App* app)
|
||||
|
||||
// Current block info
|
||||
if (state.sync.blocks > 0) {
|
||||
ImGui::TextDisabled("(Current: %d)", state.sync.blocks);
|
||||
ImGui::TextDisabled(TR("grpa_current_block_paren"), state.sync.blocks);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
@@ -152,7 +152,7 @@ void BlockInfoDialog::render(App* app)
|
||||
rpc::RPCClient::TraceScope trace("Explorer / Block info");
|
||||
auto hashResult = rpc->call("getblockhash", {height});
|
||||
if (!hashResult.is_string()) {
|
||||
error = "unexpected getblockhash result";
|
||||
error = TR("grpa_unexpected_getblockhash_result");
|
||||
} else {
|
||||
block = rpc->call("getblock", {hashResult.get<std::string>()});
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ private:
|
||||
|
||||
if (!s_bootstrap) {
|
||||
s_state = State::Failed;
|
||||
s_errorMsg = "Bootstrap not initialized";
|
||||
s_errorMsg = TR("grpc_bootstrap_not_initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ private:
|
||||
s_state = State::Done;
|
||||
} else {
|
||||
s_errorMsg = finalProg.error;
|
||||
if (s_errorMsg.empty()) s_errorMsg = "Bootstrap failed";
|
||||
if (s_errorMsg.empty()) s_errorMsg = TR("grpc_bootstrap_failed");
|
||||
s_state = State::Failed;
|
||||
}
|
||||
s_bootstrap.reset();
|
||||
|
||||
+218
-29
@@ -93,6 +93,10 @@ char s_new_zaddr[128] = "";
|
||||
char s_new_msg[256] = "";
|
||||
char s_search[80] = ""; // conversation-list filter (Q8)
|
||||
bool s_show_hidden = false; // when on, the list also shows hidden conversations (with an Unhide action)
|
||||
bool s_show_delete_confirm = false; // "Delete conversation?" confirm overlay (revive vs block)
|
||||
std::string s_delete_cid; // conversation targeted by the delete confirm
|
||||
std::string s_delete_name; // its peer name (for the confirm copy / block-list label)
|
||||
bool s_show_blocked = false; // blocked-conversations manager overlay (unblock)
|
||||
bool s_show_emoji_picker = false; // emoji picker overlay — fills the conversation-list pane while open
|
||||
char s_emoji_search[48] = ""; // emoji picker keyword filter
|
||||
|
||||
@@ -425,9 +429,9 @@ void RenderChatSettingsPreview(App* app, float width) {
|
||||
struct PMsg { const char* body; bool outgoing; bool startGroup; bool lastInGroup; std::string meta; };
|
||||
const std::string peer = "Ava";
|
||||
const PMsg msgs[] = {
|
||||
{ u8"Did the payment go through? \U0001F642", false, true, true, peer + " " + t1 },
|
||||
{ u8"Yep — just confirmed ✅", true, true, false, std::string(TR("chat_you")) + " " + t2 },
|
||||
{ u8"Sending the rest now \U0001F44D", true, false, true, std::string() },
|
||||
{ TR("grpb_preview_msg_payment_through"), false, true, true, peer + " " + t1 },
|
||||
{ TR("grpb_preview_msg_yep_confirmed"), true, true, false, std::string(TR("chat_you")) + " " + t2 },
|
||||
{ TR("grpb_preview_msg_sending_rest"), true, false, true, std::string() },
|
||||
};
|
||||
const int N = 3;
|
||||
|
||||
@@ -523,6 +527,16 @@ struct ConvSummary {
|
||||
bool hidden = false; // shown only while "Show hidden" is on
|
||||
};
|
||||
|
||||
// Per-frame memoization of the conversation-list build and the open-thread message list (both otherwise
|
||||
// rescan + copy + sort the whole chat history every frame). File-scope so ResetChatTab() can reset them
|
||||
// on a wallet switch; the hide/unhide/rename handlers reset s_convsKey directly to force a rebuild.
|
||||
std::vector<ConvSummary> s_convs;
|
||||
int s_convsHidden = 0;
|
||||
std::uint64_t s_convsKey = ~0ull;
|
||||
std::string s_threadCid;
|
||||
std::uint64_t s_threadRev = ~0ull;
|
||||
std::vector<chat::ChatMessage> s_threadMsgs;
|
||||
|
||||
// Centered, muted, wrapped hint for the empty states.
|
||||
void centeredHint(const char* text) {
|
||||
ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||
@@ -725,32 +739,46 @@ void RenderChatTab(App* app)
|
||||
}
|
||||
|
||||
// Build conversation summaries (single scan per conversation), sorted by most-recent activity.
|
||||
std::vector<ConvSummary> convs;
|
||||
int hiddenCount = 0;
|
||||
for (const auto& cid : store.conversationIds()) {
|
||||
const bool hidden = app->settings() && app->settings()->isChatHidden(cid);
|
||||
if (hidden) ++hiddenCount;
|
||||
if (hidden && !s_show_hidden) continue; // filtered out unless "Show hidden" is on
|
||||
const auto messages = store.conversation(cid);
|
||||
if (messages.empty()) continue;
|
||||
ConvSummary c;
|
||||
c.cid = cid;
|
||||
c.hidden = hidden;
|
||||
c.count = static_cast<int>(messages.size());
|
||||
for (const auto& m : messages) { // pin to the EARLIEST (establishing) peer z-addr / key (B2 — the
|
||||
if (c.peerZaddr.empty() && !m.peer_zaddr.empty()) c.peerZaddr = m.peer_zaddr; // memo header rides
|
||||
if (c.peerPubKey.empty() && !m.peer_public_key_hex.empty()) c.peerPubKey = m.peer_public_key_hex; // outside the AEAD)
|
||||
// MEMOIZED: this previously rescanned + copied + sorted the ENTIRE chat history every frame. Rebuild
|
||||
// only when the store changed (revision), the show-hidden toggle flipped, or the contact list grew
|
||||
// (peerName resolution). Hide/unhide and rename don't move any of those, so those handlers force a
|
||||
// rebuild by resetting s_convsKey (delete/block already bump the store revision). s_convsKey is reset
|
||||
// in ResetChatTab on wallet switch.
|
||||
const std::uint64_t convsKey =
|
||||
store.revision() * 1000003ull
|
||||
+ static_cast<std::uint64_t>(s_show_hidden ? 1 : 0)
|
||||
+ (book.revision() << 20); // book.revision() catches in-place contact edits (rename) that keep size()
|
||||
if (convsKey != s_convsKey) {
|
||||
s_convs.clear();
|
||||
s_convsHidden = 0;
|
||||
for (const auto& cid : store.conversationIds()) {
|
||||
const bool hidden = app->settings() && app->settings()->isChatHidden(cid);
|
||||
if (hidden) ++s_convsHidden;
|
||||
if (hidden && !s_show_hidden) continue; // filtered out unless "Show hidden" is on
|
||||
const auto messages = store.conversation(cid);
|
||||
if (messages.empty()) continue;
|
||||
ConvSummary c;
|
||||
c.cid = cid;
|
||||
c.hidden = hidden;
|
||||
c.count = static_cast<int>(messages.size());
|
||||
for (const auto& m : messages) { // pin to the EARLIEST (establishing) peer z-addr / key (B2 — the
|
||||
if (c.peerZaddr.empty() && !m.peer_zaddr.empty()) c.peerZaddr = m.peer_zaddr; // memo header rides
|
||||
if (c.peerPubKey.empty() && !m.peer_public_key_hex.empty()) c.peerPubKey = m.peer_public_key_hex; // outside the AEAD)
|
||||
}
|
||||
const auto& last = messages.back();
|
||||
c.lastBody = last.body;
|
||||
c.lastTs = last.timestamp;
|
||||
const int idx = c.peerZaddr.empty() ? -1 : book.findByAddress(c.peerZaddr);
|
||||
c.peerName = (idx >= 0) ? book.entries()[idx].label
|
||||
: shorten(!c.peerZaddr.empty() ? c.peerZaddr : cid);
|
||||
s_convs.push_back(std::move(c));
|
||||
}
|
||||
const auto& last = messages.back();
|
||||
c.lastBody = last.body;
|
||||
c.lastTs = last.timestamp;
|
||||
const int idx = c.peerZaddr.empty() ? -1 : book.findByAddress(c.peerZaddr);
|
||||
c.peerName = (idx >= 0) ? book.entries()[idx].label
|
||||
: shorten(!c.peerZaddr.empty() ? c.peerZaddr : cid);
|
||||
convs.push_back(std::move(c));
|
||||
std::sort(s_convs.begin(), s_convs.end(),
|
||||
[](const ConvSummary& a, const ConvSummary& b) { return a.lastTs > b.lastTs; });
|
||||
s_convsKey = convsKey;
|
||||
}
|
||||
std::sort(convs.begin(), convs.end(),
|
||||
[](const ConvSummary& a, const ConvSummary& b) { return a.lastTs > b.lastTs; });
|
||||
std::vector<ConvSummary>& convs = s_convs;
|
||||
int hiddenCount = s_convsHidden;
|
||||
|
||||
// Keep the selection valid (only when there is something to select).
|
||||
if (!convs.empty() &&
|
||||
@@ -845,6 +873,15 @@ void RenderChatTab(App* app)
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::InputTextWithHint("##chatsearch", TR("chat_search"), s_search, sizeof(s_search));
|
||||
}
|
||||
// Blocked-conversations manager opener — only when at least one is blocked. Blocked convs have no
|
||||
// stored messages (deleted), so they can't appear in the list; this opens a small manager to unblock.
|
||||
const int blockedCount = app->settings() ? (int)app->settings()->blockedChatConversations().size() : 0;
|
||||
if (blockedCount > 0) {
|
||||
const std::string bl = std::string(TR("chat_blocked_manage")) + " (" + std::to_string(blockedCount) + ")";
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, material::OnSurfaceMedium());
|
||||
if (ImGui::SmallButton(bl.c_str())) s_show_blocked = true;
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
const std::string search = s_search;
|
||||
ImGui::Separator();
|
||||
if (convs.empty()) {
|
||||
@@ -1018,7 +1055,7 @@ void RenderChatTab(App* app)
|
||||
// The toolbar's left edge is known up front (from the button count). A rename (edit) icon is
|
||||
// shown whenever there's an address to save the contact under; the settings "notch" gear is
|
||||
// always the rightmost icon.
|
||||
const int nBtns = 4 + (hasAddr ? 1 : 0);
|
||||
const int nBtns = 5 + (hasAddr ? 1 : 0); // export, mute, hide, delete, settings (+rename)
|
||||
const float toolbarLeft = rightX - (nBtns * ib + (nBtns - 1) * gap);
|
||||
|
||||
// Compact address + lock (or waiting-chip) metrics, reserved to the right of the name.
|
||||
@@ -1069,6 +1106,7 @@ void RenderChatTab(App* app)
|
||||
} else {
|
||||
Notifications::instance().error(TR("address_book_exists"));
|
||||
}
|
||||
s_convsKey = ~0ull; // peerName changed — force the conversation-list memo to rebuild
|
||||
}
|
||||
s_rename_cid.clear();
|
||||
} else if (cancel) {
|
||||
@@ -1170,6 +1208,21 @@ void RenderChatTab(App* app)
|
||||
s_selected_cid.clear();
|
||||
Notifications::instance().info(TR("chat_hidden_toast"));
|
||||
}
|
||||
s_convsKey = ~0ull; // hidden-state changed — force the conversation-list memo to rebuild
|
||||
}
|
||||
bx += ib + gap;
|
||||
}
|
||||
// Delete — clears this conversation's LOCAL history. Destructive (and offers a
|
||||
// "delete & block" variant), so it opens a confirm dialog rather than acting inline.
|
||||
{
|
||||
ImGui::SetCursorScreenPos(ImVec2(bx, by));
|
||||
material::IconButtonStyle a = base;
|
||||
a.tooltip = TR("chat_delete");
|
||||
a.hoverColor = material::Error();
|
||||
if (material::IconButton("##hdr_delete", ICON_MD_DELETE_OUTLINE, ifont, ImVec2(ib, ib), a)) {
|
||||
s_delete_cid = sel->cid;
|
||||
s_delete_name = sel->peerName;
|
||||
s_show_delete_confirm = true;
|
||||
}
|
||||
bx += ib + gap;
|
||||
}
|
||||
@@ -1278,7 +1331,15 @@ void RenderChatTab(App* app)
|
||||
const float groupGap = (compact ? 4.0f : 7.0f) * dp;
|
||||
const float msgGap = (compact ? 2.0f : 3.0f) * dp;
|
||||
const ImU32 accentBase = bubbleAccentColor(cs ? cs->getChatBubbleAccent() : 0);
|
||||
const auto messages = store.conversation(s_selected_cid);
|
||||
// MEMOIZED: store.conversation() linear-scans ALL messages across ALL conversations and
|
||||
// copies+sorts the match every call. Rebuild only when the open thread or the store changes,
|
||||
// not every frame while the thread is simply being read/scrolled.
|
||||
if (s_selected_cid != s_threadCid || store.revision() != s_threadRev) {
|
||||
s_threadMsgs = store.conversation(s_selected_cid);
|
||||
s_threadCid = s_selected_cid;
|
||||
s_threadRev = store.revision();
|
||||
}
|
||||
const auto& messages = s_threadMsgs;
|
||||
// Grouping + per-day separators (Tier 1). Same-sender messages within kGroupWindow share
|
||||
// one meta header and stack tightly; a date pill is drawn once per calendar day.
|
||||
const std::int64_t nowTs = static_cast<std::int64_t>(std::time(nullptr));
|
||||
@@ -1820,6 +1881,122 @@ void RenderChatTab(App* app)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Delete-conversation confirm (revive-on-new-message vs delete & block) ----
|
||||
if (s_show_delete_confirm) {
|
||||
const float dp = Layout::dpiScale();
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = TR("chat_delete_title");
|
||||
ov.p_open = &s_show_delete_confirm; // X / backdrop closes it (no-op)
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 500.0f; ov.idSuffix = "chatdelete";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
ImGui::PushTextWrapPos(0.0f);
|
||||
ImGui::TextUnformatted((std::string(TR("chat_delete_body_prefix")) + s_delete_name +
|
||||
TR("chat_delete_body_suffix")).c_str());
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, material::OnSurfaceMedium());
|
||||
ImGui::TextUnformatted(TR("chat_delete_revive_note"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
ImGui::TextUnformatted(TR("chat_delete_local_note"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||
|
||||
auto doDelete = [&](bool block) {
|
||||
// Delete first — if the persisted rows can't be removed, change nothing else (no block,
|
||||
// no toast) so the store and DB can't diverge.
|
||||
if (!app->chatService().deleteConversation(s_delete_cid, block)) {
|
||||
Notifications::instance().error(TR("chat_delete_failed"));
|
||||
s_show_delete_confirm = false;
|
||||
s_delete_cid.clear(); s_delete_name.clear();
|
||||
return;
|
||||
}
|
||||
if (app->settings()) {
|
||||
if (block) app->settings()->setChatBlocked(s_delete_cid, s_delete_name, true);
|
||||
app->settings()->setChatHidden(s_delete_cid, false); // clear any prior hide flag
|
||||
app->settings()->save();
|
||||
}
|
||||
// Revive mode: forget the seen-watermark so a re-imported message badges as unread even if
|
||||
// its stamped time predates the deleted thread. Block mode keeps it, so an unblock-restored
|
||||
// history doesn't all re-badge.
|
||||
if (!block) app->forgetChatConversationSeen(s_delete_cid);
|
||||
if (s_selected_cid == s_delete_cid) s_selected_cid.clear();
|
||||
Notifications::instance().info(block ? TR("chat_blocked_toast") : TR("chat_deleted_toast"));
|
||||
s_show_delete_confirm = false;
|
||||
s_delete_cid.clear(); s_delete_name.clear();
|
||||
};
|
||||
|
||||
auto textW = [&](const char* t) {
|
||||
return ImGui::CalcTextSize(t).x + ImGui::GetStyle().FramePadding.x * 2.0f + 20.0f * dp;
|
||||
};
|
||||
const float gap2 = Layout::spacingSm();
|
||||
const float wDel = std::max(100.0f * dp, textW(TR("chat_delete_confirm")));
|
||||
const float wBlk = std::max(130.0f * dp, textW(TR("chat_delete_block")));
|
||||
const float wCan = std::max(90.0f * dp, textW(TR("chat_cancel")));
|
||||
material::BeginOverlayDialogFooter(wDel + wBlk + wCan + gap2 * 2.0f, /*drawSeparator=*/false);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(material::WithAlpha(material::Error(), 205)));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(material::Error()));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::ColorConvertU32ToFloat4(material::WithAlpha(material::Error(), 235)));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(material::OnError()));
|
||||
const bool doDel = material::TactileButton(TR("chat_delete_confirm"), ImVec2(wDel, 0));
|
||||
ImGui::SameLine(0, gap2);
|
||||
const bool doBlk = material::TactileButton(TR("chat_delete_block"), ImVec2(wBlk, 0));
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::SameLine(0, gap2);
|
||||
const bool doCancel = material::TactileButton(TR("chat_cancel"), ImVec2(wCan, 0));
|
||||
|
||||
if (doDel) doDelete(false);
|
||||
if (doBlk) doDelete(true);
|
||||
if (doCancel) { s_show_delete_confirm = false; s_delete_cid.clear(); s_delete_name.clear(); }
|
||||
|
||||
material::EndOverlayDialog();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Blocked-conversations manager (unblock) ----
|
||||
if (s_show_blocked) {
|
||||
const float dp = Layout::dpiScale();
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = TR("chat_blocked_title");
|
||||
ov.p_open = &s_show_blocked;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 500.0f; ov.idSuffix = "chatblocked";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
ImGui::PushTextWrapPos(0.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, material::OnSurfaceMedium());
|
||||
ImGui::TextUnformatted(TR("chat_blocked_desc"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
if (app->settings()) {
|
||||
// Copy so unblocking (which mutates the settings vector) during iteration is safe.
|
||||
const auto blocked = app->settings()->blockedChatConversations();
|
||||
std::string unblockCid;
|
||||
for (const auto& b : blocked) {
|
||||
ImGui::PushID(b.cid.c_str());
|
||||
const std::string label = b.name.empty() ? shorten(b.cid, 10, 6) : b.name;
|
||||
const float bw = ImGui::CalcTextSize(TR("chat_unblock")).x +
|
||||
ImGui::GetStyle().FramePadding.x * 2.0f + 16.0f * dp;
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(label.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - bw);
|
||||
if (material::TactileButton(TR("chat_unblock"), ImVec2(bw, 0))) unblockCid = b.cid;
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (!unblockCid.empty()) {
|
||||
app->settings()->setChatBlocked(unblockCid, "", false);
|
||||
app->settings()->save();
|
||||
Notifications::instance().info(TR("chat_unblocked_toast")); // re-imports on the next chat scan
|
||||
if (app->settings()->blockedChatConversations().empty()) s_show_blocked = false;
|
||||
}
|
||||
}
|
||||
material::EndOverlayDialog();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Chat customization modal (opened by the header settings "notch") — house BlurFloat overlay ----
|
||||
if (s_show_chat_settings) {
|
||||
material::OverlayDialogSpec ov;
|
||||
@@ -1883,6 +2060,18 @@ void ResetChatTab()
|
||||
s_rename_focus = false;
|
||||
s_show_new_convo = false;
|
||||
s_show_chat_settings = false;
|
||||
s_show_delete_confirm = false;
|
||||
s_delete_cid.clear();
|
||||
s_delete_name.clear();
|
||||
s_show_blocked = false;
|
||||
// Drop the per-frame memoization caches so the next wallet doesn't briefly render the previous one's
|
||||
// conversations/thread (store.revision() is monotonic and would rebuild anyway, but be explicit).
|
||||
s_convs.clear();
|
||||
s_convsHidden = 0;
|
||||
s_convsKey = ~0ull;
|
||||
s_threadCid.clear();
|
||||
s_threadRev = ~0ull;
|
||||
s_threadMsgs.clear();
|
||||
}
|
||||
|
||||
void RenderChatSettingsControls(App* app, float contentWidth, bool drawCards)
|
||||
|
||||
@@ -37,18 +37,21 @@ ConsoleModel::DrainResult ConsoleModel::drain()
|
||||
lines_.pop_front();
|
||||
++result.popped;
|
||||
}
|
||||
++revision_; // deque changed — lets the view memoize its filter/layout passes
|
||||
return result;
|
||||
}
|
||||
|
||||
void ConsoleModel::clear()
|
||||
{
|
||||
lines_.clear();
|
||||
++revision_;
|
||||
}
|
||||
|
||||
bool ConsoleModel::toggleCollapsed(std::size_t i)
|
||||
{
|
||||
if (i >= lines_.size() || lines_[i].foldSpan <= 0) return false;
|
||||
lines_[i].collapsed = !lines_[i].collapsed;
|
||||
++revision_; // fold change alters which lines are visible
|
||||
return lines_[i].collapsed;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "console_channel.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -76,11 +77,16 @@ public:
|
||||
const ConsoleModelLine& operator[](std::size_t i) const { return lines_[i]; }
|
||||
const ConsoleModelLine& back() const { return lines_.back(); }
|
||||
|
||||
// Monotonic counter bumped whenever the visible deque changes (drain added/evicted lines, clear,
|
||||
// fold toggle). The view memoizes its per-frame filter + text-layout passes against this.
|
||||
std::uint64_t revision() const { return revision_; }
|
||||
|
||||
private:
|
||||
const std::size_t max_lines_;
|
||||
std::deque<ConsoleModelLine> lines_; // visible model — main thread only
|
||||
std::vector<ConsoleModelLine> pending_; // guarded by ingest_mutex_
|
||||
std::mutex ingest_mutex_;
|
||||
std::uint64_t revision_ = 0;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -337,7 +337,7 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
||||
float outputH = ComputeConsoleOutputHeight(
|
||||
availHeight,
|
||||
input_height,
|
||||
schema::UI().drawElement("tabs.console", "output-min-height").size,
|
||||
schema::UI().drawElement("tabs.console", "output-min-height").size * Layout::dpiScale(),
|
||||
schema::UI().drawElement("tabs.console", "output-min-height-ratio").size);
|
||||
|
||||
ImDrawList* dlOut = ImGui::GetWindowDrawList();
|
||||
@@ -820,11 +820,23 @@ void ConsoleTab::renderOutput()
|
||||
// segment records which bytes of the source text appear on that visual row, so
|
||||
// hit-testing and selection highlight can map screen positions to exact char offsets.
|
||||
float wrap_width = ClampConsoleWrapWidth(ImGui::GetContentRegionAvail().x, padX);
|
||||
ImFontConsoleMeasure measure(ImGui::GetFont(), ImGui::GetFontSize());
|
||||
layout_ = BuildConsoleLayout(
|
||||
static_cast<int>(visible_indices_.size()),
|
||||
[this](int vi) -> const std::string& { return model_[visible_indices_[vi]].text; },
|
||||
wrap_width, line_height, interLineGap, measure);
|
||||
// Memoize the text-shaping pass: rebuild only when the visible set, wrap width, line height, gap or
|
||||
// zoom changed. Otherwise this re-wrapped and re-measured every visible line (glyph-by-glyph) every
|
||||
// frame, even while idle/scrolled. layout_ is a member, so the cached geometry stays valid for
|
||||
// drawVisibleLines / screenToTextPos when we skip the rebuild.
|
||||
std::uint64_t layoutKey = vis_generation_ * 1000003ull;
|
||||
layoutKey = layoutKey * 131ull + static_cast<std::uint64_t>(wrap_width * 16.0f);
|
||||
layoutKey = layoutKey * 131ull + static_cast<std::uint64_t>(line_height * 16.0f);
|
||||
layoutKey = layoutKey * 131ull + static_cast<std::uint64_t>(interLineGap * 16.0f);
|
||||
layoutKey = layoutKey * 131ull + static_cast<std::uint64_t>(s_console_zoom * 1000.0f);
|
||||
if (layoutKey != layout_key_) {
|
||||
ImFontConsoleMeasure measure(ImGui::GetFont(), ImGui::GetFontSize());
|
||||
layout_ = BuildConsoleLayout(
|
||||
static_cast<int>(visible_indices_.size()),
|
||||
[this](int vi) -> const std::string& { return model_[visible_indices_[vi]].text; },
|
||||
wrap_width, line_height, interLineGap, measure);
|
||||
layout_key_ = layoutKey;
|
||||
}
|
||||
|
||||
// Mouse/keyboard interaction (wheel-up detach, selection drag, Ctrl+C/A). Raw IO bypasses
|
||||
// the child window's event consumption.
|
||||
@@ -891,6 +903,20 @@ void ConsoleTab::renderOutput()
|
||||
|
||||
void ConsoleTab::computeVisibleLines(bool& hasTextFilter, std::string& filterLower)
|
||||
{
|
||||
// Memoize: rebuild the visible set only when the model changed or the filter state changed.
|
||||
// Otherwise this scanned the entire (up to 10k-line) model with a per-line filter predicate every
|
||||
// frame. The out-params + filter_match_count_/folding_active_ are members that stay valid until the
|
||||
// key moves, so an early return leaves last frame's (still-correct) results in place.
|
||||
std::uint64_t visKey = model_.revision() * 1000003ull;
|
||||
for (const char* p = filter_text_; *p; ++p) visKey = visKey * 131ull + static_cast<unsigned char>(*p);
|
||||
visKey = visKey * 2ull + (s_daemon_messages_enabled ? 1u : 0u);
|
||||
visKey = visKey * 2ull + (s_errors_only_enabled ? 1u : 0u);
|
||||
visKey = visKey * 2ull + (s_rpc_trace_enabled ? 1u : 0u);
|
||||
visKey = visKey * 2ull + (s_app_messages_enabled ? 1u : 0u);
|
||||
if (visKey == vis_key_) return; // nothing that affects the visible set changed
|
||||
vis_key_ = visKey;
|
||||
++vis_generation_; // the layout pass keys off this
|
||||
|
||||
ConsoleOutputFilter outputFilter{filter_text_, s_daemon_messages_enabled,
|
||||
s_errors_only_enabled, s_rpc_trace_enabled,
|
||||
s_app_messages_enabled};
|
||||
@@ -2079,6 +2105,12 @@ void ConsoleTab::clear()
|
||||
// line indices) here to avoid an out-of-bounds crash. computeVisibleLines() rebuilds them next frame.
|
||||
visible_indices_.clear();
|
||||
selection_.clear();
|
||||
// Force both memoized passes to rebuild: renderOutput() runs later THIS frame against the now-empty
|
||||
// visible_indices_, and computeVisibleLines() recomputes next frame — without these resets the layout
|
||||
// memo would skip the rebuild and keep stale geometry for the just-emptied set.
|
||||
vis_key_ = ~0ull;
|
||||
layout_key_ = ~0ull;
|
||||
++vis_generation_;
|
||||
stop_confirm_pending_ = false; // a pending 'stop' confirmation is cancelled by clearing
|
||||
addLine(TR("console_cleared"), ConsoleChannel::Info);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "../../rpc/rpc_client.h"
|
||||
#include "../../rpc/rpc_worker.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
@@ -188,6 +189,14 @@ private:
|
||||
bool has_text_filter_ = false; // computed once per frame (before the toolbar draws it)
|
||||
std::string filter_lower_; // lowercased filter needle for match highlighting
|
||||
|
||||
// Memoization keys so the two expensive per-frame passes rebuild only on change (not every frame):
|
||||
// computeVisibleLines (filter scan over the whole model) is keyed on the model revision + filter
|
||||
// state; BuildConsoleLayout (glyph-by-glyph text shaping of every visible line) is keyed on the
|
||||
// resulting visible-set generation + wrap width + line height/zoom.
|
||||
std::uint64_t vis_key_ = ~0ull; // key of the last computeVisibleLines
|
||||
std::uint64_t vis_generation_ = 0; // bumped whenever visible_indices_ is rebuilt
|
||||
std::uint64_t layout_key_ = ~0ull; // key of the last BuildConsoleLayout
|
||||
|
||||
// Wrap layout for the visible lines (segments + per-line heights + cumulative Y),
|
||||
// recomputed each frame by the pure BuildConsoleLayout (console_text_layout.h) and
|
||||
// consumed by the renderer + hit-testing.
|
||||
|
||||
@@ -108,13 +108,13 @@ static const char* relativeTime(int64_t timestamp) {
|
||||
int64_t diff = now - timestamp;
|
||||
if (diff < 0) diff = 0;
|
||||
if (diff < 60)
|
||||
snprintf(buf, sizeof(buf), "%lld sec ago", (long long)diff);
|
||||
snprintf(buf, sizeof(buf), TR("grpa_sec_ago"), (long long)diff);
|
||||
else if (diff < 3600)
|
||||
snprintf(buf, sizeof(buf), "%lld min ago", (long long)(diff / 60));
|
||||
snprintf(buf, sizeof(buf), TR("grpa_min_ago"), (long long)(diff / 60));
|
||||
else if (diff < 86400)
|
||||
snprintf(buf, sizeof(buf), "%lld hr ago", (long long)(diff / 3600));
|
||||
snprintf(buf, sizeof(buf), TR("grpa_hr_ago"), (long long)(diff / 3600));
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "%lld days ago", (long long)(diff / 86400));
|
||||
snprintf(buf, sizeof(buf), TR("grpa_days_ago"), (long long)(diff / 86400));
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -1216,7 +1216,7 @@ static void renderBlockDetailModal(App* app) {
|
||||
txDL->AddRectFilled(rowStart,
|
||||
ImVec2(rowStart.x + txContentW, rowStart.y + txRowH),
|
||||
WithAlpha(OnSurface(), 10),
|
||||
S.drawElement("tabs.explorer", "row-rounding").size);
|
||||
S.drawElement("tabs.explorer", "row-rounding").size * dp);
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
material::Tooltip("%s", txid.c_str());
|
||||
}
|
||||
@@ -1315,7 +1315,7 @@ static void renderBlockDetailModal(App* app) {
|
||||
}
|
||||
|
||||
if (s_detail_txids.size() > 100) {
|
||||
snprintf(buf, sizeof(buf), "... showing first 100 of %d", (int)s_detail_txids.size());
|
||||
snprintf(buf, sizeof(buf), TR("grpa_showing_first_100_of"), (int)s_detail_txids.size());
|
||||
ImGui::TextDisabled("%s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "faq_content.h"
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
namespace faq {
|
||||
|
||||
// ── Wallet group ───────────────────────────────────────────────────────────
|
||||
// Variant-aware: gs_1 (what the app IS) and sec_1 (where encryption lives) differ between the
|
||||
// full-node and Lite builds; migrate-to-seed (seed_2) is full-node only; and the Lite build gets a
|
||||
// trailing "Lite Wallet" subcategory that stands in for the (hidden) Daemon group. The remaining
|
||||
// answers are worded neutrally so one string serves both variants.
|
||||
static std::vector<FaqSubcategory> buildWalletFaq(bool fullNode)
|
||||
{
|
||||
std::vector<FaqSubcategory> w;
|
||||
w.push_back({ "faq_w_gs_title", {
|
||||
{ fullNode ? "faq_w_gs_1_q" : "faq_l_gs_1_q", fullNode ? "faq_w_gs_1_a" : "faq_l_gs_1_a" },
|
||||
{ "faq_w_gs_2_q", "faq_w_gs_2_a" },
|
||||
{ "faq_w_gs_3_q", "faq_w_gs_3_a" },
|
||||
{ "faq_w_gs_4_q", "faq_w_gs_4_a" },
|
||||
}});
|
||||
w.push_back({ "faq_w_addr_title", {
|
||||
{ "faq_w_addr_1_q", "faq_w_addr_1_a" },
|
||||
{ "faq_w_addr_2_q", "faq_w_addr_2_a" },
|
||||
{ "faq_w_addr_3_q", "faq_w_addr_3_a" },
|
||||
{ "faq_w_addr_4_q", "faq_w_addr_4_a" },
|
||||
}});
|
||||
w.push_back({ "faq_w_send_title", {
|
||||
{ "faq_w_send_1_q", "faq_w_send_1_a" },
|
||||
{ "faq_w_send_2_q", "faq_w_send_2_a" },
|
||||
{ "faq_w_send_3_q", "faq_w_send_3_a" },
|
||||
{ "faq_w_send_4_q", "faq_w_send_4_a" },
|
||||
}});
|
||||
w.push_back({ "faq_w_bal_title", {
|
||||
{ "faq_w_bal_1_q", "faq_w_bal_1_a" },
|
||||
{ "faq_w_bal_2_q", "faq_w_bal_2_a" },
|
||||
{ "faq_w_bal_3_q", "faq_w_bal_3_a" },
|
||||
}});
|
||||
w.push_back({ "faq_w_sec_title", {
|
||||
{ "faq_w_sec_1_q", fullNode ? "faq_w_sec_1_a" : "faq_l_sec_1_a" },
|
||||
{ "faq_w_sec_2_q", "faq_w_sec_2_a" },
|
||||
{ "faq_w_sec_3_q", "faq_w_sec_3_a" },
|
||||
}});
|
||||
{
|
||||
std::vector<FaqEntry> seed = { { "faq_w_seed_1_q", "faq_w_seed_1_a" } };
|
||||
if (fullNode) seed.push_back({ "faq_w_seed_2_q", "faq_w_seed_2_a" }); // migrate-to-seed is full-node only
|
||||
seed.push_back({ "faq_w_seed_3_q", "faq_w_seed_3_a" });
|
||||
seed.push_back({ "faq_w_seed_4_q", "faq_w_seed_4_a" });
|
||||
w.push_back({ "faq_w_seed_title", std::move(seed) });
|
||||
}
|
||||
w.push_back({ "faq_w_chat_title", {
|
||||
{ "faq_w_chat_1_q", "faq_w_chat_1_a" },
|
||||
{ "faq_w_chat_2_q", "faq_w_chat_2_a" },
|
||||
{ "faq_w_chat_3_q", "faq_w_chat_3_a" },
|
||||
{ "faq_w_chat_4_q", "faq_w_chat_4_a" },
|
||||
}});
|
||||
w.push_back({ "faq_w_set_title", {
|
||||
{ "faq_w_set_1_q", "faq_w_set_1_a" },
|
||||
{ "faq_w_set_2_q", "faq_w_set_2_a" },
|
||||
{ "faq_w_set_3_q", "faq_w_set_3_a" },
|
||||
{ "faq_w_set_4_q", "faq_w_set_4_a" },
|
||||
}});
|
||||
// Lite-only: explain the server model (stands in for the hidden Daemon group).
|
||||
if (!fullNode) {
|
||||
w.push_back({ "faq_l_lite_title", {
|
||||
{ "faq_l_lite_1_q", "faq_l_lite_1_a" },
|
||||
{ "faq_l_lite_2_q", "faq_l_lite_2_a" },
|
||||
{ "faq_l_lite_3_q", "faq_l_lite_3_a" },
|
||||
}});
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
const std::vector<FaqSubcategory>& walletFaq(bool fullNode)
|
||||
{
|
||||
static const std::vector<FaqSubcategory> kFull = buildWalletFaq(true);
|
||||
static const std::vector<FaqSubcategory> kLite = buildWalletFaq(false);
|
||||
return fullNode ? kFull : kLite;
|
||||
}
|
||||
|
||||
// ── Daemon group (full-node only) ──────────────────────────────────────────
|
||||
const std::vector<FaqSubcategory>& daemonFaq()
|
||||
{
|
||||
static const std::vector<FaqSubcategory> kDaemon = {
|
||||
{ "faq_d_node_title", {
|
||||
{ "faq_d_node_1_q", "faq_d_node_1_a" },
|
||||
{ "faq_d_node_2_q", "faq_d_node_2_a" },
|
||||
{ "faq_d_node_3_q", "faq_d_node_3_a" },
|
||||
}},
|
||||
{ "faq_d_sync_title", {
|
||||
{ "faq_d_sync_1_q", "faq_d_sync_1_a" },
|
||||
{ "faq_d_sync_2_q", "faq_d_sync_2_a" },
|
||||
{ "faq_d_sync_3_q", "faq_d_sync_3_a" },
|
||||
{ "faq_d_sync_4_q", "faq_d_sync_4_a" },
|
||||
}},
|
||||
{ "faq_d_mgmt_title", {
|
||||
{ "faq_d_mgmt_1_q", "faq_d_mgmt_1_a" },
|
||||
{ "faq_d_mgmt_2_q", "faq_d_mgmt_2_a" },
|
||||
{ "faq_d_mgmt_3_q", "faq_d_mgmt_3_a" },
|
||||
}},
|
||||
{ "faq_d_upd_title", {
|
||||
{ "faq_d_upd_1_q", "faq_d_upd_1_a" },
|
||||
{ "faq_d_upd_2_q", "faq_d_upd_2_a" },
|
||||
{ "faq_d_upd_3_q", "faq_d_upd_3_a" },
|
||||
}},
|
||||
{ "faq_d_mine_title", {
|
||||
{ "faq_d_mine_1_q", "faq_d_mine_1_a" },
|
||||
{ "faq_d_mine_2_q", "faq_d_mine_2_a" },
|
||||
{ "faq_d_mine_3_q", "faq_d_mine_3_a" },
|
||||
{ "faq_d_mine_4_q", "faq_d_mine_4_a" },
|
||||
}},
|
||||
{ "faq_d_net_title", {
|
||||
{ "faq_d_net_1_q", "faq_d_net_1_a" },
|
||||
{ "faq_d_net_2_q", "faq_d_net_2_a" },
|
||||
}},
|
||||
{ "faq_d_perf_title", {
|
||||
{ "faq_d_perf_1_q", "faq_d_perf_1_a" },
|
||||
{ "faq_d_perf_2_q", "faq_d_perf_2_a" },
|
||||
}},
|
||||
{ "faq_d_trbl_title", {
|
||||
{ "faq_d_trbl_1_q", "faq_d_trbl_1_a" },
|
||||
{ "faq_d_trbl_2_q", "faq_d_trbl_2_a" },
|
||||
{ "faq_d_trbl_3_q", "faq_d_trbl_3_a" },
|
||||
{ "faq_d_trbl_4_q", "faq_d_trbl_4_a" },
|
||||
}},
|
||||
};
|
||||
return kDaemon;
|
||||
}
|
||||
|
||||
} // namespace faq
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
@@ -0,0 +1,44 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
//
|
||||
// FAQ content model. The FAQ screen is data-driven: this header exposes the two
|
||||
// top-level groups (Wallet, Daemon) as ordered lists of subcategories, each a list
|
||||
// of question/answer entries. Every string is an i18n KEY (looked up with TR at
|
||||
// render time), so wording + translations live in src/util/i18n.cpp + res/lang/*.json
|
||||
// and never require touching UI code. Add a Q&A by appending a {qKey,aKey} pair here
|
||||
// and its two strings to loadBuiltinEnglish().
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
namespace faq {
|
||||
|
||||
// One question and its answer, both i18n keys. The answer may contain "\n\n"
|
||||
// paragraph breaks; it is rendered wrapped. Keep answers free of printf specifiers
|
||||
// (%d/%s/…) — the i18n layer rejects translations whose format signature drifts.
|
||||
struct FaqEntry {
|
||||
const char* questionKey;
|
||||
const char* answerKey;
|
||||
};
|
||||
|
||||
// A named group of questions. titleKey is an i18n key for the subcategory header.
|
||||
struct FaqSubcategory {
|
||||
const char* titleKey;
|
||||
std::vector<FaqEntry> entries;
|
||||
};
|
||||
|
||||
// The two top-level groups. daemonFaq() is full-node material and is only shown when
|
||||
// the build supports full-node lifecycle actions (see App::supportsFullNodeLifecycleActions()).
|
||||
// walletFaq() is variant-aware: pass fullNode=false for the Lite variant, which swaps in
|
||||
// lite-appropriate answers (no local node / daemon), drops full-node-only entries
|
||||
// (e.g. migrate-to-seed), and appends a "Lite Wallet" subcategory explaining the server model.
|
||||
const std::vector<FaqSubcategory>& walletFaq(bool fullNode);
|
||||
const std::vector<FaqSubcategory>& daemonFaq();
|
||||
|
||||
} // namespace faq
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
@@ -0,0 +1,203 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "faq_dialog.h"
|
||||
#include "faq_content.h"
|
||||
#include "../../app.h"
|
||||
#include "../../util/i18n.h"
|
||||
#include "../../util/text_format.h"
|
||||
#include "../../embedded/IconsMaterialDesign.h"
|
||||
#include "../schema/ui_schema.h"
|
||||
#include "../layout.h"
|
||||
#include "../material/type.h"
|
||||
#include "../material/colors.h"
|
||||
#include "../material/draw_helpers.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
|
||||
namespace {
|
||||
|
||||
// Persists across frames (the dialog is re-entered each frame while open). Group 0 = Wallet,
|
||||
// 1 = Daemon. `expanded` keys are FaqEntry::questionKey (stable string literals from faq_content).
|
||||
struct FaqDialogState {
|
||||
int group = 0;
|
||||
char search[128] = "";
|
||||
std::unordered_map<std::string, bool> expanded;
|
||||
};
|
||||
FaqDialogState s_faq;
|
||||
|
||||
bool entryMatches(const faq::FaqEntry& e, const char* query)
|
||||
{
|
||||
return util::containsIgnoreCase(TR(e.questionKey), query) ||
|
||||
util::containsIgnoreCase(TR(e.answerKey), query);
|
||||
}
|
||||
|
||||
// One answer body: wrapped, muted. Shared by the collapsible (non-search) and search paths.
|
||||
void renderAnswer(const char* answerKey)
|
||||
{
|
||||
ImGui::Indent(Layout::spacingMd());
|
||||
ImGui::PushFont(material::Type().body2());
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(material::OnSurfaceMedium()));
|
||||
ImGui::TextWrapped("%s", TR(answerKey));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopFont();
|
||||
ImGui::Unindent(Layout::spacingMd());
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void RenderFaqDialog(App* app, bool* p_open)
|
||||
{
|
||||
auto& S = schema::UI();
|
||||
auto win = S.window("dialogs.faq");
|
||||
const float dp = Layout::dpiScale();
|
||||
|
||||
const bool daemonAvailable = app && app->supportsFullNodeLifecycleActions();
|
||||
if (!daemonAvailable) s_faq.group = 0; // no Daemon tab in lite builds
|
||||
|
||||
// Floating "BlurFloat" modal, matching the Wallets dialog: live-blur backdrop, no boxed card, a
|
||||
// plain heading (no ✕ — a Close button sits at the bottom). Roomy fixed width; height capped to the
|
||||
// viewport so the content flexes + scrolls on small / HiDPI screens.
|
||||
const float vpH = ImGui::GetMainViewport()->Size.y;
|
||||
const float wantH = (win.height > 0 ? win.height : 640.0f) * dp;
|
||||
material::OverlayDialogSpec spec;
|
||||
spec.title = TR("faq_title");
|
||||
spec.p_open = p_open;
|
||||
spec.style = material::OverlayStyle::BlurFloat;
|
||||
spec.cardWidth = (win.width > 0 ? win.width : 860.0f);
|
||||
spec.cardHeight = std::min(wantH, vpH * 0.86f) / dp;
|
||||
spec.idSuffix = "faq";
|
||||
if (!material::BeginOverlayDialog(spec)) {
|
||||
return;
|
||||
}
|
||||
// Esc closes (ImGui consumes Esc itself while the search box is being edited, so this only
|
||||
// fires when the field isn't capturing it).
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) *p_open = false;
|
||||
|
||||
// Subtitle under the plain heading, matching the Wallets dialog's intro caption.
|
||||
material::Type().textColored(material::TypeStyle::Caption, material::OnSurfaceMedium(),
|
||||
TR(daemonAvailable ? "faq_intro" : "faq_intro_lite"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
const float contentW = ImGui::GetContentRegionAvail().x;
|
||||
|
||||
// ── Group tabs: Wallet | Daemon — only when there's a choice. Lite has just the Wallet group, so a
|
||||
// lone "Wallet" selector is redundant; skip it entirely (the group is already pinned to 0 above).
|
||||
if (daemonAvailable) {
|
||||
const float gap = ImGui::GetStyle().ItemSpacing.x;
|
||||
const float tabW = (contentW - gap) / 2.0f;
|
||||
auto tab = [&](const char* label, int idx) {
|
||||
const bool active = (s_faq.group == idx);
|
||||
if (active) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(material::Primary()));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(material::OnPrimary()));
|
||||
}
|
||||
if (material::TactileButton(label, ImVec2(tabW, 0))) s_faq.group = idx;
|
||||
if (active) ImGui::PopStyleColor(2);
|
||||
};
|
||||
tab(TR("faq_group_wallet"), 0);
|
||||
ImGui::SameLine();
|
||||
tab(TR("faq_group_daemon"), 1);
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
// ── Search ──
|
||||
ImGui::SetNextItemWidth(contentW);
|
||||
ImGui::InputTextWithHint("##FaqSearch", TR("faq_search_hint"), s_faq.search, sizeof(s_faq.search));
|
||||
const bool searching = s_faq.search[0] != '\0';
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// ── Scrollable Q&A body ── (reserve room for the Close button footer below)
|
||||
const float footerH = ImGui::GetFrameHeightWithSpacing() + Layout::spacingMd();
|
||||
float bodyH = ImGui::GetContentRegionAvail().y - footerH;
|
||||
if (bodyH < 80.0f * dp) bodyH = 80.0f * dp;
|
||||
// Inner padding gives the content breathing room and, on the right, a clear gap to the LEFT of the
|
||||
// scrollbar (WindowPadding.x is exactly that gap); the scrollbar itself is made chunkier than the
|
||||
// app default. NoScrollWithMouse + ApplySmoothScroll gives the wheel eased scrolling, matching the
|
||||
// Wallets dialog / Settings page (ApplySmoothScroll owns the wheel input and lerps ScrollY).
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(Layout::spacingMd(), Layout::spacingXs()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 16.0f * dp);
|
||||
ImGui::BeginChild("##FaqScroll", ImVec2(0, bodyH), false,
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
material::ApplySmoothScroll();
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
|
||||
const auto& groups = (s_faq.group == 1 && daemonAvailable) ? faq::daemonFaq() : faq::walletFaq(daemonAvailable);
|
||||
bool anyShown = false;
|
||||
|
||||
bool firstSection = true;
|
||||
for (const auto& subcat : groups) {
|
||||
// Collect the entries visible under the current search.
|
||||
std::vector<const faq::FaqEntry*> visible;
|
||||
for (const auto& e : subcat.entries) {
|
||||
if (!searching || entryMatches(e, s_faq.search)) visible.push_back(&e);
|
||||
}
|
||||
if (visible.empty()) continue;
|
||||
|
||||
// Section break: generous space above every section after the first, so topic groups read as
|
||||
// clearly separated bands rather than one uniform list.
|
||||
if (!firstSection) ImGui::Dummy(ImVec2(0, Layout::spacingLg()));
|
||||
firstSection = false;
|
||||
anyShown = true;
|
||||
|
||||
// Section header — accent overline + a thin full-width rule beneath it, anchoring the group
|
||||
// above its (brighter, normal-case) question rows.
|
||||
material::Type().textColored(material::TypeStyle::Overline,
|
||||
material::Primary(), TR(subcat.titleKey));
|
||||
{
|
||||
const ImVec2 rp = ImGui::GetCursorScreenPos();
|
||||
const float rw = ImGui::GetContentRegionAvail().x;
|
||||
dl->AddLine(ImVec2(rp.x, rp.y + dp), ImVec2(rp.x + rw, rp.y + dp),
|
||||
material::Divider(), 1.0f * dp);
|
||||
}
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
for (const auto* e : visible) {
|
||||
const float rowW = ImGui::GetContentRegionAvail().x;
|
||||
if (searching) {
|
||||
// Search results: show question + answer directly (no collapsing).
|
||||
ImGui::PushFont(material::Type().subtitle2());
|
||||
ImGui::TextWrapped("%s", TR(e->questionKey));
|
||||
ImGui::PopFont();
|
||||
renderAnswer(e->answerKey);
|
||||
} else {
|
||||
bool& exp = s_faq.expanded[e->questionKey];
|
||||
std::string id = std::string("##faq_") + e->questionKey;
|
||||
material::CollapsibleHeader(dl, id.c_str(), TR(e->questionKey), exp, rowW,
|
||||
material::Type().subtitle2(), material::OnSurface());
|
||||
if (exp) renderAnswer(e->answerKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyShown) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingLg()));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(material::OnSurfaceMedium()));
|
||||
ImGui::TextWrapped("%s", TR("faq_no_results"));
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar(2); // ScrollbarSize, WindowPadding
|
||||
|
||||
// Close button footer (BlurFloat has no ✕ in the heading).
|
||||
const float closeW = 120.0f * dp;
|
||||
material::BeginOverlayDialogFooter(closeW, false);
|
||||
if (material::TactileButton(TR("close"), ImVec2(closeW, 0))) *p_open = false;
|
||||
|
||||
material::EndOverlayDialog();
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
@@ -0,0 +1,18 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace dragonx {
|
||||
class App;
|
||||
namespace ui {
|
||||
|
||||
// Renders the Help & FAQ modal. Call every frame while *p_open is true; the dialog
|
||||
// clears *p_open itself on close (✕ / click-outside / Esc). Content is data-driven
|
||||
// (see faq_content.h) and grouped into Wallet and Daemon tabs; the Daemon tab is
|
||||
// hidden on builds without full-node lifecycle support.
|
||||
void RenderFaqDialog(App* app, bool* p_open);
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
@@ -173,7 +173,7 @@ void KeyExportDialog::render(App* app)
|
||||
s_key = found;
|
||||
s_show_key = wantViewing; // viewing keys are less sensitive
|
||||
} else {
|
||||
s_error = r.ok ? std::string("Key not available for this address") : r.error;
|
||||
s_error = r.ok ? std::string(TR("grpc_key_not_available")) : r.error;
|
||||
}
|
||||
wallet::secureWipeLiteSecret(found);
|
||||
s_fetching = false;
|
||||
@@ -240,14 +240,15 @@ void KeyExportDialog::render(App* app)
|
||||
ImGui::TextDisabled("%s", TR("key_export_click_retrieve"));
|
||||
} else {
|
||||
// Key fetched. Layout: [ key text (click-to-copy) + Show/Hide below ] [ QR | square ].
|
||||
const float gap = 12.0f;
|
||||
const float dp = Layout::dpiScale();
|
||||
const float gap = 12.0f * dp;
|
||||
const float avail = ImGui::GetContentRegionAvail().x;
|
||||
// Larger, responsive QR: ~30% of the content width, clamped to a comfortable range.
|
||||
float qrSize = avail * 0.30f;
|
||||
if (qrSize < 200.0f) qrSize = 200.0f;
|
||||
if (qrSize > 340.0f) qrSize = 340.0f;
|
||||
if (qrSize < 200.0f * dp) qrSize = 200.0f * dp;
|
||||
if (qrSize > 340.0f * dp) qrSize = 340.0f * dp;
|
||||
float keyW = avail - qrSize - gap;
|
||||
const bool sideBySide = keyW >= 200.0f; // too narrow -> stack the QR under the key
|
||||
const bool sideBySide = keyW >= 200.0f * dp; // too narrow -> stack the QR under the key
|
||||
if (!sideBySide) keyW = avail;
|
||||
|
||||
// Chunk for readability, but keep a Bech32 HRP (e.g. "secret-extended-key-main") intact
|
||||
|
||||
@@ -1580,7 +1580,7 @@ static void mktDrawPriceHero(const MktCtx& cx)
|
||||
}
|
||||
} else {
|
||||
const char* status = market.price_loading ? TR("market_price_loading") : TR("market_price_unavailable");
|
||||
DrawTextShadow(dl, sub1, sub1->LegacySize, ImVec2(cx0, cy + 10), OnSurfaceDisabled(), status);
|
||||
DrawTextShadow(dl, sub1, sub1->LegacySize, ImVec2(cx0, cy + 10 * dp), OnSurfaceDisabled(), status);
|
||||
if (!market.price_loading && !market.price_error.empty()) {
|
||||
std::string errorText = market.price_error;
|
||||
float maxErrorW = cardMax.x - cx0 - Layout::spacingLg();
|
||||
@@ -1590,7 +1590,7 @@ static void mktDrawPriceHero(const MktCtx& cx)
|
||||
}
|
||||
if (errorText.size() < market.price_error.size()) errorText += "...";
|
||||
dl->AddText(capFont, capFont->LegacySize,
|
||||
ImVec2(cx0, cy + 10 + sub1->LegacySize + Layout::spacingXs()),
|
||||
ImVec2(cx0, cy + 10 * dp + sub1->LegacySize + Layout::spacingXs()),
|
||||
Warning(), errorText.c_str());
|
||||
}
|
||||
}
|
||||
@@ -1874,7 +1874,7 @@ static void mktDrawPriceChart(const MktCtx& cx)
|
||||
: (tk == ticks - 1) ? plotRight - lblSz.x
|
||||
: xpos - lblSz.x * 0.5f;
|
||||
dl->AddText(capFont, capFont->LegacySize,
|
||||
ImVec2(lx, plotBottom + 4), OnSurfaceDisabled(), tlbl);
|
||||
ImVec2(lx, plotBottom + 4 * mktDp), OnSurfaceDisabled(), tlbl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,12 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
||||
}
|
||||
};
|
||||
ImFont* stepFont = Type().iconSmall();
|
||||
const float fieldH = capFont->LegacySize + 6.0f * dp;
|
||||
// Match the -/+ button height to the InputInt's actual frame height (GetFrameHeight ==
|
||||
// fontSize + 2*FramePadding.y, evaluated with the same font/style the input renders with,
|
||||
// since nothing pushes a font between here and the InputInt below). This keeps the buttons
|
||||
// the SAME height as the number box and top-aligned with it at sy — previously fieldH was
|
||||
// capFont+6px, shorter than the box, so they sat misaligned.
|
||||
const float fieldH = ImGui::GetFrameHeight();
|
||||
const float sideW = fieldH; // square -/+ buttons
|
||||
const float fieldW = 46.0f * dp;
|
||||
const float g = 2.0f * dp;
|
||||
@@ -544,7 +549,7 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
||||
s_benchConfirm = true;
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg),
|
||||
"Benchmark takes ~%ds and interrupts mining. Click again to start.",
|
||||
TR("grpc_benchmark_takes_secs"),
|
||||
(int)(s_benchmark.totalEstimatedSecs() + 0.5f));
|
||||
Notifications::instance().warning(msg);
|
||||
} else {
|
||||
|
||||
@@ -220,7 +220,7 @@ void RenderMiningEarnings(App* app, const WalletState& state, const MiningInfo&
|
||||
if (estActive)
|
||||
snprintf(estVal, sizeof(estVal), "~%.4f", estDaily);
|
||||
else
|
||||
snprintf(estVal, sizeof(estVal), "N/A");
|
||||
snprintf(estVal, sizeof(estVal), "%s", TR("grpc_na"));
|
||||
// Disclose in pool mode that Est. Daily is a rough solo-equivalent (before the pool fee), so the
|
||||
// number isn't silently mismatched to its plain "Est. Daily" label. (M-05)
|
||||
const char* estSub = (s_pool_mode && estActive) ? TR("mining_est_daily_pool_sub") : nullptr;
|
||||
@@ -475,7 +475,7 @@ void RenderMiningEarnings(App* app, const WalletState& state, const MiningInfo&
|
||||
else if (totalRAM > 0)
|
||||
snprintf(sysBuf, sizeof(sysBuf), "-- / %.0f GB", totalRAM / 1024.0);
|
||||
else
|
||||
snprintf(sysBuf, sizeof(sysBuf), "N/A");
|
||||
snprintf(sysBuf, sizeof(sysBuf), "%s", TR("grpc_na"));
|
||||
|
||||
float sysTextW = capFont->CalcTextSizeA(capFont->LegacySize, 10000, 0, sysBuf).x;
|
||||
float sysTextX = barX + barW - textPadX - sysTextW;
|
||||
|
||||
@@ -326,7 +326,7 @@ static void RenderLeftPoolCard(App* app, const WalletState& state, ImDrawList* d
|
||||
? it->second.feePercent
|
||||
: kp.feePercent;
|
||||
if (feePct >= 0.0)
|
||||
snprintf(right, sizeof(right), "%s %s%% fee", hrStr.c_str(),
|
||||
snprintf(right, sizeof(right), TR("grpc_hashrate_fee"), hrStr.c_str(),
|
||||
FormatFeePercent(feePct).c_str());
|
||||
else
|
||||
snprintf(right, sizeof(right), "%s", hrStr.c_str());
|
||||
@@ -421,9 +421,10 @@ void RenderMiningStats(const WalletState& state, const MiningInfo& mining,
|
||||
// centered ~1000dp band inside the panel's padded content region; the
|
||||
// glass panel itself still fills rightW, leftover -> side margin.
|
||||
const float rightContentW = rightW - pad * 2.0f;
|
||||
const float chartBandW = std::min(rightContentW, 1000.0f * dp);
|
||||
const float chartBandX = rightMin.x + pad
|
||||
+ std::max(0.0f, (rightContentW - chartBandW) * 0.5f);
|
||||
// Fill the panel's content width so the chart extends across the whole panel on a wide window.
|
||||
// (Previously capped at ~1000dp and centered, which left large empty margins either side.)
|
||||
const float chartBandW = rightContentW;
|
||||
const float chartBandX = rightMin.x + pad;
|
||||
|
||||
// Right panel: live log (if toggled + available) else the sparkline.
|
||||
if (showLogView) {
|
||||
|
||||
@@ -263,8 +263,7 @@ static void RenderMiningTabContent(App* app)
|
||||
}
|
||||
if (benchmarkUpdate.inconclusive) {
|
||||
Notifications::instance().warning(
|
||||
"Benchmark inconclusive: no hashrate samples were recorded. "
|
||||
"Check the pool connection and try again.");
|
||||
TR("grpc_benchmark_inconclusive"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -380,7 +380,7 @@ void RenderPeersTab(App* app)
|
||||
if (tlsCount == totalPeers) {
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
ImVec2 txtSize = sub1->CalcTextSizeA(sub1->LegacySize, FLT_MAX, 0, buf);
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx + txtSize.x + 4, valY), Success(), ICON_MD_CHECK);
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx + txtSize.x + 4 * dp, valY), Success(), ICON_MD_CHECK);
|
||||
}
|
||||
} else {
|
||||
dl->AddText(sub1, sub1->LegacySize, ImVec2(cx, valY), OnSurfaceDisabled(), "\xE2\x80\x94");
|
||||
@@ -695,7 +695,7 @@ void RenderPeersTab(App* app)
|
||||
ImVec2 pillMin(dirX - S.drawElement("tabs.peers", "dir-pill-padding").size * dp, cy + S.drawElement("tabs.peers", "dir-pill-y-offset").size * dp);
|
||||
ImVec2 pillMax(dirX + dirSz.x + S.drawElement("tabs.peers", "dir-pill-padding").size * dp, cy + capFont->LegacySize + S.drawElement("tabs.peers", "dir-pill-y-bottom").size * dp);
|
||||
dl->AddRectFilled(pillMin, pillMax, dirBg, S.drawElement("tabs.peers", "dir-pill-rounding").size * dp);
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(dirX, cy + 2), dirFg, dirLabel);
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(dirX, cy + 2 * dp), dirFg, dirLabel);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -745,9 +745,9 @@ void RenderPeersTab(App* app)
|
||||
ImU32 tlsBg = WithAlpha(Success(), 25);
|
||||
ImU32 tlsFg = WithAlpha(Success(), 200);
|
||||
ImVec2 tlsMin(badgeX, cy2);
|
||||
ImVec2 tlsMax(tlsMin.x + tlsBadgeW, tlsMin.y + capFont->LegacySize + 2);
|
||||
ImVec2 tlsMax(tlsMin.x + tlsBadgeW, tlsMin.y + capFont->LegacySize + 2 * dp);
|
||||
dl->AddRectFilled(tlsMin, tlsMax, tlsBg, S.drawElement("tabs.peers", "tls-badge-rounding").size * dp);
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(tlsMin.x + 4, cy2 + 1), tlsFg, "TLS");
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(tlsMin.x + 4 * dp, cy2 + 1 * dp), tlsFg, "TLS");
|
||||
} else {
|
||||
dl->AddText(capFont, capFont->LegacySize, ImVec2(badgeX, cy2),
|
||||
WithAlpha(Error(), 140), TR("peers_no_tls"));
|
||||
|
||||
@@ -259,11 +259,11 @@ static void RenderAddressDropdown(App* app, float width) {
|
||||
snprintf(buf, sizeof(buf), "%s %s (%s) \xe2\x80\x94 %.8f %s%s",
|
||||
tag, lblIt->second.c_str(), trunc.c_str(),
|
||||
addr.balance, DRAGONX_TICKER,
|
||||
isNew ? " [NEW]" : "");
|
||||
isNew ? TR("grpb_new_badge_suffix") : "");
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s %s \xe2\x80\x94 %.8f %s%s",
|
||||
tag, trunc.c_str(), addr.balance, DRAGONX_TICKER,
|
||||
isNew ? " [NEW]" : "");
|
||||
isNew ? TR("grpb_new_badge_suffix") : "");
|
||||
}
|
||||
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
@@ -272,9 +272,9 @@ static void RenderAddressDropdown(App* app, float width) {
|
||||
s_cached_qr_data.clear(); // Force QR regeneration
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
material::Tooltip("%s\nBalance: %.8f %s%s",
|
||||
material::Tooltip(TR("grpb_tooltip_address_balance"),
|
||||
addr.address.c_str(), addr.balance, DRAGONX_TICKER,
|
||||
isCurrent ? "\n(selected)" : "");
|
||||
isCurrent ? TR("grpb_selected_suffix") : "");
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
@@ -370,20 +370,25 @@ static void RenderRecentReceived(const AddressInfo& /* addr */,
|
||||
S.drawElement("tabs.balance", "recent-tx-icon-size").size * hs);
|
||||
ImU32 recvCol = Success();
|
||||
|
||||
// Collect matching transactions
|
||||
std::vector<const TransactionInfo*> recvs;
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "receive" && tx.type != "mined") continue;
|
||||
recvs.push_back(&tx);
|
||||
}
|
||||
|
||||
// Grow the list to fill the dead space beneath the (fixed-height) receive card:
|
||||
// fit as many newest-first rows as the remaining region can show, instead of a
|
||||
// fixed 4. The child scrolls if the real history exceeds what fits, so nothing is
|
||||
// lost. A floor of 4 keeps the section substantial when the region is short.
|
||||
// Compute maxRows BEFORE the collect loop so the scan can stop early (below).
|
||||
float listH = std::max(rowH, ImGui::GetContentRegionAvail().y);
|
||||
size_t maxRows = std::max<size_t>(4, (size_t)std::floor(listH / rowH));
|
||||
if (recvs.size() > maxRows) recvs.resize(maxRows); // newest-first
|
||||
|
||||
// Collect matching transactions. state.transactions is newest-first, so the first
|
||||
// maxRows matches ARE exactly the rows we render — stop scanning once we have them
|
||||
// instead of filtering the entire history every frame (mirrors RenderSharedRecentTx
|
||||
// in balance_components.cpp).
|
||||
std::vector<const TransactionInfo*> recvs;
|
||||
recvs.reserve(maxRows);
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "receive" && tx.type != "mined") continue;
|
||||
recvs.push_back(&tx);
|
||||
if (recvs.size() >= maxRows) break; // newest-first: prefix is all we show
|
||||
}
|
||||
|
||||
ImGui::BeginChild("##RecentReceivedRows", ImVec2(width, listH), false,
|
||||
ImGuiWindowFlags_NoBackground);
|
||||
@@ -716,17 +721,17 @@ void RenderReceiveTab(App* app)
|
||||
ImU32 iconCol = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
float ss = iconW * 0.5f;
|
||||
float cx = startX + ss;
|
||||
float ag = S.drawElement("tabs.receive", "swap-icon-arrow-gap").size;
|
||||
float ag = S.drawElement("tabs.receive", "swap-icon-arrow-gap").size * cardDp;
|
||||
float al = ss * S.drawElement("tabs.receive", "swap-icon-arrow-length-ratio").size;
|
||||
float hss = S.drawElement("tabs.receive", "swap-icon-arrowhead-size").size;
|
||||
float hss = S.drawElement("tabs.receive", "swap-icon-arrowhead-size").size * cardDp;
|
||||
float ay1 = cy - ag;
|
||||
dl->AddLine(ImVec2(cx - al, ay1), ImVec2(cx + al, ay1), iconCol, S.drawElement("tabs.receive", "swap-icon-line-thickness").size);
|
||||
dl->AddLine(ImVec2(cx - al, ay1), ImVec2(cx + al, ay1), iconCol, S.drawElement("tabs.receive", "swap-icon-line-thickness").size * cardDp);
|
||||
dl->AddTriangleFilled(
|
||||
ImVec2(cx + al, ay1),
|
||||
ImVec2(cx + al - hss, ay1 - hss),
|
||||
ImVec2(cx + al - hss, ay1 + hss), iconCol);
|
||||
float ay2 = cy + ag;
|
||||
dl->AddLine(ImVec2(cx + al, ay2), ImVec2(cx - al, ay2), iconCol, S.drawElement("tabs.receive", "swap-icon-line-thickness").size);
|
||||
dl->AddLine(ImVec2(cx + al, ay2), ImVec2(cx - al, ay2), iconCol, S.drawElement("tabs.receive", "swap-icon-line-thickness").size * cardDp);
|
||||
dl->AddTriangleFilled(
|
||||
ImVec2(cx - al, ay2),
|
||||
ImVec2(cx - al + hss, ay2 - hss),
|
||||
@@ -744,8 +749,8 @@ void RenderReceiveTab(App* app)
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
float chipRound = schema::UI().drawElement("tabs.receive", "chip-rounding").size;
|
||||
float chipGap = schema::UI().drawElement("tabs.receive", "chip-gap").size;
|
||||
float chipH = schema::UI().drawElement("tabs.receive", "chip-height").size;
|
||||
float chipGap = schema::UI().drawElement("tabs.receive", "chip-gap").size * cardDp;
|
||||
float chipH = schema::UI().drawElement("tabs.receive", "chip-height").size * cardDp;
|
||||
|
||||
struct Preset { const char* label; double amount; };
|
||||
Preset presets[] = {
|
||||
@@ -873,7 +878,7 @@ void RenderReceiveTab(App* app)
|
||||
RenderQRCode(s_qr_texture, qrSize);
|
||||
} else {
|
||||
ImGui::Dummy(ImVec2(qrSize, qrSize));
|
||||
ImVec2 textPos(qrPanelMin.x + totalQrSize * 0.5f - S.drawElement("tabs.receive", "qr-unavailable-text-offset").size,
|
||||
ImVec2 textPos(qrPanelMin.x + totalQrSize * 0.5f - S.drawElement("tabs.receive", "qr-unavailable-text-offset").size * cardDp,
|
||||
qrPanelMin.y + totalQrSize * 0.5f);
|
||||
dl->AddText(capFont, capFont->LegacySize, textPos,
|
||||
OnSurfaceDisabled(), TR("qr_unavailable"));
|
||||
|
||||
+22
-14
@@ -364,7 +364,8 @@ static void RenderAddressSuggestions(const WalletState& state, float width, cons
|
||||
if (suggestions.empty()) return;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(schema::UI().resolveColor(schema::UI().drawElement("tabs.send", "suggestion-bg-color").color)));
|
||||
float sugH = std::min((float)suggestions.size() * schema::UI().drawElement("tabs.send", "suggestion-row-height").size + schema::UI().drawElement("tabs.send", "suggestion-list-padding").size, schema::UI().drawElement("tabs.send", "suggestion-max-height").size);
|
||||
const float dp = Layout::dpiScale();
|
||||
float sugH = std::min((float)suggestions.size() * schema::UI().drawElement("tabs.send", "suggestion-row-height").size * dp + schema::UI().drawElement("tabs.send", "suggestion-list-padding").size * dp, schema::UI().drawElement("tabs.send", "suggestion-max-height").size * dp);
|
||||
ImGui::BeginChild(childId, ImVec2(width, sugH), true);
|
||||
for (size_t si = 0; si < suggestions.size(); si++) {
|
||||
int sugTrunc = (int)schema::UI().drawElement("tabs.send", "suggestion-trunc-len").size;
|
||||
@@ -387,13 +388,14 @@ static void RenderAddressSuggestions(const WalletState& state, float width, cons
|
||||
// ============================================================================
|
||||
static void RenderFeeTierSelector(const char* suffix = "") {
|
||||
auto& S = schema::UI();
|
||||
const float dp = Layout::dpiScale();
|
||||
const char* feeLabels[] = { TR("send_fee_low"), TR("send_fee_normal"), TR("send_fee_high") };
|
||||
const double feeValues[] = { FEE_LOW, FEE_NORMAL, FEE_HIGH };
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, schema::UI().drawElement("tabs.send", "fee-rounding").size);
|
||||
for (int fi = 0; fi < 3; fi++) {
|
||||
if (fi > 0) ImGui::SameLine(0, S.drawElement("tabs.send", "fee-tier-gap").size);
|
||||
if (fi > 0) ImGui::SameLine(0, S.drawElement("tabs.send", "fee-tier-gap").size * dp);
|
||||
bool active = (s_fee_tier == fi);
|
||||
if (active) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(IM_COL32(255, 255, 255, (int)S.drawElement("tabs.send", "fee-tier-active-bg-alpha").size)));
|
||||
@@ -515,7 +517,7 @@ static void RenderAmountBar(ImDrawList* dl, double available, float innerW,
|
||||
// Max button — use caption font to fit bar height
|
||||
ImGui::SameLine(0, gap);
|
||||
char maxId[32];
|
||||
snprintf(maxId, sizeof(maxId), "Max%s", suffix);
|
||||
snprintf(maxId, sizeof(maxId), "%s%s", TR("grpb_max"), suffix);
|
||||
if (TactileButton(maxId, ImVec2(maxBtnW, barH), capFont)) {
|
||||
s_amount = maxAmount;
|
||||
s_send_max = true;
|
||||
@@ -976,6 +978,7 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
bool is_valid_address, double available,
|
||||
const char* suffix = "") {
|
||||
auto& S = schema::UI();
|
||||
const float dp = Layout::dpiScale();
|
||||
const auto& state = app->getWalletState();
|
||||
double total = s_amount + s_fee;
|
||||
// Block spending from a view-only source (imported viewing key, no spending key) — it would only
|
||||
@@ -1065,12 +1068,12 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
if (ImGui::BeginPopup(confirmClearId)) {
|
||||
ImGui::Text("%s", TR("send_clear_fields"));
|
||||
ImGui::Spacing();
|
||||
if (TactileButton(TR("send_yes_clear"), ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-yes-width").size, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TR("send_yes_clear"), ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-yes-width").size * dp, 0), S.resolveFont("button"))) {
|
||||
ClearFormWithUndo();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (TactileButton(TR("send_keep"), ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-keep-width").size, 0), S.resolveFont("button"))) {
|
||||
if (TactileButton(TR("send_keep"), ImVec2(schema::UI().drawElement("tabs.send", "clear-confirm-keep-width").size * dp, 0), S.resolveFont("button"))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
@@ -1086,7 +1089,7 @@ static void RenderActionButtons(App* app, float width, float vScale,
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(WithAlpha(Warning(), (int)S.drawElement("tabs.send", "undo-btn-bg-alpha").size)));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(WithAlpha(Warning(), (int)S.drawElement("tabs.send", "undo-btn-hover-alpha").size)));
|
||||
char undoId[32];
|
||||
snprintf(undoId, sizeof(undoId), "Undo Clear%s", suffix);
|
||||
snprintf(undoId, sizeof(undoId), "%s%s", TR("grpb_undo_clear"), suffix);
|
||||
if (TactileButton(undoId, ImVec2(width, btnH), S.resolveFont("button"))) {
|
||||
RestoreFormSnapshot();
|
||||
Notifications::instance().info(TR("send_form_restored"));
|
||||
@@ -1117,20 +1120,25 @@ static void RenderRecentSends(const WalletState& state, float width, ImFont* cap
|
||||
S.drawElement("tabs.balance", "recent-tx-icon-size").size * hs);
|
||||
ImU32 sendCol = Error();
|
||||
|
||||
// Collect matching transactions
|
||||
std::vector<const TransactionInfo*> sends;
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "send" && tx.type != "shield") continue;
|
||||
sends.push_back(&tx);
|
||||
}
|
||||
|
||||
// Grow the list to fill the dead space beneath the (fixed-height) compose card:
|
||||
// fit as many newest-first rows as the remaining region can show, instead of a
|
||||
// fixed 4. The child scrolls if the real history exceeds what fits, so nothing is
|
||||
// lost. A floor of 4 keeps the section substantial when the region is short.
|
||||
// Row budget is hoisted above the collect loop (it only needs the remaining region
|
||||
// height + row height, no per-row state) so the scan can early-exit.
|
||||
float listH = std::max(rowH, ImGui::GetContentRegionAvail().y);
|
||||
size_t maxRows = std::max<size_t>(4, (size_t)std::floor(listH / rowH));
|
||||
if (sends.size() > maxRows) sends.resize(maxRows); // newest-first
|
||||
|
||||
// Collect matching transactions. state.transactions is newest-first, so scan only a
|
||||
// bounded prefix and stop once maxRows matches are gathered (mirrors the early-exit in
|
||||
// balance_components.cpp:RenderSharedRecentTx) instead of filtering the whole history.
|
||||
std::vector<const TransactionInfo*> sends;
|
||||
sends.reserve(maxRows);
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "send" && tx.type != "shield") continue;
|
||||
sends.push_back(&tx);
|
||||
if (sends.size() == maxRows) break; // newest-first: enough rows to fill the region
|
||||
}
|
||||
|
||||
ImGui::BeginChild("##RecentSendRows", ImVec2(width, listH), false,
|
||||
ImGuiWindowFlags_NoBackground);
|
||||
|
||||
@@ -216,7 +216,7 @@ void RenderSettingsWindow(App* app, bool* p_open)
|
||||
if (!skin.valid) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
|
||||
ImGui::BeginDisabled(true);
|
||||
std::string label = skin.name + " (invalid)";
|
||||
std::string label = skin.name + TR("swin_invalid_suffix");
|
||||
ImGui::Selectable(label.c_str(), false);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleColor();
|
||||
@@ -261,7 +261,7 @@ void RenderSettingsWindow(App* app, bool* p_open)
|
||||
ImGui::PushFont(material::Type().iconSmall());
|
||||
if (material::StyledButton(ICON_REFRESH_THEMES, ImVec2(0, 0))) {
|
||||
skinMgr.refresh();
|
||||
Notifications::instance().info("Theme list refreshed");
|
||||
Notifications::instance().info(TR("swin_theme_list_refreshed"));
|
||||
}
|
||||
ImGui::PopFont();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
@@ -425,14 +425,14 @@ void RenderSettingsWindow(App* app, bool* p_open)
|
||||
app->rpc()->getInfo([](const nlohmann::json& result, const std::string& error) {
|
||||
if (error.empty()) {
|
||||
std::string version = result.value("version", "unknown");
|
||||
std::string msg = "Connection successful!\ndragonxd version: " + version;
|
||||
std::string msg = std::string(TR("swin_connection_successful")) + version;
|
||||
Notifications::instance().success(msg);
|
||||
} else {
|
||||
Notifications::instance().error("Connection failed: " + error);
|
||||
Notifications::instance().error(std::string(TR("swin_connection_failed")) + error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Notifications::instance().error("RPC client not initialized");
|
||||
Notifications::instance().error(TR("swin_rpc_client_not_initialized"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,15 +455,15 @@ void RenderSettingsWindow(App* app, bool* p_open)
|
||||
if (error.empty()) {
|
||||
int start = result.value("start_height", 0);
|
||||
int end = result.value("stop_height", 0);
|
||||
std::string msg = "Rescan started from block " + std::to_string(start) +
|
||||
" to " + std::to_string(end);
|
||||
std::string msg = std::string(TR("swin_rescan_started_from_block")) + std::to_string(start) +
|
||||
TR("swin_rescan_to") + std::to_string(end);
|
||||
Notifications::instance().success(msg);
|
||||
} else {
|
||||
Notifications::instance().error("Rescan failed: " + error);
|
||||
Notifications::instance().error(std::string(TR("swin_rescan_failed")) + error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Notifications::instance().error("RPC client not initialized");
|
||||
Notifications::instance().error(TR("swin_rpc_client_not_initialized"));
|
||||
}
|
||||
}
|
||||
ImGui::TextDisabled(" %s", TR("settings_rescan_desc"));
|
||||
@@ -503,9 +503,9 @@ void RenderSettingsWindow(App* app, bool* p_open)
|
||||
if (doConfirm) {
|
||||
std::string ztx_file = util::Platform::getDragonXDataDir() + "ztx_history.json";
|
||||
if (util::Platform::deleteFile(ztx_file)) {
|
||||
Notifications::instance().success("Z-transaction history cleared");
|
||||
Notifications::instance().success(TR("swin_ztx_history_cleared"));
|
||||
} else {
|
||||
Notifications::instance().info("No history file found");
|
||||
Notifications::instance().info(TR("swin_no_history_file_found"));
|
||||
}
|
||||
s_confirm_clear_ztx = false;
|
||||
}
|
||||
@@ -568,7 +568,7 @@ void RenderSettingsWindow(App* app, bool* p_open)
|
||||
// Save/Cancel buttons
|
||||
if (material::StyledButton(TR("save"), ImVec2(saveBtn.width, 0), S.resolveFont(saveBtn.font))) {
|
||||
saveSettingsFromUI(app->settings());
|
||||
Notifications::instance().success("Settings saved");
|
||||
Notifications::instance().success(TR("swin_settings_saved"));
|
||||
*p_open = false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
@@ -124,7 +124,7 @@ void TransactionDetailsDialog::render(App* app)
|
||||
ImGui::SetNextItemWidth(txidInput.width); // negative = fill, reserving |width| px for the Copy button (a content-region margin, not raw px — must NOT be dpi-scaled)
|
||||
ImGui::InputText("##TxID", txid_buf, sizeof(txid_buf), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::SameLine();
|
||||
if (material::StyledButton("Copy##TxID", ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
||||
if (material::StyledButton((std::string(TR("grpb_copy")) + "##TxID").c_str(), ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
||||
ImGui::SetClipboardText(tx.txid.c_str());
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ void TransactionDetailsDialog::render(App* app)
|
||||
ImGui::InputText("##Address", addr_buf, sizeof(addr_buf), ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (material::StyledButton("Copy##Addr", ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
||||
if (material::StyledButton((std::string(TR("grpb_copy")) + "##Addr").c_str(), ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
||||
ImGui::SetClipboardText(tx.address.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
@@ -186,53 +187,106 @@ void RenderTransactionsTab(App* app)
|
||||
// Summary Cards — Received | Sent | Mined
|
||||
// ================================================================
|
||||
{
|
||||
int recvCount = 0, sendCount = 0, minedCount = 0;
|
||||
double recvTotal = 0.0, sendTotal = 0.0, minedTotal = 0.0;
|
||||
|
||||
// Identify autoshield legs (same txid with a "send" leg and a "receive"-to-z leg):
|
||||
// that pair is a single internal shielding move — shown as one "Shield" row in the
|
||||
// list below — not income or spending. Counting both legs double-counts the amount
|
||||
// into BOTH the Sent and Received totals, so the cards disagree with the list.
|
||||
// Mirror the list's pairing logic and exclude both legs from the totals.
|
||||
std::unordered_map<std::string, std::vector<size_t>> summaryTxidMap;
|
||||
for (size_t i = 0; i < state.transactions.size(); i++)
|
||||
summaryTxidMap[state.transactions[i].txid].push_back(i);
|
||||
std::vector<bool> isShieldLeg(state.transactions.size(), false);
|
||||
for (const auto& kv : summaryTxidMap) {
|
||||
if (kv.second.size() < 2) continue;
|
||||
int send_i = -1, recv_i = -1;
|
||||
for (size_t si : kv.second) {
|
||||
const auto& stx = state.transactions[si];
|
||||
if (stx.type == "send" && send_i < 0) send_i = (int)si;
|
||||
else if (stx.type == "receive" && recv_i < 0 &&
|
||||
!stx.address.empty() && stx.address[0] == 'z') recv_i = (int)si;
|
||||
}
|
||||
if (send_i >= 0 && recv_i >= 0) {
|
||||
isShieldLeg[send_i] = true;
|
||||
isShieldLeg[recv_i] = true;
|
||||
// The list shows the merged shield under the "Sent" filter, so count it there too —
|
||||
// otherwise the Sent card reads 0 while the Sent list is non-empty. Use the shielded
|
||||
// (receive-leg) amount, which is what the merged row displays.
|
||||
sendCount++;
|
||||
sendTotal += std::abs(state.transactions[recv_i].amount);
|
||||
// Content fingerprint over state.transactions, shared by the summary-card totals (below) and
|
||||
// the display-list memoization (further down). This is a cheap, allocation-free FNV-1a hash
|
||||
// computed ONCE per frame and folds in every field the *totals* and the *displayed rows*
|
||||
// depend on: tx count, last update time, and per-tx txid (drives autoshield pairing),
|
||||
// amount (drives the totals — bit-exact), confirmations, timestamp, type, and address. It is
|
||||
// sort-independent (s_sort_mode is NOT folded here); the display key mixes s_sort_mode on top
|
||||
// so a sort change re-sorts the display cache without invalidating the totals (which don't
|
||||
// depend on order). NB vs. the old display key: this additionally folds full txid + amount +
|
||||
// full type/address (not just first char), which the summary totals require — the old key
|
||||
// omitted them, so it could not have gated the totals correctly.
|
||||
std::uint64_t contentKey;
|
||||
{
|
||||
std::uint64_t h = 1469598103934665603ULL; // FNV-1a offset basis
|
||||
auto mix = [&h](std::uint64_t v) { h = (h ^ v) * 1099511628211ULL; };
|
||||
auto mixBytes = [&mix](const std::string& s) {
|
||||
mix(s.size());
|
||||
for (unsigned char c : s) mix(static_cast<std::uint64_t>(c));
|
||||
};
|
||||
mix(state.transactions.size());
|
||||
mix(static_cast<std::uint64_t>(state.last_tx_update));
|
||||
for (const auto& t : state.transactions) {
|
||||
mixBytes(t.txid);
|
||||
std::uint64_t amtBits = 0;
|
||||
std::memcpy(&amtBits, &t.amount, sizeof(double)); // bit-exact fold of the double
|
||||
mix(amtBits);
|
||||
mix(static_cast<std::uint64_t>(t.confirmations));
|
||||
mix(static_cast<std::uint64_t>(t.timestamp));
|
||||
mixBytes(t.type);
|
||||
mixBytes(t.address);
|
||||
}
|
||||
contentKey = h;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < state.transactions.size(); i++) {
|
||||
if (isShieldLeg[i]) continue; // internal shielding move — not received or sent
|
||||
const auto& tx = state.transactions[i];
|
||||
if (tx.type == "receive") {
|
||||
recvCount++;
|
||||
recvTotal += std::abs(tx.amount);
|
||||
} else if (tx.type == "send") {
|
||||
sendCount++;
|
||||
sendTotal += std::abs(tx.amount);
|
||||
} else if (tx.type == "generate" || tx.type == "immature" || tx.type == "mined") {
|
||||
minedCount++;
|
||||
minedTotal += std::abs(tx.amount);
|
||||
// Summary-card totals: Received / Sent / Mined (counts + amounts). These are three static
|
||||
// numbers that only change when the tx list changes, so memoize them behind contentKey and
|
||||
// recompute only when it moves. File-scope-style function statics persist across wallet
|
||||
// switches, but a switch/refresh mutates state.transactions (count, txids, amounts …), which
|
||||
// bumps contentKey, so the sentinel initial key + change detection covers that for free.
|
||||
static int s_recvCount = 0, s_sendCount = 0, s_minedCount = 0;
|
||||
static double s_recvTotal = 0.0, s_sendTotal = 0.0, s_minedTotal = 0.0;
|
||||
static std::uint64_t s_summaryKey = 0; // sentinel; first frame always recomputes
|
||||
|
||||
if (contentKey != s_summaryKey) {
|
||||
int recvCount = 0, sendCount = 0, minedCount = 0;
|
||||
double recvTotal = 0.0, sendTotal = 0.0, minedTotal = 0.0;
|
||||
|
||||
// Identify autoshield legs (same txid with a "send" leg and a "receive"-to-z leg):
|
||||
// that pair is a single internal shielding move — shown as one "Shield" row in the
|
||||
// list below — not income or spending. Counting both legs double-counts the amount
|
||||
// into BOTH the Sent and Received totals, so the cards disagree with the list.
|
||||
// Mirror the list's pairing logic and exclude both legs from the totals.
|
||||
std::unordered_map<std::string, std::vector<size_t>> summaryTxidMap;
|
||||
for (size_t i = 0; i < state.transactions.size(); i++)
|
||||
summaryTxidMap[state.transactions[i].txid].push_back(i);
|
||||
std::vector<bool> isShieldLeg(state.transactions.size(), false);
|
||||
for (const auto& kv : summaryTxidMap) {
|
||||
if (kv.second.size() < 2) continue;
|
||||
int send_i = -1, recv_i = -1;
|
||||
for (size_t si : kv.second) {
|
||||
const auto& stx = state.transactions[si];
|
||||
if (stx.type == "send" && send_i < 0) send_i = (int)si;
|
||||
else if (stx.type == "receive" && recv_i < 0 &&
|
||||
!stx.address.empty() && stx.address[0] == 'z') recv_i = (int)si;
|
||||
}
|
||||
if (send_i >= 0 && recv_i >= 0) {
|
||||
isShieldLeg[send_i] = true;
|
||||
isShieldLeg[recv_i] = true;
|
||||
// The list shows the merged shield under the "Sent" filter, so count it there too —
|
||||
// otherwise the Sent card reads 0 while the Sent list is non-empty. Use the shielded
|
||||
// (receive-leg) amount, which is what the merged row displays.
|
||||
sendCount++;
|
||||
sendTotal += std::abs(state.transactions[recv_i].amount);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < state.transactions.size(); i++) {
|
||||
if (isShieldLeg[i]) continue; // internal shielding move — not received or sent
|
||||
const auto& tx = state.transactions[i];
|
||||
if (tx.type == "receive") {
|
||||
recvCount++;
|
||||
recvTotal += std::abs(tx.amount);
|
||||
} else if (tx.type == "send") {
|
||||
sendCount++;
|
||||
sendTotal += std::abs(tx.amount);
|
||||
} else if (tx.type == "generate" || tx.type == "immature" || tx.type == "mined") {
|
||||
minedCount++;
|
||||
minedTotal += std::abs(tx.amount);
|
||||
}
|
||||
}
|
||||
|
||||
s_recvCount = recvCount; s_recvTotal = recvTotal;
|
||||
s_sendCount = sendCount; s_sendTotal = sendTotal;
|
||||
s_minedCount = minedCount; s_minedTotal = minedTotal;
|
||||
s_summaryKey = contentKey;
|
||||
}
|
||||
|
||||
// Render from the cached totals (identical values — only the recomputation is skipped).
|
||||
const int recvCount = s_recvCount, sendCount = s_sendCount, minedCount = s_minedCount;
|
||||
const double recvTotal = s_recvTotal, sendTotal = s_sendTotal, minedTotal = s_minedTotal;
|
||||
|
||||
float availWidth = ImGui::GetContentRegionAvail().x;
|
||||
float cardGap = cGap;
|
||||
float cardW = (availWidth - 2 * cardGap) / 3.0f;
|
||||
@@ -263,10 +317,10 @@ void RenderTransactionsTab(App* app)
|
||||
int idx_map[] = {-1, 1, 0, 2};
|
||||
int idx = idx_map[type_filter];
|
||||
float xOff = idx * (cardW + cardGap);
|
||||
ImVec2 acMin(origin.x + xOff, origin.y + cardH - 3);
|
||||
ImVec2 acMin(origin.x + xOff, origin.y + cardH - 3 * vs);
|
||||
ImVec2 acMax(origin.x + xOff + cardW, origin.y + cardH);
|
||||
ImU32 acCol = (type_filter == 1) ? redCol : (type_filter == 2) ? greenCol : goldCol;
|
||||
dl->AddRectFilled(acMin, acMax, acCol, 2.0f);
|
||||
dl->AddRectFilled(acMin, acMax, acCol, 2.0f * hs);
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(availWidth, cardH));
|
||||
@@ -346,31 +400,24 @@ void RenderTransactionsTab(App* app)
|
||||
//
|
||||
// This merge + sort is O(N log N) with several heap allocations, so it is MEMOIZED: it only
|
||||
// rebuilds when the underlying transactions actually change, not every frame. The cache key
|
||||
// is a cheap, allocation-free FNV-1a fingerprint over the fields that affect the displayed
|
||||
// rows (count, last update time, and each tx's confirmations / timestamp / type+address
|
||||
// first char). A new block bumps every confirmation, so the key changes and we rebuild;
|
||||
// between changes (the common case while the user reads/scrolls) we reuse the cache. The
|
||||
// result is already sorted newest-first by the refresh service, but we re-sort here to apply
|
||||
// the "pending first" ordering — also folded into the memoized build.
|
||||
// reuses the shared per-frame contentKey computed above (a cheap, allocation-free FNV-1a
|
||||
// fingerprint over count / last update time / every tx's txid+amount+confirmations+timestamp+
|
||||
// type+address) and mixes in s_sort_mode on top, so the display cache also rebuilds on a sort
|
||||
// change. contentKey already folds in more than the display list strictly needs (amount,
|
||||
// full txid/type/address) — a superset, so any change that would have flipped the old
|
||||
// per-first-char key still flips this one. A new block bumps every confirmation, so the key
|
||||
// changes and we rebuild; between changes (the common case while the user reads/scrolls) we
|
||||
// reuse the cache. The result is already sorted newest-first by the refresh service, but we
|
||||
// re-sort here to apply the "pending first" ordering — also folded into the memoized build.
|
||||
static std::vector<DisplayTx> s_display_cache;
|
||||
static std::uint64_t s_display_cache_key = 0;
|
||||
static bool s_display_cache_valid = false;
|
||||
|
||||
std::uint64_t displayKey;
|
||||
{
|
||||
std::uint64_t h = 1469598103934665603ULL; // FNV-1a offset basis
|
||||
auto mix = [&h](std::uint64_t v) { h = (h ^ v) * 1099511628211ULL; };
|
||||
mix(state.transactions.size());
|
||||
mix(static_cast<std::uint64_t>(state.last_tx_update));
|
||||
mix(static_cast<std::uint64_t>(s_sort_mode)); // re-sort the cache when the mode changes
|
||||
for (const auto& t : state.transactions) {
|
||||
mix(static_cast<std::uint64_t>(t.confirmations));
|
||||
mix(static_cast<std::uint64_t>(t.timestamp));
|
||||
mix(t.type.empty() ? 0u : static_cast<unsigned char>(t.type[0]));
|
||||
mix(t.address.empty() ? 0u : static_cast<unsigned char>(t.address[0]));
|
||||
}
|
||||
displayKey = h;
|
||||
}
|
||||
// Derive the display key from the shared contentKey (avoids a second full pass over the
|
||||
// transactions) plus the sort mode — the only display-affecting input contentKey omits.
|
||||
std::uint64_t displayKey =
|
||||
(contentKey ^ (static_cast<std::uint64_t>(s_sort_mode) + 0x9E3779B97F4A7C15ULL))
|
||||
* 1099511628211ULL;
|
||||
|
||||
if (!s_display_cache_valid || displayKey != s_display_cache_key) {
|
||||
s_display_cache.clear();
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
const float headH = Type().h6()->LegacySize + Layout::spacingXs() // framework h6 title
|
||||
+ Type().caption()->LegacySize + Layout::spacingSm() // intro + gap
|
||||
+ ctrlRow + Layout::spacingSm(); // sort control row + gap
|
||||
const float padV = 48.0f; // content-child padding (top+bottom) + margin
|
||||
const float padV = 48.0f * dp; // content-child padding (top+bottom) + margin
|
||||
// Size to content, but cap at a viewport fraction: with many wallets on a small / HiDPI
|
||||
// screen the content-sized card could exceed the window (the framework would clip a too-tall
|
||||
// card, footer and all). When capped, the wallet list flexes + scrolls and the controls
|
||||
|
||||
+420
-1
@@ -259,6 +259,23 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["chat_emoji_search"] = "Search emoji";
|
||||
strings_["chat_hide_hidden"] = "Hide hidden";
|
||||
strings_["chat_hidden_toast"] = "Conversation hidden — a new message brings it back";
|
||||
// Delete conversation (local cache) — revive-on-new-message vs delete & block
|
||||
strings_["chat_delete"] = "Delete conversation";
|
||||
strings_["chat_delete_title"] = "Delete conversation?";
|
||||
strings_["chat_delete_body_prefix"] = "Delete your local copy of the conversation with ";
|
||||
strings_["chat_delete_body_suffix"] = "?";
|
||||
strings_["chat_delete_revive_note"] = "\"Delete\" clears the history on this device. If they message you again, the conversation comes back.";
|
||||
strings_["chat_delete_local_note"] = "This only affects this device — the messages stay on the blockchain and the other person keeps their copy.";
|
||||
strings_["chat_delete_confirm"] = "Delete";
|
||||
strings_["chat_delete_block"] = "Delete & block";
|
||||
strings_["chat_deleted_toast"] = "Conversation deleted";
|
||||
strings_["chat_delete_failed"] = "Couldn't delete the conversation — nothing was changed.";
|
||||
strings_["chat_blocked_toast"] = "Conversation deleted & blocked";
|
||||
strings_["chat_blocked_manage"] = "Blocked";
|
||||
strings_["chat_blocked_title"] = "Blocked conversations";
|
||||
strings_["chat_blocked_desc"] = "Blocked conversations are removed and their messages are dropped — old and new — until you unblock. Unblocking re-imports the conversation from the chain.";
|
||||
strings_["chat_unblock"] = "Unblock";
|
||||
strings_["chat_unblocked_toast"] = "Conversation unblocked";
|
||||
strings_["chat_pick_contact"] = "Choose from contacts\xE2\x80\xA6";
|
||||
strings_["chat_no_z_contacts"] = "No shielded-address contacts yet";
|
||||
strings_["chat_copy_address_tip"] = "Click to copy address";
|
||||
@@ -730,6 +747,12 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["tt_tor"] = "Route daemon connections through the Tor network for anonymity";
|
||||
strings_["tt_keep_daemon"] = "Daemon will still stop when running the setup wizard";
|
||||
strings_["tt_stop_external"] = "Applies when connecting to a daemon\nyou started outside this wallet";
|
||||
strings_["stratum_host_section"] = "MINING POOL HOSTING";
|
||||
strings_["stratum_host"] = "Host a mining pool (stratum)";
|
||||
strings_["tt_stratum_host"] = "Run a RandomX stratum pool server on this node so other RandomX miners can point at this computer. Requires a v1.3.0+ node and a daemon restart to apply.";
|
||||
strings_["stratum_host_hint"] = "Miners connect to this computer on port 22769 (RPC port + 1000) with a RandomX stratum miner. Restart the daemon to apply.";
|
||||
strings_["stratum_allowip_hint"] = "Allow miners from IP or CIDR (blank = this computer only)";
|
||||
strings_["stratum_expose_warn"] = "Opens a mining port to the network you allow — only use on a trusted LAN.";
|
||||
strings_["tt_verbose"] = "Log detailed connection diagnostics,\ndaemon state, and port owner info\nto the Console tab";
|
||||
strings_["tt_mine_idle"] = "Automatically start mining when the\nsystem is idle (no keyboard/mouse input)";
|
||||
strings_["tt_idle_delay"] = "How long to wait before starting mining";
|
||||
@@ -1251,7 +1274,9 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["wallet_recovered_restore_sub"] = "Puts your largest untouched backup back in place, verbatim, then re-scans — slightly faster, but only as complete as that one file was. Your current file is kept as a dated backup either way.";
|
||||
strings_["wallet_recovered_notify"] = "Your wallet file needed a repair — your original was safely backed up. Open the app to review your options.";
|
||||
strings_["wallet_degraded_notify"] = "Your wallet opened in reduced-function mode: existing funds are safe and spendable, but creating new addresses and shielding are disabled. Back up your seed phrase and restore it to fully repair the wallet.";
|
||||
strings_["autoshield_by_node"] = "Auto-shield is handled by your node";
|
||||
strings_["autoshield_by_node"] = "Your node auto-shields mined coinbase for you.";
|
||||
strings_["autoshield_off_backup_seed"] = "Mined coinbase isn't being auto-shielded yet. Back up your seed phrase (Settings \xE2\x86\x92 Backup & Data) so your node can safely turn it on.";
|
||||
strings_["autoshield_off_generic"] = "Your node isn't auto-shielding mined coinbase right now.";
|
||||
// In-dialog recovery lifecycle (Offer → Working → Done/Failed) + disclosures.
|
||||
strings_["wallet_recovery_working_label"] = "Working";
|
||||
strings_["wallet_recovery_done"] = "Done";
|
||||
@@ -1535,6 +1560,189 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["about_version"] = "Version:";
|
||||
strings_["about_website"] = "Website";
|
||||
|
||||
// --- Help & FAQ ---
|
||||
strings_["faq"] = "FAQ";
|
||||
strings_["faq_title"] = "Help & FAQ";
|
||||
strings_["faq_intro"] = "Answers about your wallet and the DragonX node. Search, or browse by topic below.";
|
||||
strings_["faq_intro_lite"] = "Answers about your Lite wallet. Search, or browse by topic below.";
|
||||
strings_["faq_search_hint"] = "Search the FAQ\xE2\x80\xA6";
|
||||
strings_["faq_open_tooltip"] = "Help & FAQ";
|
||||
strings_["faq_no_results"] = "No results. Try a different search term.";
|
||||
strings_["faq_group_wallet"] = "Wallet";
|
||||
strings_["faq_group_daemon"] = "Daemon";
|
||||
|
||||
// Wallet subcategory titles
|
||||
strings_["faq_w_gs_title"] = "Getting Started";
|
||||
strings_["faq_w_addr_title"] = "Addresses & Privacy";
|
||||
strings_["faq_w_send_title"] = "Sending & Receiving";
|
||||
strings_["faq_w_bal_title"] = "Balance & Sync";
|
||||
strings_["faq_w_sec_title"] = "Security & Encryption";
|
||||
strings_["faq_w_seed_title"] = "Seed Phrase & Backup";
|
||||
strings_["faq_w_chat_title"] = "Chat & Contacts";
|
||||
strings_["faq_w_set_title"] = "Settings & Appearance";
|
||||
// Daemon subcategory titles
|
||||
strings_["faq_d_node_title"] = "The Full Node";
|
||||
strings_["faq_d_sync_title"] = "Sync & Blockchain";
|
||||
strings_["faq_d_mgmt_title"] = "Node Management";
|
||||
strings_["faq_d_upd_title"] = "Updating the Node";
|
||||
strings_["faq_d_mine_title"] = "Mining & Pools";
|
||||
strings_["faq_d_net_title"] = "Peers & Network";
|
||||
strings_["faq_d_perf_title"] = "Storage & Performance";
|
||||
strings_["faq_d_trbl_title"] = "Troubleshooting";
|
||||
|
||||
// Wallet > Getting Started
|
||||
strings_["faq_w_gs_1_q"] = "What is ObsidianDragon?";
|
||||
strings_["faq_w_gs_1_a"] = "ObsidianDragon is a full-node wallet for DragonX (DRGX). It manages your coins and, on the desktop build, runs a DragonX full node (the daemon) in the background so your wallet verifies the blockchain itself instead of trusting a third-party server.\n\nThink of it as two parts working together: the wallet (keys, balances, sending) and the node (the blockchain and network). The questions here are split the same way \xE2\x80\x94 see the Wallet and Daemon tabs above.";
|
||||
strings_["faq_w_gs_2_q"] = "How do I create a new wallet?";
|
||||
strings_["faq_w_gs_2_a"] = "On first launch the setup wizard creates a wallet for you automatically. New wallets are backed by a secret recovery phrase (a list of words). Write that phrase down and keep it offline \xE2\x80\x94 it is the only way to restore your funds if this computer is lost.\n\nYou can review or back up the phrase any time from Settings > Backup & Data > Seed phrase.";
|
||||
strings_["faq_w_gs_3_q"] = "How do I restore a wallet I already have?";
|
||||
strings_["faq_w_gs_3_a"] = "Use the seed-phrase restore flow if you have a recovery phrase. (On the full-node build you can also copy an existing wallet.dat file into the wallet's data directory before launch.)\n\nAfter restoring, the wallet re-scans to find your past transactions, so your balance and history may take a while to appear the first time.";
|
||||
strings_["faq_w_gs_4_q"] = "What does the first-run setup do?";
|
||||
strings_["faq_w_gs_4_a"] = "The wizard lets you pick an appearance/theme, set up encryption and a PIN, and \xE2\x80\x94 on the full-node build \xE2\x80\x94 optionally download a bootstrap to speed up the first sync. You can skip any step and change all of these later in Settings.";
|
||||
|
||||
// Wallet > Addresses & Privacy
|
||||
strings_["faq_w_addr_1_q"] = "What's the difference between transparent and shielded addresses?";
|
||||
strings_["faq_w_addr_1_a"] = "Transparent addresses (they start with R or t) work like most coins: the amounts and addresses are public on the blockchain.\n\nShielded addresses (z-addresses, starting with zs) use zero-knowledge cryptography \xE2\x80\x94 the amount, sender, and receiver are encrypted on-chain. Balances held in shielded addresses are private.";
|
||||
strings_["faq_w_addr_2_q"] = "Which address type should I use?";
|
||||
strings_["faq_w_addr_2_a"] = "Prefer shielded (z) addresses whenever possible \xE2\x80\x94 they keep your balance and payment history private. DragonX is a privacy coin and shielded is the default for received funds.\n\nUse a transparent address only when a service you interact with cannot handle shielded addresses.";
|
||||
strings_["faq_w_addr_3_q"] = "Should I reuse an address or make a new one?";
|
||||
strings_["faq_w_addr_3_a"] = "For shielded addresses, reuse is fine and does not leak history. For transparent addresses, using a fresh address per payment improves privacy. Create new addresses from the Receive tab.";
|
||||
strings_["faq_w_addr_4_q"] = "What is 'shielding'?";
|
||||
strings_["faq_w_addr_4_a"] = "Shielding moves coins from a transparent address into a shielded one, making them private. Newly mined coins arrive transparent and are shielded for you automatically.\n\nYou can also shield manually from the wallet; a shield is just a special transaction, so it takes a normal confirmation time to complete.";
|
||||
|
||||
// Wallet > Sending & Receiving
|
||||
strings_["faq_w_send_1_q"] = "How do I send funds?";
|
||||
strings_["faq_w_send_1_a"] = "Open the Send tab, paste the recipient's address, enter an amount, and confirm. If the wallet is encrypted you'll be asked to unlock it for the transaction.\n\nSends to shielded addresses are private; sends to transparent addresses are public.";
|
||||
strings_["faq_w_send_2_q"] = "What fee do I pay?";
|
||||
strings_["faq_w_send_2_a"] = "DragonX fees are very low and are set automatically. The fee is shown before you confirm. You do not normally need to change it.";
|
||||
strings_["faq_w_send_3_q"] = "Can I attach a message to a payment?";
|
||||
strings_["faq_w_send_3_a"] = "Yes \xE2\x80\x94 when sending to a shielded (z) address you can include an encrypted memo. Only the recipient can read it. Transparent addresses do not support memos.";
|
||||
strings_["faq_w_send_4_q"] = "I sent a payment but it says pending \xE2\x80\x94 why?";
|
||||
strings_["faq_w_send_4_a"] = "A transaction is 'pending' until it is mined into a block and gains confirmations. This usually takes a minute or two. Shielded transactions also need your wallet to be synced to build the proof.\n\nIf a send stays pending unusually long, check that your wallet is connected and synced (see the status bar at the bottom of the window).";
|
||||
|
||||
// Wallet > Balance & Sync
|
||||
strings_["faq_w_bal_1_q"] = "Why is my balance 0 or not updating?";
|
||||
strings_["faq_w_bal_1_a"] = "While your wallet is still syncing, your balance is incomplete and may read 0 \xE2\x80\x94 it hasn't scanned all of your transactions yet. It fills in once syncing finishes.\n\nWatch the status bar at the bottom: it shows the connection state and block height. Once syncing is complete, your balance is accurate.";
|
||||
strings_["faq_w_bal_2_q"] = "What are confirmations?";
|
||||
strings_["faq_w_bal_2_a"] = "Each new block mined on top of the block containing your transaction adds one confirmation. More confirmations mean the payment is more firmly settled. Received funds become spendable after the first confirmation.";
|
||||
strings_["faq_w_bal_3_q"] = "What's the difference between total and spendable balance?";
|
||||
strings_["faq_w_bal_3_a"] = "Total includes funds that are still confirming or are temporarily locked (for example, coins in the middle of being shielded). Spendable is what you can send right now. They converge as transactions confirm.";
|
||||
|
||||
// Wallet > Security & Encryption
|
||||
strings_["faq_w_sec_1_q"] = "How do I encrypt my wallet?";
|
||||
strings_["faq_w_sec_1_a"] = "Open Settings > Node & Security and set a passphrase (the SECURITY section). Encryption protects your private keys on disk, so someone with access to the files still cannot spend your coins.\n\nChoose a strong passphrase and don't lose it \xE2\x80\x94 there is no way to recover an encrypted wallet without it.";
|
||||
strings_["faq_w_sec_2_q"] = "What is the PIN / lock screen?";
|
||||
strings_["faq_w_sec_2_a"] = "The PIN locks the app's screen so balances and actions are hidden when you step away. It's a convenience lock on top of encryption; you set it up during the wizard or in Settings, after encrypting the wallet.";
|
||||
strings_["faq_w_sec_3_q"] = "I forgot my passphrase \xE2\x80\x94 can it be recovered?";
|
||||
strings_["faq_w_sec_3_a"] = "No. Wallet encryption cannot be bypassed. If you still have your seed recovery phrase, you can restore the wallet from it into a fresh wallet and set a new passphrase. Without either the passphrase or the seed phrase, the funds cannot be recovered.";
|
||||
|
||||
// Wallet > Seed Phrase & Backup
|
||||
strings_["faq_w_seed_1_q"] = "How do I back up my recovery phrase?";
|
||||
strings_["faq_w_seed_1_a"] = "Go to Settings > Backup & Data > Seed phrase. Write the words down on paper in order and store them somewhere safe and offline. Anyone with the phrase can spend your coins, so never store it in a photo, email, or cloud note.";
|
||||
strings_["faq_w_seed_2_q"] = "My wallet has no recovery phrase \xE2\x80\x94 can I add one?";
|
||||
strings_["faq_w_seed_2_a"] = "Older (legacy) wallets weren't seed-based. The wallet can migrate a legacy wallet into a modern seed-backed one: it creates a new seed wallet and sweeps your funds into it. Look for 'Migrate to seed\xE2\x80\xA6' in Settings > Backup & Data. This feature needs the current daemon version.";
|
||||
strings_["faq_w_seed_3_q"] = "How do I restore from my recovery phrase?";
|
||||
strings_["faq_w_seed_3_a"] = "Use the seed-restore flow when setting up a wallet and enter your words in order. Your wallet then re-scans to rebuild your balance and history, which can take a while the first time.";
|
||||
strings_["faq_w_seed_4_q"] = "Should I also back up wallet.dat?";
|
||||
strings_["faq_w_seed_4_a"] = "Your seed phrase is the primary backup and is enough to restore everything. A copy of your wallet file is a convenient secondary backup that also preserves labels and settings. Keep any backup offline and private.";
|
||||
|
||||
// Wallet > Chat & Contacts
|
||||
strings_["faq_w_chat_1_q"] = "What is the Chat feature?";
|
||||
strings_["faq_w_chat_1_a"] = "Chat is an encrypted, on-chain messenger built into the wallet. Messages are sent as private shielded memos, so only you and your contact can read them.";
|
||||
strings_["faq_w_chat_2_q"] = "How does my chat identity work?";
|
||||
strings_["faq_w_chat_2_a"] = "Your chat identity is derived from your wallet's recovery phrase, so it travels with your wallet \xE2\x80\x94 restore the wallet and your identity comes back. You don't create a separate account or password.";
|
||||
strings_["faq_w_chat_3_q"] = "How do contacts work?";
|
||||
strings_["faq_w_chat_3_a"] = "Add a contact by their address in the Contacts tab, optionally with a name and avatar. Contacts can be kept private to the current wallet or shared across all wallets you open on this computer.";
|
||||
strings_["faq_w_chat_4_q"] = "How do I hide, delete, or block a conversation?";
|
||||
strings_["faq_w_chat_4_a"] = "Open a conversation and use the icons in its header. Hide (the eye) drops it from the list but keeps every message \xE2\x80\x94 a new message un-hides it. Delete (the trash) opens a dialog: 'Delete' clears the history on this device (if that person messages you again, the conversation comes back), while 'Delete & block' also stops their future messages until you unblock them from the 'Blocked' list above the conversation list. Everything here is local to this device \xE2\x80\x94 the messages stay on the blockchain and the other person keeps their own copy, so it isn't an 'unsend'.";
|
||||
// ---- Lite-variant FAQ (ObsidianDragonLite): swapped-in answers + a Lite Wallet subcategory ----
|
||||
strings_["faq_l_gs_1_q"] = "What is ObsidianDragonLite?";
|
||||
strings_["faq_l_gs_1_a"] = "ObsidianDragonLite is the lightweight DragonX wallet. Instead of running a full node, it connects to a DragonX lite server, so it starts fast and doesn't download the whole blockchain.\n\nIt manages your keys, balances, and sending just like the full wallet \xE2\x80\x94 see the 'Lite Wallet' section below for how the server connection works and the privacy tradeoff.";
|
||||
strings_["faq_l_sec_1_a"] = "Open Settings > Wallet and set a passphrase in the security section. Encryption protects your private keys on disk, so someone with access to the files still cannot spend your coins.\n\nChoose a strong passphrase and don't lose it \xE2\x80\x94 there is no way to recover an encrypted wallet without it.";
|
||||
strings_["faq_l_lite_title"] = "Lite Wallet";
|
||||
strings_["faq_l_lite_1_q"] = "How is the Lite wallet different from the full wallet?";
|
||||
strings_["faq_l_lite_1_a"] = "The Lite wallet talks to a DragonX lite server instead of running its own full node. That makes it fast to start and light on disk and bandwidth, because it doesn't download or verify the entire blockchain itself. Your private keys always stay on your device \xE2\x80\x94 they are never sent to the server.";
|
||||
strings_["faq_l_lite_2_q"] = "How do I choose or change the server?";
|
||||
strings_["faq_l_lite_2_a"] = "Open Settings and pick a server. You can switch servers at any time and the wallet re-syncs from the new one. If a server is slow or unreachable, try another.";
|
||||
strings_["faq_l_lite_3_q"] = "Is the Lite wallet as private as the full node?";
|
||||
strings_["faq_l_lite_3_a"] = "Your keys never leave your device, and shielded amounts stay encrypted. But the lite server does see which addresses your wallet asks about and your IP address, so it can link your addresses together \xE2\x80\x94 a convenience-for-privacy tradeoff. For the most privacy, use the full-node wallet, which verifies the chain itself and doesn't reveal your addresses to a server.";
|
||||
|
||||
// Wallet > Settings & Appearance
|
||||
strings_["faq_w_set_1_q"] = "How do I change the theme?";
|
||||
strings_["faq_w_set_1_a"] = "Open Settings > Appearance to pick a theme and accent. Several light and dark skins are included, and effects like blur can be toggled off for lower-powered machines.";
|
||||
strings_["faq_w_set_2_q"] = "Can I change the language?";
|
||||
strings_["faq_w_set_2_a"] = "Yes \xE2\x80\x94 Settings > Appearance has a language selector. The wallet ships with several translations; anything not yet translated falls back to English.";
|
||||
strings_["faq_w_set_3_q"] = "The text is too small (or too large) \xE2\x80\x94 can I scale it?";
|
||||
strings_["faq_w_set_3_a"] = "Use the font-scale option in Settings > Appearance. It scales the whole interface, which is handy on high-resolution (HiDPI) displays where the app might otherwise render small.";
|
||||
strings_["faq_w_set_4_q"] = "How do I show fiat prices?";
|
||||
strings_["faq_w_set_4_a"] = "Enable price fetching in Settings. The wallet then shows an approximate fiat value alongside balances and can display a small market chart. Prices are informational only.";
|
||||
|
||||
// Daemon > The Full Node
|
||||
strings_["faq_d_node_1_q"] = "What is the daemon (dragonxd)?";
|
||||
strings_["faq_d_node_1_a"] = "The daemon, dragonxd, is the DragonX full node. It downloads and verifies the entire blockchain, relays transactions to the network, and answers the wallet's queries. Running your own node means you don't have to trust anyone else's server.";
|
||||
strings_["faq_d_node_2_q"] = "Embedded vs external node \xE2\x80\x94 what's the difference?";
|
||||
strings_["faq_d_node_2_a"] = "By default the wallet launches and manages its own bundled node (embedded) \xE2\x80\x94 you don't have to do anything. If you already run dragonxd yourself, the wallet can connect to that external node instead. The wallet detects which situation it's in and behaves accordingly.";
|
||||
strings_["faq_d_node_3_q"] = "Why run a full node at all?";
|
||||
strings_["faq_d_node_3_a"] = "A full node validates every rule of the blockchain independently, so your wallet trusts math instead of a third party. It also strengthens the network. The trade-off is disk space and an initial sync.";
|
||||
|
||||
// Daemon > Sync & Blockchain
|
||||
strings_["faq_d_sync_1_q"] = "What does 'Processing blocks' at startup mean?";
|
||||
strings_["faq_d_sync_1_a"] = "On launch the node loads and validates blocks before it can serve the wallet. The 'Processing blocks / Applying blocks to build the current chain state' screen is that warm-up. It's normal; the wallet becomes usable once the node finishes and reports its height.";
|
||||
strings_["faq_d_sync_2_q"] = "What is 'Rebuilding witness cache'?";
|
||||
strings_["faq_d_sync_2_a"] = "For your shielded funds the node keeps cryptographic 'witnesses' that let you spend privately. After certain restarts or updates it rebuilds this cache by re-scanning recent blocks. On a wallet with many transactions this can take several minutes and use extra memory.\n\nIt is not stuck \xE2\x80\x94 let it finish. Force-quitting in the middle just makes it restart the rebuild next time.";
|
||||
strings_["faq_d_sync_3_q"] = "Why is syncing slow when I'm almost caught up?";
|
||||
strings_["faq_d_sync_3_a"] = "Near the chain tip, work that scans your wallet (checking balances and notes) competes with the node for the same internal lock, which can slow down connecting the final blocks on a large wallet. Recent versions throttle that scanning so the node can catch up. Staying on a lighter tab (or just waiting) lets it finish faster.";
|
||||
strings_["faq_d_sync_4_q"] = "How long does the initial sync take?";
|
||||
strings_["faq_d_sync_4_a"] = "The first sync downloads and verifies the whole chain and can take a while depending on your connection and disk. You can speed it up dramatically by enabling the bootstrap download in the setup wizard, which fetches a recent verified copy of the chain data.";
|
||||
|
||||
// Daemon > Node Management
|
||||
strings_["faq_d_mgmt_1_q"] = "How do I start or stop the node?";
|
||||
strings_["faq_d_mgmt_1_a"] = "The embedded node starts automatically with the wallet and stops when appropriate, so you normally don't manage it by hand. Advanced controls (including restarting the daemon) live in Settings > Node & Security.";
|
||||
strings_["faq_d_mgmt_2_q"] = "What does 'close external daemon on exit' do?";
|
||||
strings_["faq_d_mgmt_2_a"] = "If you connect the wallet to a node you started yourself, this option decides whether closing the wallet also shuts that node down. Leave it off if you want your node to keep running after you close the wallet.";
|
||||
strings_["faq_d_mgmt_3_q"] = "Where is the blockchain and wallet data stored?";
|
||||
strings_["faq_d_mgmt_3_a"] = "Node and wallet data live in the DragonX data directory under your user profile (for example, in AppData on Windows or your home folder on Linux/macOS). The blockchain is the large part; keep enough free disk space for it to grow.";
|
||||
|
||||
// Daemon > Updating the Node
|
||||
strings_["faq_d_upd_1_q"] = "How do I update the node?";
|
||||
strings_["faq_d_upd_1_a"] = "Open Settings > Node & Security > Daemon binary and use 'Check for updates\xE2\x80\xA6'. The wallet downloads the latest verified node build and installs it; the new version takes effect the next time the daemon starts.";
|
||||
strings_["faq_d_upd_2_q"] = "Can I install a specific node version?";
|
||||
strings_["faq_d_upd_2_a"] = "Yes \xE2\x80\x94 the update dialog lists every release so you can pick a specific or older build. Be cautious downgrading: an older node may not accept blockchain data written by a newer one and could need a re-index.";
|
||||
strings_["faq_d_upd_3_q"] = "Is the update safe?";
|
||||
strings_["faq_d_upd_3_a"] = "Every downloaded node archive is verified against a checksum and a cryptographic signature before it's installed. If verification fails, the wallet refuses the update.";
|
||||
|
||||
// Daemon > Mining & Pools
|
||||
strings_["faq_d_mine_1_q"] = "How do I mine DragonX?";
|
||||
strings_["faq_d_mine_1_a"] = "The Mining tab lets you mine with your CPU. DragonX uses the RandomX algorithm, which is designed for regular processors. Set the number of threads and start \xE2\x80\x94 rewards accrue to your wallet.";
|
||||
strings_["faq_d_mine_2_q"] = "Solo vs pool mining \xE2\x80\x94 which should I choose?";
|
||||
strings_["faq_d_mine_2_a"] = "Solo mining sends rewards straight to you but pays only when you find a block, which is infrequent unless you have a lot of hashrate. Pool mining shares work with others for smaller, steadier payouts. The Mining tab supports both.";
|
||||
strings_["faq_d_mine_3_q"] = "How do I update the miner?";
|
||||
strings_["faq_d_mine_3_a"] = "The Mining tab's pool section has an 'Update miner\xE2\x80\xA6' button that downloads, verifies, and installs the latest optimized DragonX miner build. Like the node updater, each download is checksum- and signature-verified before install.";
|
||||
strings_["faq_d_mine_4_q"] = "Can I host a mining pool?";
|
||||
strings_["faq_d_mine_4_a"] = "The current node can run a built-in stratum server so other miners can point at your machine. There's a toggle for it under Settings > Node & Security. By default it only listens locally; exposing it to other computers requires opening it up deliberately.";
|
||||
|
||||
// Daemon > Peers & Network
|
||||
strings_["faq_d_net_1_q"] = "The wallet shows no peers or connections \xE2\x80\x94 what's wrong?";
|
||||
strings_["faq_d_net_1_a"] = "Right after launch the node needs a moment to find peers, so a brief '0 peers' is normal. If it persists, check your internet connection and that a firewall isn't blocking the node. The node finds peers automatically through built-in seeds.";
|
||||
strings_["faq_d_net_2_q"] = "Is my connection to the network encrypted?";
|
||||
strings_["faq_d_net_2_a"] = "Yes \xE2\x80\x94 DragonX nodes talk to each other over TLS, so peer connections are encrypted. The wallet also talks to its own node over a secure local channel.";
|
||||
|
||||
// Daemon > Storage & Performance
|
||||
strings_["faq_d_perf_1_q"] = "How much disk does the node use, and can I tune memory?";
|
||||
strings_["faq_d_perf_1_a"] = "The blockchain is the large item and grows over time, so keep several gigabytes free. The node uses a database cache for speed; the bundled defaults are tuned for typical machines. Advanced users can adjust the node's database cache by editing dbcache in its config file (DRAGONX.conf).";
|
||||
strings_["faq_d_perf_2_q"] = "How do I make the initial sync faster?";
|
||||
strings_["faq_d_perf_2_a"] = "Enable the bootstrap download (offered in the setup wizard) to fetch a recent, verified copy of the chain instead of validating every block from scratch. A fast disk (SSD) also helps a lot.";
|
||||
|
||||
// Daemon > Troubleshooting
|
||||
strings_["faq_d_trbl_1_q"] = "The node seems stuck on 'Activating best chain' \xE2\x80\x94 is it frozen?";
|
||||
strings_["faq_d_trbl_1_a"] = "Usually not. On a large wallet the node can spend several minutes rebuilding shielded witnesses or applying blocks, during which it looks paused and may be slow to answer. Give it time \xE2\x80\x94 the debug log (Settings) shows steady progress if it's working.\n\nAvoid force-quitting during this phase; it restarts the work from scratch next launch.";
|
||||
strings_["faq_d_trbl_2_q"] = "What is 'degraded mode'?";
|
||||
strings_["faq_d_trbl_2_a"] = "If a wallet file is recovered but part of its key data is missing, the node may open it in a limited 'degraded' mode where it can't derive new addresses or shield funds. The safest fix is to restore from your seed recovery phrase into a fresh wallet.";
|
||||
strings_["faq_d_trbl_3_q"] = "When should I re-index or re-scan?";
|
||||
strings_["faq_d_trbl_3_a"] = "A re-scan makes the wallet re-read the chain to rediscover your transactions (useful after importing keys). A re-index rebuilds the node's blockchain database and is only needed if that database is corrupted or incompatible after a version change. Both can take a while; start them from Settings > Node & Security.";
|
||||
strings_["faq_d_trbl_4_q"] = "The node is using a lot of memory \xE2\x80\x94 is that normal?";
|
||||
strings_["faq_d_trbl_4_a"] = "Memory use spikes during heavy work like a witness rebuild or an initial sync, then drops back down once it finishes. Sustained high memory at idle is unusual \xE2\x80\x94 restarting the wallet clears it. On a low-RAM machine, make sure other memory-heavy apps aren't competing.";
|
||||
|
||||
// --- Address Book Dialog ---
|
||||
strings_["address_book_add"] = "Add Address";
|
||||
strings_["address_book_add_new"] = "Add New";
|
||||
@@ -2349,6 +2557,217 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["explorer_hash_not_found"] = "No block or transaction found for this hash";
|
||||
strings_["explorer_not_connected"] = "Not connected to daemon — cannot look up a block or transaction hash";
|
||||
strings_["explorer_no_results"] = "No matching cached blocks";
|
||||
|
||||
// ---- i18n audit 2026-09: wrap previously-hardcoded UI strings ----
|
||||
// app_security.cpp — encryption / PIN / lock flow
|
||||
strings_["sec_restart_daemon_for_encryption"] = "Please restart your daemon for encryption to take effect.";
|
||||
strings_["sec_encrypting_wallet"] = "Encrypting wallet...";
|
||||
strings_["sec_wallet_encrypted_restarting_daemon"] = "Wallet encrypted. Restarting daemon...";
|
||||
strings_["sec_wallet_encrypted_successfully"] = "Wallet encrypted successfully";
|
||||
strings_["sec_encryption_failed_prefix"] = "Encryption failed: ";
|
||||
strings_["sec_wallet_encrypted_and_pin_set"] = "Wallet encrypted & PIN set";
|
||||
strings_["sec_wallet_encrypted_but_pin_vault_failed"] = "Wallet encrypted but PIN vault failed";
|
||||
strings_["sec_couldnt_lock_wallet"] = "Couldn't lock the wallet — it is still unlocked. Check the daemon connection.";
|
||||
strings_["sec_changing_passphrase"] = "Changing passphrase...";
|
||||
strings_["sec_passphrase_changed_successfully"] = "Passphrase changed successfully";
|
||||
strings_["sec_failed_prefix"] = "Failed: ";
|
||||
strings_["sec_encryption_did_not_complete"] = "Wallet encryption did not complete — your wallet is NOT encrypted. Open Settings to finish encrypting it.";
|
||||
strings_["sec_wallet_locked_title"] = "Wallet Locked";
|
||||
strings_["sec_too_many_attempts_wait"] = "Too many attempts. Wait %.0f seconds...";
|
||||
strings_["sec_mode_passphrase"] = " Passphrase";
|
||||
strings_["sec_use_passphrase_instead"] = "Use passphrase instead";
|
||||
strings_["sec_use_pin_instead"] = "Use PIN instead";
|
||||
strings_["sec_unlocking_fmt"] = "Unlocking%s";
|
||||
strings_["sec_unlock_button"] = "Unlock";
|
||||
strings_["sec_not_connected_to_daemon"] = "Not connected to daemon";
|
||||
strings_["sec_unlock_failed_prefix"] = "Unlock failed: ";
|
||||
strings_["sec_incorrect_passphrase_decrypt"] = "Incorrect passphrase";
|
||||
strings_["sec_importing_keys_rescanning"] = "Importing keys & rescanning blockchain — wallet is usable while this runs";
|
||||
strings_["sec_encrypted_backup_suffix"] = "\nEncrypted backup: wallet.dat.encrypted.bak";
|
||||
strings_["sec_wallet_decrypted_all_keys_imported"] = "Wallet decrypted successfully! All keys imported.";
|
||||
strings_["sec_total_elapsed_fmt"] = "Total elapsed: %dm %02ds";
|
||||
strings_["sec_verifying_passphrase"] = "Verifying passphrase...";
|
||||
strings_["sec_incorrect_passphrase_pin_setup"] = "Incorrect passphrase";
|
||||
strings_["sec_pin_set_successfully"] = "PIN set successfully";
|
||||
strings_["sec_failed_to_create_vault"] = "Failed to create vault";
|
||||
strings_["sec_not_connected_to_daemon_pin"] = "Not connected to daemon";
|
||||
strings_["sec_changing_pin"] = "Changing PIN...";
|
||||
strings_["sec_pin_changed_successfully"] = "PIN changed successfully";
|
||||
strings_["sec_incorrect_current_pin"] = "Incorrect current PIN";
|
||||
strings_["sec_internal_error_change_pin"] = "Internal error";
|
||||
strings_["sec_verifying_pin"] = "Verifying PIN...";
|
||||
strings_["sec_pin_removed"] = "PIN removed";
|
||||
strings_["sec_incorrect_pin_remove"] = "Incorrect PIN";
|
||||
strings_["sec_internal_error_remove_pin"] = "Internal error";
|
||||
|
||||
// app.cpp — wizard, seed backup, shutdown, daemon/miner lifecycle
|
||||
strings_["appx_creating_your_wallet"] = "Creating your wallet…";
|
||||
strings_["appx_create_failed_prefix"] = "Create failed: ";
|
||||
strings_["appx_back_up_seed_phrase_title"] = "Back up your seed phrase";
|
||||
strings_["appx_seed_backup_warning"] = "These 24 words are the ONLY way to restore your wallet. Write them down in order, store them offline, and never share them. If you lose them, your funds are gone forever.";
|
||||
strings_["appx_birthday_block_height"] = "Birthday (block height): %llu — back this up too.";
|
||||
strings_["appx_ive_written_it_down"] = "I've written it down";
|
||||
strings_["appx_copy"] = "Copy";
|
||||
strings_["appx_skip_anyway"] = "Skip anyway";
|
||||
strings_["appx_skip"] = "Skip";
|
||||
strings_["appx_seed_not_backed_up_warning"] = "You have not backed up your seed — funds could be lost. Skip anyway?";
|
||||
strings_["appx_confirm_your_backup"] = "Confirm your backup";
|
||||
strings_["appx_tap_words_in_order"] = "Tap the words in the correct order to confirm you saved them.";
|
||||
strings_["appx_progress_n_of_n"] = "Progress: %d / %d";
|
||||
strings_["appx_not_next_word"] = " — that's not the next word";
|
||||
strings_["appx_done"] = "Done";
|
||||
strings_["appx_wallet_created_and_backed_up"] = "Wallet created and backed up.";
|
||||
strings_["appx_back"] = "Back";
|
||||
strings_["appx_restoring_your_wallet"] = "Restoring your wallet…";
|
||||
strings_["appx_recovery_phrase_word_count"] = "Recovery phrase should be 24 words — you have %d.";
|
||||
strings_["appx_could_not_start_restore"] = "Could not start restore";
|
||||
strings_["appx_node_rebuilding_witness_cache"] = "Node is rebuilding its witness cache";
|
||||
strings_["appx_stopping_node_discards_rebuild"] = "Stopping the node now discards the in-progress rebuild and restarts it (several minutes) the next time you open the wallet. You can keep the node running instead.";
|
||||
strings_["appx_keep_node_running_and_quit"] = "Keep node running & quit";
|
||||
strings_["appx_stop_anyway_and_quit"] = "Stop anyway & quit";
|
||||
strings_["appx_cancel"] = "Cancel";
|
||||
strings_["appx_last_used_wallet_not_found_prefix"] = "Your last-used wallet file (";
|
||||
strings_["appx_last_used_wallet_not_found_suffix"] = ") was not found — opened the default wallet instead. If you moved it, restore it and switch back from the wallet list.";
|
||||
strings_["appx_wallet_open_failed_prefix"] = "Wallet open failed: ";
|
||||
strings_["appx_blockchain_rescan_complete"] = "Blockchain rescan complete";
|
||||
strings_["appx_miner_stopped_unexpectedly"] = "Miner stopped unexpectedly.";
|
||||
strings_["appx_miner_stopped_prefix"] = "Miner stopped: ";
|
||||
strings_["appx_pool_miner_connected_and_hashing"] = "Pool miner connected and hashing.";
|
||||
strings_["appx_bootstrap_complete_reconciling"] = "Bootstrap complete — reconciling your wallet with the new chain data.";
|
||||
strings_["appx_blockchain_data_deleted"] = "Blockchain data deleted (%d items). The daemon is restarting to re-sync from the network.";
|
||||
strings_["appx_invalid_payment_uri_prefix"] = "Invalid payment URI: ";
|
||||
strings_["appx_payment_request_loaded"] = "Payment request loaded";
|
||||
strings_["appx_fullnode_lifecycle_unavailable_lite"] = "Full-node lifecycle actions are unavailable in lite build";
|
||||
strings_["appx_blockchain_maintenance_in_progress"] = "A blockchain maintenance operation is already in progress.";
|
||||
strings_["appx_node_busy_restarting"] = "The node is busy restarting — try again in a moment.";
|
||||
strings_["appx_restarting_daemon_rescan_flag"] = "Restarting daemon with -rescan flag...";
|
||||
strings_["appx_restarting_daemon_zapwallettxes"] = "Restarting daemon with -zapwallettxes=2 (wallet repair)...";
|
||||
strings_["appx_no_bundled_daemon_to_install"] = "This build has no bundled daemon to install";
|
||||
strings_["appx_no_embedded_daemon_to_install"] = "This build has no embedded daemon to install";
|
||||
strings_["appx_daemon_reinstall_in_progress"] = "The daemon reinstall is already in progress.";
|
||||
strings_["appx_installing_bundled_daemon"] = "Installing bundled daemon — the node will stop, update, and restart...";
|
||||
strings_["appx_stopping_daemon_deleting_blockchain"] = "Stopping daemon and deleting blockchain data...";
|
||||
strings_["appx_stopping_pool_miner"] = "Stopping pool miner...";
|
||||
strings_["appx_disconnecting"] = "Disconnecting...";
|
||||
strings_["appx_sending_stop_command_to_daemon"] = "Sending stop command to daemon...";
|
||||
strings_["appx_cleaning_up"] = "Cleaning up...";
|
||||
strings_["appx_shutdown_complete"] = "Shutdown complete";
|
||||
strings_["appx_n_seconds"] = "%d seconds";
|
||||
strings_["appx_n_min_n_sec"] = "%d min %d sec";
|
||||
strings_["appx_still_status_prefix"] = "Still \"";
|
||||
strings_["appx_still_status_suffix"] = "\" — force quitting now may corrupt chain data.";
|
||||
strings_["appx_rebuilding_witness_cache_blocks_left"] = "Rebuilding witness cache %.0f%% — %d blocks left";
|
||||
strings_["appx_rebuilding_witness_cache_pct"] = "Rebuilding witness cache %.0f%%";
|
||||
strings_["appx_setting_initial_sapling_witnesses"] = "Setting initial Sapling witnesses %.0f%%";
|
||||
strings_["appx_rebuilding_sapling_note_witnesses"] = "Rebuilding Sapling note witnesses…";
|
||||
strings_["appx_syncing_pct_block_n_of_n"] = "Syncing %.1f%% — Block %d / %d";
|
||||
strings_["appx_last_block_n"] = "Last block: %d";
|
||||
strings_["appx_encrypting_wallet"] = "Encrypting wallet...";
|
||||
strings_["appx_waiting_for_daemon_to_encrypt_wallet"] = "Waiting for daemon to encrypt wallet...";
|
||||
strings_["appx_daemon_error"] = "Daemon Error";
|
||||
strings_["appx_use_settings_restart_daemon_hint"] = "Use Settings > Restart Daemon to try again";
|
||||
strings_["appx_copied_clipboard_autoclears"] = "Copied — clipboard auto-clears in 45s";
|
||||
strings_["appx_dragonxd_output"] = "dragonxd output";
|
||||
strings_["appx_theme_prefix"] = "Theme: ";
|
||||
strings_["appx_low_spec_mode_enabled"] = "Low-spec mode enabled";
|
||||
strings_["appx_low_spec_mode_disabled"] = "Low-spec mode disabled";
|
||||
strings_["appx_theme_effects_enabled"] = "Theme effects enabled";
|
||||
strings_["appx_theme_effects_disabled"] = "Theme effects disabled";
|
||||
strings_["appx_simple_background_enabled"] = "Simple background enabled";
|
||||
strings_["appx_simple_background_disabled"] = "Simple background disabled";
|
||||
|
||||
// balance_tab.cpp
|
||||
strings_["baltab_shielded_amount"] = "Shielded %.8f";
|
||||
strings_["baltab_transparent_amount"] = "Transparent %.8f";
|
||||
strings_["baltab_market_price_4dp"] = "Market: $%.4f";
|
||||
strings_["baltab_market_price_8dp"] = "Market: $%.8f";
|
||||
strings_["baltab_pct_of_total_zaddr"] = "%.0f%% of total · %d Z-addr";
|
||||
strings_["baltab_t_addresses_count"] = "%d T-addresses";
|
||||
strings_["baltab_pct_change_24h"] = "%s%.1f%% 24h";
|
||||
strings_["baltab_total_balance"] = "Total Balance";
|
||||
strings_["baltab_shielded"] = "Shielded";
|
||||
strings_["baltab_transparent"] = "Transparent";
|
||||
strings_["baltab_market"] = "Market";
|
||||
|
||||
// settings_window.cpp
|
||||
strings_["swin_invalid_suffix"] = " (invalid)";
|
||||
strings_["swin_theme_list_refreshed"] = "Theme list refreshed";
|
||||
strings_["swin_connection_successful"] = "Connection successful!\ndragonxd version: ";
|
||||
strings_["swin_connection_failed"] = "Connection failed: ";
|
||||
strings_["swin_rpc_client_not_initialized"] = "RPC client not initialized";
|
||||
strings_["swin_rescan_started_from_block"] = "Rescan started from block ";
|
||||
strings_["swin_rescan_to"] = " to ";
|
||||
strings_["swin_rescan_failed"] = "Rescan failed: ";
|
||||
strings_["swin_ztx_history_cleared"] = "Z-transaction history cleared";
|
||||
strings_["swin_no_history_file_found"] = "No history file found";
|
||||
strings_["swin_settings_saved"] = "Settings saved";
|
||||
|
||||
// settings_page.cpp / explorer_tab.cpp / block_info_dialog.cpp
|
||||
strings_["grpa_tab_appearance"] = "Appearance";
|
||||
strings_["grpa_tab_wallet"] = "Wallet";
|
||||
strings_["grpa_tab_backup_data"] = "Backup & Data";
|
||||
strings_["grpa_tab_node_security"] = "Node & Security";
|
||||
strings_["grpa_tab_explorer"] = "Explorer";
|
||||
strings_["grpa_tab_chat"] = "Chat";
|
||||
strings_["grpa_tab_about"] = "About";
|
||||
strings_["grpa_enter_private_key_to_import"] = "Enter a private key to import.";
|
||||
strings_["grpa_invalid_suffix"] = " (invalid)";
|
||||
strings_["grpa_seed_demo_chat"] = "Seed demo chat";
|
||||
strings_["grpa_dbg_addrman"] = "Peer address tracking and management";
|
||||
strings_["grpa_dbg_alert"] = "Alert system messages";
|
||||
strings_["grpa_dbg_bench"] = "Benchmark timings for operations";
|
||||
strings_["grpa_dbg_coindb"] = "Coin database read/write operations";
|
||||
strings_["grpa_dbg_db"] = "Berkeley DB operations";
|
||||
strings_["grpa_dbg_estimatefee"] = "Fee estimation algorithm";
|
||||
strings_["grpa_dbg_http"] = "HTTP RPC server activity";
|
||||
strings_["grpa_dbg_libevent"] = "Libevent networking library";
|
||||
strings_["grpa_dbg_lock"] = "Lock contention debugging";
|
||||
strings_["grpa_dbg_mempool"] = "Transaction memory pool activity";
|
||||
strings_["grpa_dbg_net"] = "Network connections and messages";
|
||||
strings_["grpa_dbg_paymentdisclosure"] = "Payment disclosure protocol";
|
||||
strings_["grpa_dbg_pow"] = "Proof-of-work mining activity";
|
||||
strings_["grpa_dbg_proxy"] = "SOCKS5 proxy connections";
|
||||
strings_["grpa_dbg_prune"] = "Block pruning operations";
|
||||
strings_["grpa_dbg_rand"] = "Random number generation";
|
||||
strings_["grpa_dbg_reindex"] = "Blockchain reindexing progress";
|
||||
strings_["grpa_dbg_rpc"] = "RPC command processing";
|
||||
strings_["grpa_dbg_selectcoins"] = "Coin selection for transactions";
|
||||
strings_["grpa_dbg_tor"] = "Tor integration and circuit info";
|
||||
strings_["grpa_dbg_zmq"] = "ZeroMQ notification system";
|
||||
strings_["grpa_dbg_zrpc"] = "Shielded (z-addr) RPC operations";
|
||||
strings_["grpa_sec_ago"] = "%lld sec ago";
|
||||
strings_["grpa_min_ago"] = "%lld min ago";
|
||||
strings_["grpa_hr_ago"] = "%lld hr ago";
|
||||
strings_["grpa_days_ago"] = "%lld days ago";
|
||||
strings_["grpa_showing_first_100_of"] = "... showing first 100 of %d";
|
||||
strings_["grpa_error_prefix"] = "Error: ";
|
||||
strings_["grpa_invalid_response_from_daemon"] = "Invalid response from daemon";
|
||||
strings_["grpa_current_block_paren"] = "(Current: %d)";
|
||||
strings_["grpa_unexpected_getblockhash_result"] = "unexpected getblockhash result";
|
||||
|
||||
// receive / chat / send / transaction_details
|
||||
strings_["grpb_new_badge_suffix"] = " [NEW]";
|
||||
strings_["grpb_tooltip_address_balance"] = "%s\nBalance: %.8f %s%s";
|
||||
strings_["grpb_selected_suffix"] = "\n(selected)";
|
||||
strings_["grpb_preview_msg_payment_through"] = "Did the payment go through? 🙂";
|
||||
strings_["grpb_preview_msg_yep_confirmed"] = "Yep — just confirmed ✅";
|
||||
strings_["grpb_preview_msg_sending_rest"] = "Sending the rest now 👍";
|
||||
strings_["grpb_max"] = "Max";
|
||||
strings_["grpb_undo_clear"] = "Undo Clear";
|
||||
strings_["grpb_copy"] = "Copy";
|
||||
|
||||
// bootstrap / mining_* / key_export
|
||||
strings_["grpc_bootstrap_not_initialized"] = "Bootstrap not initialized";
|
||||
strings_["grpc_bootstrap_failed"] = "Bootstrap failed";
|
||||
strings_["grpc_na"] = "N/A";
|
||||
strings_["grpc_key_not_available"] = "Key not available for this address";
|
||||
strings_["grpc_benchmark_takes_secs"] = "Benchmark takes ~%ds and interrupts mining. Click again to start.";
|
||||
strings_["grpc_hashrate_fee"] = "%s %s%% fee";
|
||||
strings_["grpc_benchmark_inconclusive"] = "Benchmark inconclusive: no hashrate samples were recorded. Check the pool connection and try again.";
|
||||
|
||||
// misc pre-existing missing keys
|
||||
strings_["copied_to_clipboard"] = "Copied to clipboard";
|
||||
|
||||
}
|
||||
|
||||
const char* I18n::translate(const char* key) const
|
||||
|
||||
@@ -66,7 +66,9 @@ std::vector<LiteServerEndpoint> defaultDragonXLiteServers()
|
||||
{"https://lite2.dragonx.is", "DragonX Lite 2", true},
|
||||
{"https://lite3.dragonx.is", "DragonX Lite 3", true},
|
||||
{"https://lite4.dragonx.is", "DragonX Lite 4", true},
|
||||
{"https://lite5.dragonx.is", "DragonX Lite 5", true}
|
||||
{"https://lite5.dragonx.is", "DragonX Lite 5", true},
|
||||
{"https://lite6.dragonx.is", "DragonX Lite 6", true},
|
||||
{"https://lite7.dragonx.is", "DragonX Lite 7", true}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+120
-8
@@ -1417,31 +1417,51 @@ void testNetworkRefreshRpcCollectors()
|
||||
});
|
||||
coreRpc.addResponse("getblockchaininfo", json{
|
||||
{"blocks", 150},
|
||||
{"headers", 155},
|
||||
{"headers", 150},
|
||||
{"bestblockhash", "core-best-150"},
|
||||
{"verificationprogress", 0.80},
|
||||
{"longestchain", 160},
|
||||
{"verificationprogress", 1.0},
|
||||
{"longestchain", 150}, // caught up → balance scan runs
|
||||
{"notarized", 145}
|
||||
});
|
||||
auto core = Refresh::collectCoreRefreshResult(coreRpc);
|
||||
// getblockchaininfo is issued FIRST — sync detection gates (and must not be delayed behind) the
|
||||
// O(mapWallet) balance scan. When caught up, the balance scan follows: display (minconf=0) +
|
||||
// spendable (minconf=1).
|
||||
EXPECT_TRUE(coreRpc.methodNames() == std::vector<std::string>({
|
||||
"z_gettotalbalance", "z_gettotalbalance", "getblockchaininfo" // display (minconf=0) + spendable (minconf=1)
|
||||
"getblockchaininfo", "z_gettotalbalance", "z_gettotalbalance"
|
||||
}));
|
||||
EXPECT_EQ(coreRpc.calls[0].params, json::array({0}));
|
||||
EXPECT_EQ(coreRpc.calls[1].params, json::array({1}));
|
||||
EXPECT_EQ(coreRpc.calls[1].params, json::array({0}));
|
||||
EXPECT_EQ(coreRpc.calls[2].params, json::array({1}));
|
||||
EXPECT_TRUE(core.balanceOk);
|
||||
EXPECT_TRUE(core.blockchainOk);
|
||||
EXPECT_NEAR(*core.totalBalance, 4.25, 0.00000001);
|
||||
EXPECT_EQ(*core.blocks, 150);
|
||||
EXPECT_EQ(*core.bestBlockHash, std::string("core-best-150"));
|
||||
EXPECT_EQ(*core.longestChain, 160);
|
||||
EXPECT_EQ(*core.longestChain, 150);
|
||||
|
||||
// When getblockchaininfo shows the node is behind (blocks < longestchain - 2), the balance scan is
|
||||
// skipped this cycle regardless of includeBalance — so sync-state (and hence kSyncProfile) updates
|
||||
// promptly instead of after the multi-second scan. Only getblockchaininfo is issued.
|
||||
MockRefreshRpc coreBehindRpc;
|
||||
coreBehindRpc.addResponse("z_gettotalbalance", json{{"total", "4.25000000"}});
|
||||
coreBehindRpc.addResponse("getblockchaininfo", json{
|
||||
{"blocks", 150}, {"headers", 160}, {"longestchain", 160}
|
||||
});
|
||||
auto coreBehind = Refresh::collectCoreRefreshResult(coreBehindRpc);
|
||||
EXPECT_TRUE(coreBehindRpc.methodNames() == std::vector<std::string>({"getblockchaininfo"}));
|
||||
EXPECT_FALSE(coreBehind.balanceOk);
|
||||
EXPECT_TRUE(coreBehind.blockchainOk);
|
||||
EXPECT_EQ(*coreBehind.blocks, 150);
|
||||
EXPECT_EQ(*coreBehind.longestChain, 160);
|
||||
|
||||
MockRefreshRpc coreFallbackRpc;
|
||||
coreFallbackRpc.addFailure("z_gettotalbalance", "wallet warming up");
|
||||
coreFallbackRpc.addResponse("getblockchaininfo", json{{"blocks", 8}, {"headers", 9}});
|
||||
auto partialCore = Refresh::collectCoreRefreshResult(coreFallbackRpc);
|
||||
// No longestchain in the response → not classified as "behind" → balance is still attempted (here it
|
||||
// fails). getblockchaininfo is still issued first.
|
||||
EXPECT_TRUE(coreFallbackRpc.methodNames() == std::vector<std::string>({
|
||||
"z_gettotalbalance", "z_gettotalbalance", "getblockchaininfo"
|
||||
"getblockchaininfo", "z_gettotalbalance", "z_gettotalbalance"
|
||||
}));
|
||||
EXPECT_FALSE(partialCore.balanceOk);
|
||||
EXPECT_TRUE(partialCore.blockchainOk);
|
||||
@@ -6986,6 +7006,97 @@ void testHushChatDatabase()
|
||||
scrub(dbPath); scrub(dbPath2);
|
||||
}
|
||||
|
||||
// Per-conversation local delete: (1) "delete, revive on new message" tombstones the removed messages so
|
||||
// a chain re-scan can't re-import them — but a NEW txid in the same conversation flows through; the
|
||||
// tombstone survives a DB reload. (2) "delete & block" removes the rows WITHOUT a tombstone and relies on
|
||||
// a blocked-cid predicate to drop messages while blocked; unblocking re-imports the conversation.
|
||||
void testHushChatDeleteConversation()
|
||||
{
|
||||
using namespace dragonx::chat;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const std::string dbPath = (fs::temp_directory_path() / "drgx_chat_del_test.sqlite").string();
|
||||
auto scrub = [](const std::string& p) { fs::remove(p); fs::remove(p + "-wal"); fs::remove(p + "-shm"); };
|
||||
scrub(dbPath);
|
||||
|
||||
ChatKeyPair alice, bob;
|
||||
ChatIdentityResult ra = deriveChatIdentityFromSecret("del-alice", alice, true);
|
||||
ChatIdentityResult rb = deriveChatIdentityFromSecret("del-bob", bob, true);
|
||||
|
||||
// Build one incoming (alice->bob) metadata entry. encryptOutgoing produces a fresh ciphertext each
|
||||
// call, but dedup/tombstone key off (txid, position) only, so re-using the same txids re-scans them.
|
||||
auto mkMeta = [&](const std::string& txid, const std::string& cid, const std::string& body) {
|
||||
std::string e, ct;
|
||||
EXPECT_TRUE(encryptOutgoing(alice, rb.public_key_hex, body, e, ct) == ChatCryptoStatus::Ok);
|
||||
HushChatTransactionMetadata m;
|
||||
m.txid = txid; m.type = HushChatHeaderType::Message; m.conversation_id = cid;
|
||||
m.reply_zaddr = "zs-alice"; m.sender_public_key_hex = ra.public_key_hex;
|
||||
m.secretstream_header_hex = e; m.payload_memo = ct; m.payload_position = 1;
|
||||
return m;
|
||||
};
|
||||
std::vector<HushChatTransactionMetadata> convX{ mkMeta("dx1", "conv-x", "x-one"),
|
||||
mkMeta("dx2", "conv-x", "x-two") };
|
||||
std::vector<HushChatTransactionMetadata> convY{ mkMeta("dy1", "conv-y", "y-one") };
|
||||
|
||||
// (1) Delete with revive-on-new-message.
|
||||
{
|
||||
ChatDatabase db(dbPath);
|
||||
EXPECT_TRUE(db.unlockWithSecret("del-seed"));
|
||||
ChatService svc; svc.setIdentity(bob); svc.setPersistence(&db);
|
||||
EXPECT_EQ(svc.ingest(convX, {}, 100), 2);
|
||||
EXPECT_EQ(svc.ingest(convY, {}, 100), 1);
|
||||
EXPECT_EQ((int)svc.store().size(), 3);
|
||||
|
||||
EXPECT_TRUE(svc.deleteConversation("conv-x", /*block=*/false));
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-x").size(), 0);
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-y").size(), 1); // untouched
|
||||
|
||||
EXPECT_EQ(svc.ingest(convX, {}, 100), 0); // re-scan: tombstoned, not re-imported
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-x").size(), 0);
|
||||
|
||||
std::vector<HushChatTransactionMetadata> convXnew{ mkMeta("dx3", "conv-x", "x-three") };
|
||||
EXPECT_EQ(svc.ingest(convXnew, {}, 200), 1); // NEW txid revives the thread
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-x").size(), 1);
|
||||
EXPECT_EQ(svc.store().conversation("conv-x")[0].body, std::string("x-three"));
|
||||
}
|
||||
|
||||
// (2) Tombstone survives a DB reload into a fresh service.
|
||||
{
|
||||
ChatDatabase db(dbPath);
|
||||
EXPECT_TRUE(db.unlockWithSecret("del-seed"));
|
||||
ChatService svc; svc.setIdentity(bob); svc.setPersistence(&db);
|
||||
svc.loadFromDatabase();
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-x").size(), 1); // only the revived dx3 persisted
|
||||
EXPECT_EQ(svc.store().conversation("conv-x")[0].body, std::string("x-three"));
|
||||
EXPECT_EQ(svc.ingest(convX, {}, 100), 0); // dx1/dx2 still tombstoned after reload
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-x").size(), 1);
|
||||
}
|
||||
|
||||
// (3) Delete & block, then unblock.
|
||||
{
|
||||
ChatDatabase db(dbPath);
|
||||
EXPECT_TRUE(db.unlockWithSecret("del-seed"));
|
||||
ChatService svc; svc.setIdentity(bob); svc.setPersistence(&db);
|
||||
svc.loadFromDatabase();
|
||||
bool blocked = false;
|
||||
svc.setBlockedPredicate([&](const std::string& cid) { return blocked && cid == "conv-y"; });
|
||||
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-y").size(), 1);
|
||||
blocked = true;
|
||||
EXPECT_TRUE(svc.deleteConversation("conv-y", /*block=*/true));
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-y").size(), 0);
|
||||
EXPECT_EQ(svc.ingest(convY, {}, 100), 0); // suppressed by the predicate (no tombstone)
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-y").size(), 0);
|
||||
|
||||
blocked = false; // unblock
|
||||
EXPECT_EQ(svc.ingest(convY, {}, 100), 1); // re-imported from chain
|
||||
EXPECT_EQ((int)svc.store().conversation("conv-y").size(), 1);
|
||||
EXPECT_EQ(svc.store().conversation("conv-y")[0].body, std::string("y-one"));
|
||||
}
|
||||
|
||||
scrub(dbPath);
|
||||
}
|
||||
|
||||
// Phase 4: outgoing memo construction round-trips through the receive parser + decrypt, and
|
||||
// ChatService compose/recordOutgoing echoes into the store.
|
||||
void testHushChatOutgoing()
|
||||
@@ -7436,6 +7547,7 @@ int main()
|
||||
testHushChatReceivePath();
|
||||
testHushChatService();
|
||||
testHushChatDatabase();
|
||||
testHushChatDeleteConversation();
|
||||
testHushChatOutgoing();
|
||||
testHushChatTransport();
|
||||
testHushChatShuffledReceive();
|
||||
|
||||
Reference in New Issue
Block a user