Goblin — Build 98
@@ -0,0 +1,23 @@
|
||||
name: Fetch patched nym SDK
|
||||
description: >
|
||||
Clone the patched nym workspace from our own mirror
|
||||
(git.us-ea.st/GRIN/nym, branch `goblin` = upstream nymtech/nym @ b6eb391 +
|
||||
Goblin's Android webpki-roots patch) into ../nym, so the
|
||||
`nym-sdk = { path = "../nym/sdk/rust/nym-sdk" }` dependency resolves.
|
||||
Self-hosted: no upstream-GitHub fetch and no patch-apply step.
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Clone patched nym
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
DEST="$(dirname "$GITHUB_WORKSPACE")/nym"
|
||||
if [ -e "$DEST/sdk/rust/nym-sdk/Cargo.toml" ]; then
|
||||
echo "nym already present at $DEST"
|
||||
exit 0
|
||||
fi
|
||||
rm -rf "$DEST"
|
||||
git clone --branch goblin --depth 1 https://git.us-ea.st/GRIN/nym.git "$DEST"
|
||||
echo "nym cloned from GRIN/nym@goblin -> $DEST"
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Build
|
||||
on: [push, pull_request]
|
||||
|
||||
# aws-lc-sys (pulled in by nym-sdk) builds AWS-LC, which needs NASM on native
|
||||
# Windows. Use the prebuilt NASM objects the crate ships so the runner doesn't
|
||||
# need NASM installed; harmless on Linux/macOS.
|
||||
env:
|
||||
AWS_LC_SYS_PREBUILT_NASM: 1
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: Linux Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
# nym-sdk is a path dep on ../nym; materialize it before building.
|
||||
- uses: ./.github/actions/fetch-nym
|
||||
- name: Release build
|
||||
run: cargo build --release
|
||||
|
||||
windows:
|
||||
name: Windows Build
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: ./.github/actions/fetch-nym
|
||||
- name: Release build
|
||||
run: cargo build --release
|
||||
|
||||
macos:
|
||||
name: MacOS Build
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: ./.github/actions/fetch-nym
|
||||
- name: Release build
|
||||
run: cargo build --release
|
||||
@@ -0,0 +1,145 @@
|
||||
# Release builds on native runners — one per platform, no cross-compilation
|
||||
# (nokhwa's camera backends want each platform's own SDK; see NEXT-STEPS judgment).
|
||||
#
|
||||
# Manually triggered (Actions → Release → Run workflow) against an existing tag
|
||||
# until a run has been validated end-to-end; then this can move to a tag trigger.
|
||||
# Android is built locally via scripts/android.sh for now — the gradle `ci`
|
||||
# flavor expects maven credentials this repository does not carry.
|
||||
name: Release
|
||||
|
||||
on:
|
||||
# macOS is DEFERRED until Linux/Windows/Android are polished — so this is
|
||||
# manual-dispatch only for now (no auto-build on release publish). When macOS
|
||||
# is back on the table, re-add `release: { types: [published] }` here and the
|
||||
# macOS job will attach a universal build to each release automatically.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Existing release tag to build and upload artifacts to (e.g. build27)"
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
TAG: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
# aws-lc-sys (via nym-sdk) needs NASM on native Windows; use its prebuilt NASM.
|
||||
AWS_LC_SYS_PREBUILT_NASM: 1
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: Linux x86_64
|
||||
runs-on: ubuntu-latest
|
||||
# Built locally and uploaded with the release; only run on manual dispatch.
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
submodules: recursive
|
||||
- uses: ./.github/actions/fetch-nym
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: GOBLIN_BUILD="${TAG#build}" cargo build --release
|
||||
- name: Package
|
||||
run: |
|
||||
tar -C target/release -czf "goblin-$TAG-linux-x86_64.tar.gz" goblin
|
||||
sha256sum "goblin-$TAG-linux-x86_64.tar.gz" > "goblin-$TAG-linux-x86_64-sha256sum.txt"
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
files: |
|
||||
goblin-${{ inputs.tag || github.event.release.tag_name }}-linux-x86_64.tar.gz
|
||||
goblin-${{ inputs.tag || github.event.release.tag_name }}-linux-x86_64-sha256sum.txt
|
||||
|
||||
windows:
|
||||
name: Windows x86_64 (MSVC)
|
||||
runs-on: windows-latest
|
||||
# Built locally and uploaded with the release; only run on manual dispatch.
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
submodules: recursive
|
||||
- uses: ./.github/actions/fetch-nym
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: GOBLIN_BUILD="${TAG#build}" cargo build --release
|
||||
- name: Build MSI installer (cargo-wix / WiX 3 — same packaging as GRIM)
|
||||
shell: pwsh
|
||||
run: |
|
||||
# The .msi is built from wix/main.wxs (the cargo-wix default template:
|
||||
# WixUI_Minimal + launch-after-install), so `cargo wix` wires up the
|
||||
# WixUI/WixUtil extensions, cultures and CargoTargetBinDir for us. The
|
||||
# installer + shortcuts + Add/Remove-Programs entry carry wix/Product.ico
|
||||
# (the yellow Goblin icon). --no-build reuses the release exe above so the
|
||||
# embedded GOBLIN_BUILD number is preserved.
|
||||
cargo install cargo-wix --locked
|
||||
$wix = Get-ChildItem 'C:\Program Files (x86)' -Directory -Filter 'WiX Toolset v3*' -ErrorAction SilentlyContinue | Select-Object -Last 1
|
||||
if (-not $wix) {
|
||||
choco install wixtoolset --no-progress -y | Out-Null
|
||||
$wix = Get-ChildItem 'C:\Program Files (x86)' -Directory -Filter 'WiX Toolset v3*' | Select-Object -Last 1
|
||||
}
|
||||
$env:WIX = "$($wix.FullName)\"
|
||||
$env:PATH = "$($wix.FullName)\bin;$env:PATH"
|
||||
$msi = "goblin-$env:TAG-win-x86_64.msi"
|
||||
cargo wix --no-build --nocapture -o "$msi"
|
||||
if ($LASTEXITCODE -ne 0 -or -not (Test-Path "$msi")) { throw "cargo wix failed to produce $msi" }
|
||||
(Get-FileHash "$msi" -Algorithm SHA256).Hash.ToLower() + " $msi" | Out-File -Encoding ascii "goblin-$env:TAG-win-x86_64-msi-sha256sum.txt"
|
||||
- name: Package portable zip
|
||||
shell: bash
|
||||
run: |
|
||||
7z a "goblin-$TAG-win-x86_64.zip" ./target/release/goblin.exe
|
||||
sha256sum "goblin-$TAG-win-x86_64.zip" > "goblin-$TAG-win-x86_64-sha256sum.txt"
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
files: |
|
||||
goblin-${{ inputs.tag || github.event.release.tag_name }}-win-x86_64.msi
|
||||
goblin-${{ inputs.tag || github.event.release.tag_name }}-win-x86_64-msi-sha256sum.txt
|
||||
goblin-${{ inputs.tag || github.event.release.tag_name }}-win-x86_64.zip
|
||||
goblin-${{ inputs.tag || github.event.release.tag_name }}-win-x86_64-sha256sum.txt
|
||||
|
||||
macos:
|
||||
name: macOS universal
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
submodules: recursive
|
||||
- uses: ./.github/actions/fetch-nym
|
||||
- name: Build both architectures
|
||||
run: |
|
||||
export GOBLIN_BUILD="${TAG#build}"
|
||||
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
cargo build --release --target aarch64-apple-darwin
|
||||
cargo build --release --target x86_64-apple-darwin
|
||||
- name: Universal binary into Goblin.app bundle
|
||||
run: |
|
||||
# Combine both arches into one universal Mach-O and drop it into the
|
||||
# app bundle's executable slot (CFBundleExecutable=goblin).
|
||||
lipo -create -output goblin \
|
||||
target/aarch64-apple-darwin/release/goblin \
|
||||
target/x86_64-apple-darwin/release/goblin
|
||||
cp goblin macos/Goblin.app/Contents/MacOS/goblin
|
||||
chmod +x macos/Goblin.app/Contents/MacOS/goblin
|
||||
# Drop the placeholder that kept the empty dir tracked in git.
|
||||
rm -f macos/Goblin.app/Contents/MacOS/.gitignore
|
||||
# Ad-hoc sign (no Apple cert in CI). REQUIRED on Apple Silicon: lipo
|
||||
# strips the per-arch signatures cargo/ld add, and an unsigned arm64
|
||||
# Mach-O is killed by the OS. Ad-hoc gives a valid (if unidentified)
|
||||
# signature; users still right-click → Open past Gatekeeper.
|
||||
codesign --force --sign - macos/Goblin.app/Contents/MacOS/goblin
|
||||
codesign --force --sign - macos/Goblin.app
|
||||
# ditto is the macOS-correct way to zip an .app (preserves the bundle
|
||||
# layout, symlinks and permissions; plain `zip` mangles bundles).
|
||||
ditto -c -k --keepParent macos/Goblin.app "goblin-$TAG-macos-universal.zip"
|
||||
shasum -a 256 "goblin-$TAG-macos-universal.zip" > "goblin-$TAG-macos-universal-sha256sum.txt"
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
files: |
|
||||
goblin-${{ inputs.tag || github.event.release.tag_name }}-macos-universal.zip
|
||||
goblin-${{ inputs.tag || github.event.release.tag_name }}-macos-universal-sha256sum.txt
|
||||
@@ -0,0 +1,26 @@
|
||||
*.iml
|
||||
android/build
|
||||
android/.idea
|
||||
android/.gradle
|
||||
android/local.properties
|
||||
android/keystore
|
||||
android/keystore.asc
|
||||
android/keystore.properties
|
||||
android/*.apk
|
||||
android/*sha256sum.txt
|
||||
/.idea
|
||||
.DS_Store
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
*.so
|
||||
target
|
||||
.cargo/
|
||||
app/src/main/jniLibs
|
||||
macos/cert.pem
|
||||
linux/Goblin.AppDir/AppRun
|
||||
.intentionally-empty-file.o
|
||||
Cargo.toml-e
|
||||
screenshots/
|
||||
# GRIM-canonical build toolchains fetched by scripts/toolchain.sh
|
||||
.toolchains/
|
||||
@@ -0,0 +1,7 @@
|
||||
[submodule "node"]
|
||||
path = node
|
||||
url = https://code.gri.mw/ardocrat/node
|
||||
[submodule "wallet"]
|
||||
path = wallet
|
||||
url = https://code.gri.mw/ardocrat/wallet
|
||||
branch = grim
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2026 The Grim Developers
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
rustfmt --version &>/dev/null
|
||||
if [ $? != 0 ]; then
|
||||
printf "[pre_commit] \033[0;31merror\033[0m: \"rustfmt\" not available. \n"
|
||||
printf "[pre_commit] \033[0;31merror\033[0m: rustfmt can be installed via - \n"
|
||||
printf "[pre_commit] $ rustup component add rustfmt-preview \n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
problem_files=()
|
||||
|
||||
# first collect all the files that need reformatting
|
||||
for file in $(git diff --name-only --cached); do
|
||||
if [ ${file: -3} == ".rs" ]; then
|
||||
rustfmt --check $file &>/dev/null
|
||||
if [ $? != 0 ]; then
|
||||
problem_files+=($file)
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#problem_files[@]} == 0 ]; then
|
||||
# nothing to do
|
||||
printf "[pre_commit] rustfmt \033[0;32mok\033[0m \n"
|
||||
else
|
||||
# reformat the files that need it and re-stage them.
|
||||
printf "[pre_commit] the following files were rustfmt'd before commit: \n"
|
||||
for file in ${problem_files[@]}; do
|
||||
rustfmt $file
|
||||
git add $file
|
||||
printf "\033[0;32m $file\033[0m \n"
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,186 @@
|
||||
[package]
|
||||
name = "grim"
|
||||
version = "0.3.6"
|
||||
authors = ["Ardocrat <ardocrat@gri.mw>"]
|
||||
description = "Goblin: a peer-to-peer wallet for Grin. Send and receive instantly with a handle - slatepacks and the Nym mixnet handled for you."
|
||||
license = "Apache-2.0"
|
||||
repository = "https://code.gri.mw/GUI/grim"
|
||||
keywords = [ "crypto", "grin", "mimblewimble", "nostr" ]
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "goblin"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
name="grim"
|
||||
crate-type = ["rlib"]
|
||||
|
||||
# Desktop/CI release binaries ship stripped of debug symbols — the nym + nostr +
|
||||
# grin tree leaves a large symbol table that's dead weight for users (~16 MB on
|
||||
# Linux). opt-level stays at the default 3 for wallet/runtime speed.
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
[profile.release-apk]
|
||||
inherits = "release"
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.27"
|
||||
|
||||
# node
|
||||
grin_api = { path = "node/api" }
|
||||
grin_chain = { path = "node/chain" }
|
||||
grin_config = { path = "node/config" }
|
||||
grin_core = { path = "node/core" }
|
||||
grin_p2p = { path = "node/p2p" }
|
||||
grin_servers = { path = "node/servers" }
|
||||
grin_keychain = { path = "node/keychain" }
|
||||
grin_util = { path = "node/util" }
|
||||
|
||||
# wallet
|
||||
grin_wallet_impls = { path = "wallet/impls" }
|
||||
grin_wallet_api = { path = "wallet/api"}
|
||||
grin_wallet_libwallet = { path = "wallet/libwallet" }
|
||||
grin_wallet_util = { path = "wallet/util" }
|
||||
grin_wallet_controller = { path = "wallet/controller" }
|
||||
|
||||
## ui
|
||||
egui = { version = "0.33.3", default-features = false }
|
||||
egui_extras = { version = "0.33.3", features = ["image", "svg"] }
|
||||
egui-async = "0.3.4"
|
||||
rust-i18n = "3.1.5"
|
||||
|
||||
## other
|
||||
log4rs = "1.4.0"
|
||||
anyhow = "1.0.97"
|
||||
pin-project = "1.1.10"
|
||||
backtrace = "0.3.76"
|
||||
thiserror = "2.0.18"
|
||||
futures = "0.3.31"
|
||||
dirs = "6.0.0"
|
||||
sys-locale = "0.3.2"
|
||||
chrono = "0.4.43"
|
||||
parking_lot = "0.12.3"
|
||||
lazy_static = "1.5.0"
|
||||
toml = "0.9.11+spec-1.1.0"
|
||||
serde = "1.0.228"
|
||||
local-ip-address = "0.6.9"
|
||||
url = "2.5.8"
|
||||
rand = "0.9.2"
|
||||
serde_derive = "1.0.228"
|
||||
serde_json = "1.0.149"
|
||||
tokio = { version = "1.49.0", features = ["full"] }
|
||||
image = "0.25.9"
|
||||
rqrr = "0.10.1"
|
||||
qrcodegen = "1.8.0"
|
||||
qrcode = "0.14.1"
|
||||
ur = "0.4.1"
|
||||
gif = "0.14.1"
|
||||
rkv = "0.20.0"
|
||||
usvg = "0.45.1"
|
||||
ring = "0.16.20"
|
||||
hyper = { version = "1.6.0", features = ["full"], package = "hyper" }
|
||||
hyper-util = { version = "0.1.19", features = ["http1", "client", "client-legacy"] }
|
||||
http-body-util = "0.1.3"
|
||||
bytes = "1.11.0"
|
||||
hyper-socks2 = "0.9.1"
|
||||
hyper-proxy2 = "0.1.0"
|
||||
hyper-tls = "0.6.0"
|
||||
async-std = "1.13.2"
|
||||
uuid = { version = "0.8.2", features = ["v4"] }
|
||||
num-bigint = "0.4.6"
|
||||
|
||||
## nostr
|
||||
nostr-sdk = { version = "0.44", features = ["nip06", "nip44", "nip49", "nip59", "nip98"] }
|
||||
nostr-relay-pool = "0.44"
|
||||
async-wsocket = "0.13"
|
||||
tokio-tungstenite = { version = "0.26", features = ["rustls-tls-webpki-roots"] }
|
||||
regex = "1"
|
||||
base64 = "0.22"
|
||||
hex = "0.4"
|
||||
## HTTP client routed through the local Nym SOCKS5 sidecar (rustls, no native
|
||||
## TLS so it cross-compiles to Android; `socks` so every request — NIP-05,
|
||||
## price, avatars — goes over the mixnet, never clearnet).
|
||||
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "socks"] }
|
||||
## SOCKS5 TCP dialer for the nostr relay WebSocket transport over the mixnet.
|
||||
tokio-socks = "0.5"
|
||||
|
||||
## rustls is pulled by both our TLS (tungstenite/reqwest, ring) and nym-sdk
|
||||
## (aws-lc-rs); with two providers present rustls 0.23 can't auto-pick a default,
|
||||
## so we install ring explicitly at startup (see lib.rs). Direct dep just to make
|
||||
## `rustls::crypto::ring::default_provider()` reachable.
|
||||
rustls = { version = "0.23", features = ["ring"] }
|
||||
|
||||
## Nym mixnet, linked IN-PROCESS (no sidecar subprocess, no bundled binary). We
|
||||
## run the SDK's SOCKS5 client on an internal tokio task exposing 127.0.0.1:1080,
|
||||
## the same loopback seam the transport already dials. Path dep: the local nym
|
||||
## checkout carries our Android webpki-roots patch.
|
||||
nym-sdk = { path = "../nym/sdk/rust/nym-sdk" }
|
||||
|
||||
## NIP-98 payload hashing
|
||||
sha2 = "0.10.8"
|
||||
|
||||
## stratum server
|
||||
tokio-old = { version = "0.2", features = ["full"], package = "tokio" }
|
||||
tokio-util-old = { version = "0.2", features = ["codec"], package = "tokio-util" }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
nokhwa = { version = "0.10.10", default-features = false, features = ["input-v4l"] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
nokhwa = { version = "0.10.10", default-features = false, features = ["input-msmf"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
nokhwa = { version = "0.10.10", default-features = false, features = ["input-avfoundation", "output-threaded"] }
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
env_logger = "0.11.3"
|
||||
winit = { version = "0.30.12" }
|
||||
wgpu = { version = "27.0.1" }
|
||||
eframe = { version = "0.33.2", features = ["wgpu"] }
|
||||
arboard = "3.2.0"
|
||||
rfd = "0.17.2"
|
||||
interprocess = { version = "2.2.1", features = ["tokio"] }
|
||||
|
||||
## native-tls (via hyper-tls) uses OpenSSL only on Linux/Android. Upstream Grim
|
||||
## got a vendored, statically-linked OpenSSL for free through arti's `static`
|
||||
## feature; dropping arti for Nym took that with it, breaking Android/cross
|
||||
## builds (no system OpenSSL for the target) and leaving desktop dynamically
|
||||
## linked to libssl. Restore the vendored build for exactly those two targets so
|
||||
## each is self-contained. Windows (SChannel) and macOS (Security.framework)
|
||||
## don't use OpenSSL at all, so they must NOT pull it — building openssl-src
|
||||
## there is both pointless and fragile (the Windows MSVC runner's bash perl is
|
||||
## missing modules its Configure needs).
|
||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.15.0"
|
||||
jni = "0.21.1"
|
||||
android-activity = { version = "0.6.0", features = ["game-activity"] }
|
||||
winit = { version = "0.30.12", features = ["android-game-activity"] }
|
||||
eframe = { version = "0.33.2", default-features = false, features = ["glow", "android-game-activity"] }
|
||||
|
||||
[build-dependencies]
|
||||
built = "0.8.0"
|
||||
|
||||
# Windows hosts only: embed the Goblin icon (wix/Product.ico) into goblin.exe via
|
||||
# build.rs. Not compiled on Linux/macOS/Android hosts, so other builds are
|
||||
# unaffected.
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
winresource = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
nostr-sdk = { version = "0.44", features = ["nip06", "nip44", "nip49", "nip59", "nip98"] }
|
||||
tokio = { version = "1.49.0", features = ["full"] }
|
||||
base64 = "0.22"
|
||||
sha2 = "0.10"
|
||||
hex = "0.4"
|
||||
serde_yaml = "0.9"
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,77 @@
|
||||
<p align="center">
|
||||
<img src="Goblin-Banner.png" alt="Goblin" width="680"/>
|
||||
</p>
|
||||
|
||||
# Goblin
|
||||
|
||||
Goblin is a private, pay-by-username wallet for [GRIN ツ](https://grin.mw) — confidential digital cash on [Mimblewimble](https://github.com/mimblewimble/grin), with no amounts or addresses on the chain.
|
||||
|
||||
Instead of passing slatepack files back and forth, you **pay a `username` (or an `npub`)** and the payment is delivered for you as an **end-to-end encrypted message over [nostr](https://github.com/nostr-protocol/nips), routed through the [Nym mixnet](https://nym.com)**. Relays only ever see ciphertext — never the amount, the sender, or the recipient — and the mixnet hides who is talking to whom at the network layer.
|
||||
|
||||
Goblin is a fork of the **Grim** egui GRIN wallet: it keeps Grim's full GRIN node/wallet engine and layers a Nostr-native, mobile-first payments experience on top.
|
||||
|
||||
## What it does
|
||||
|
||||
- **Send to people** — pay a `username` or `npub`; the GRIN slatepack travels as a [NIP-17](https://nips.nostr.com/17) gift-wrapped DM ([kind 1059](https://nostrbook.dev/kinds/1059)) over the Nym mixnet and is applied automatically by the recipient's wallet. No files to swap, no need to both be online at once.
|
||||
- **Manual slatepacks too** — when you need to pay or get paid without a handle, **Settings → Wallet → Slatepacks** exposes the classic by-hand flow: create a slatepack to send, or paste one to receive, finalize, or pay.
|
||||
- **In-app identity** — a nostr payment key that is deliberately *not* part of your seed, so you can rotate it any time to stay unlinkable without touching your funds. An optional human-readable `name` comes from the goblin.st identity service.
|
||||
- **Private by construction** — GRIN's address-less, confidential chain; your payments and identity (nostr relays, NIP-05 lookups, price) are routed through the [Nym mixnet](https://nym.com), so who-pays-whom never touches the clear net. The GRIN node connection — block sync and broadcasting your transaction — is direct: public chain data, the same for everyone, and not tied to your identity. Keys, names and history stay on your device.
|
||||
- **Configurable amount pairing** — show balances against a world currency, Bitcoin, or sats (rates fetched over the mixnet), or turn the preview off.
|
||||
- **Cross-platform** — Linux, macOS, Windows, Android, built in pure Rust on [egui](https://github.com/emilk/egui).
|
||||
|
||||
## How a payment travels
|
||||
|
||||
```
|
||||
you ──slatepack──▶ NIP-17 gift wrap (kind 1059, NIP-44 encrypted)
|
||||
│
|
||||
Nym mixnet (5-hop)
|
||||
│
|
||||
┌─────────────┴─────────────┐
|
||||
your relays recipient's DM relays (kind 10050)
|
||||
└─────────────┬─────────────┘
|
||||
▼
|
||||
recipient ◀──unwrap, verify seal author, apply slatepack
|
||||
```
|
||||
|
||||
The wrap is [NIP-44](https://nips.nostr.com/44)-encrypted, and delivery uses the recipient's DM relay list ([kind 10050](https://nostrbook.dev/kinds/10050)).
|
||||
|
||||
Both parties only need one relay in common. The default set is the Goblin relay plus large public relays (`relay.damus.io`, `nos.lol`), and the set is editable in **Settings → Relays**.
|
||||
|
||||
## Build
|
||||
|
||||
### Desktop (Linux / macOS / Windows)
|
||||
|
||||
Goblin links the [Nym mixnet](https://nym.com) SDK **in-process** — the wallet is a single self-contained binary, no sidecar. The SDK builds from a sibling `../nym` checkout (a pinned nym tree with a small Android TLS patch):
|
||||
|
||||
```
|
||||
git clone --branch goblin https://git.us-ea.st/GRIN/nym ../nym
|
||||
git submodule update --init --recursive
|
||||
cargo build --release
|
||||
./target/release/goblin
|
||||
```
|
||||
|
||||
Goblin's identity and payment traffic — nostr relays, NIP-05 lookups and price fetches — is routed over the mixnet through a network requester (the default is baked into `NETWORK_REQUESTER` in `src/nym/sidecar.rs`); the SDK's SOCKS5 listener is run in-process on `127.0.0.1:1080`. If something is already listening there, Goblin reuses it. The GRIN node connection (block sync and transaction broadcast) is **not** mixed — it connects directly, as it carries only public chain data that isn't linked to your wallet.
|
||||
|
||||
### Android
|
||||
|
||||
Install the Android SDK / NDK, then from the repo root:
|
||||
|
||||
```
|
||||
./scripts/android.sh build|release v7|v8|x86
|
||||
```
|
||||
|
||||
`v7`/`v8`/`x86` is the device CPU architecture for `build`; for `release` pass a version in `major.minor.patch` form.
|
||||
|
||||
## Identity service (`goblin-nip05d`)
|
||||
|
||||
The optional `name` service lives in `goblin-nip05d/` (axum + SQLite) and is deployed at [goblin.st](https://goblin.st). It implements [NIP-05](https://nips.nostr.com/5) resolution, [NIP-98](https://nips.nostr.com/98)-authenticated registration and release (names are never transferred — on a key rotation you release the old name and re-register, or import your existing identity). The wallet is fully usable — and fully anonymous — without it. Avatars aren't stored or served — clients render them from the pubkey (an npub gradient with the username's first letter, else the Grin mark).
|
||||
|
||||
## License
|
||||
|
||||
Apache License v2.0.
|
||||
|
||||
## Credits
|
||||
|
||||
🤖 Built with AI pair-programming assistance (Claude)
|
||||
|
||||
The underlying cross-platform GRIN wallet engine is the upstream **Grim** project.
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,114 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 36
|
||||
ndkVersion '29.0.14206865'
|
||||
buildToolsVersion = '36.1.0'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "st.goblin.wallet"
|
||||
minSdk 24
|
||||
targetSdk 36
|
||||
versionCode 5
|
||||
versionName "0.3.6"
|
||||
}
|
||||
|
||||
lint {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
|
||||
def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
def keystoreProperties = new Properties()
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile file(keystoreProperties['storeFile'])
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
signedRelease {
|
||||
initWith release
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
debuggable true
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
namespace 'mw.gri.android'
|
||||
|
||||
flavorDimensions "mode"
|
||||
|
||||
productFlavors {
|
||||
ci {
|
||||
dimension "mode"
|
||||
}
|
||||
local {
|
||||
dimension "mode"
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
def flavor = variant.productFlavors[0].name
|
||||
|
||||
// The ci branch reads the private-mirror properties at configuration time,
|
||||
// which runs for every variant — only enter it when the mirror is configured.
|
||||
if (flavor == "ci" && project.hasProperty("mavenHost")) {
|
||||
repositories {
|
||||
maven {
|
||||
credentials {
|
||||
username "$mavenUser"
|
||||
password "$mavenPassword"
|
||||
}
|
||||
url "$mavenHost/repository/maven-central/"
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
maven {
|
||||
credentials {
|
||||
username "$mavenUser"
|
||||
password "$mavenPassword"
|
||||
}
|
||||
url "$mavenHost/repository/google-android-maven/"
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
}
|
||||
} else if (flavor == "local") {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
|
||||
// To use the Games Activity library
|
||||
implementation "androidx.games:games-activity:2.0.2"
|
||||
|
||||
// Android Camera
|
||||
implementation 'androidx.camera:camera-core:1.5.1'
|
||||
implementation 'androidx.camera:camera-camera2:1.5.1'
|
||||
implementation 'androidx.camera:camera-lifecycle:1.5.1'
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
|
||||
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
|
||||
|
||||
<application
|
||||
android:hardwareAccelerated="true"
|
||||
android:largeHeap="true"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="Goblin"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/Theme.Main"
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:extractNativeLibs="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<receiver android:name=".NotificationActionsReceiver"/>
|
||||
|
||||
<provider
|
||||
android:name=".FileProvider"
|
||||
android:authorities="st.goblin.wallet.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/paths" />
|
||||
</provider>
|
||||
|
||||
<activity
|
||||
android:launchMode="singleTask"
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode|density|locale|layoutDirection|fontScale|colorMode"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:scheme="http" tools:ignore="AppLinkUrlError">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/*" />
|
||||
<data android:mimeType="application/*" />
|
||||
<data android:pathPattern=".*\\.slatepack" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="grim" />
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".BackgroundService"
|
||||
android:stopWithTask="true"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,239 @@
|
||||
package mw.gri.android;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.*;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.*;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static android.app.Notification.EXTRA_NOTIFICATION_ID;
|
||||
|
||||
public class BackgroundService extends Service {
|
||||
private static final String TAG = BackgroundService.class.getSimpleName();
|
||||
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private boolean mStopped = false;
|
||||
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
private NotificationCompat.Builder mNotificationBuilder;
|
||||
|
||||
private String mNotificationContentText = "";
|
||||
private Boolean mCanStart = null;
|
||||
private Boolean mCanStop = null;
|
||||
|
||||
public static final String ACTION_START_NODE = "start_node";
|
||||
public static final String ACTION_STOP_NODE = "stop_node";
|
||||
|
||||
private final Runnable mUpdateSyncStatus = new Runnable() {
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void run() {
|
||||
if (mStopped) {
|
||||
return;
|
||||
}
|
||||
// Update sync status at notification.
|
||||
String syncStatusText = getSyncStatusText();
|
||||
boolean textChanged = !mNotificationContentText.equals(syncStatusText);
|
||||
if (textChanged) {
|
||||
mNotificationContentText = syncStatusText;
|
||||
mNotificationBuilder.setContentText(mNotificationContentText);
|
||||
mNotificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(mNotificationContentText));
|
||||
}
|
||||
|
||||
// Send broadcast to MainActivity if exit from the app is needed after node stop.
|
||||
if (exitAppAfterNodeStop()) {
|
||||
sendBroadcast(new Intent(MainActivity.STOP_APP_ACTION));
|
||||
mStopped = true;
|
||||
}
|
||||
|
||||
if (!mStopped) {
|
||||
boolean canStart = canStartNode();
|
||||
boolean canStop = canStopNode();
|
||||
|
||||
boolean buttonsChanged = mCanStart == null || mCanStop == null ||
|
||||
mCanStart != canStart || mCanStop != canStop;
|
||||
mCanStart = canStart;
|
||||
mCanStop = canStop;
|
||||
if (buttonsChanged) {
|
||||
mNotificationBuilder.mActions.clear();
|
||||
|
||||
// Set up buttons to start/stop node.
|
||||
Intent startStopIntent = new Intent(BackgroundService.this, NotificationActionsReceiver.class);
|
||||
if (Build.VERSION.SDK_INT > 25) {
|
||||
startStopIntent.putExtra(EXTRA_NOTIFICATION_ID, NOTIFICATION_ID);
|
||||
}
|
||||
if (canStart) {
|
||||
startStopIntent.setAction(ACTION_START_NODE);
|
||||
PendingIntent i = PendingIntent
|
||||
.getBroadcast(BackgroundService.this, 1, startStopIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
|
||||
mNotificationBuilder.addAction(R.drawable.ic_start, getStartText(), i);
|
||||
} else if (canStop) {
|
||||
startStopIntent.setAction(ACTION_STOP_NODE);
|
||||
PendingIntent i = PendingIntent
|
||||
.getBroadcast(BackgroundService.this, 1, startStopIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
|
||||
mNotificationBuilder.addAction(R.drawable.ic_stop, getStopText(), i);
|
||||
}
|
||||
}
|
||||
|
||||
// Update notification.
|
||||
if (textChanged || buttonsChanged) {
|
||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||
manager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
|
||||
}
|
||||
|
||||
// Repeat notification update.
|
||||
mHandler.postDelayed(this, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressLint({"WakelockTimeout", "UnspecifiedRegisterReceiverFlag"})
|
||||
@Override
|
||||
public void onCreate() {
|
||||
if (mStopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent CPU to sleep at background.
|
||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||
mWakeLock.acquire();
|
||||
|
||||
// Create channel to show notifications.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel notificationChannel = new NotificationChannel(
|
||||
TAG, TAG, NotificationManager.IMPORTANCE_LOW
|
||||
);
|
||||
|
||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||
manager.createNotificationChannel(notificationChannel);
|
||||
}
|
||||
|
||||
// Show notification with sync status.
|
||||
Intent i = getPackageManager().getLaunchIntentForPackage(this.getPackageName());
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_IMMUTABLE);
|
||||
try {
|
||||
mNotificationBuilder = new NotificationCompat.Builder(this, TAG)
|
||||
.setContentTitle(this.getSyncTitle())
|
||||
.setContentText(this.getSyncStatusText())
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(this.getSyncStatusText()))
|
||||
.setSmallIcon(R.drawable.ic_stat_name)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setContentIntent(pendingIntent);
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
return;
|
||||
}
|
||||
Notification notification = mNotificationBuilder.build();
|
||||
|
||||
// Start service at foreground state to prevent killing by system.
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
|
||||
// Update sync status at notification.
|
||||
mHandler.post(mUpdateSyncStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskRemoved(Intent rootIntent) {
|
||||
onStop();
|
||||
super.onTaskRemoved(rootIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
onStop();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
mStopped = true;
|
||||
|
||||
// Stop updating the notification.
|
||||
mHandler.removeCallbacks(mUpdateSyncStatus);
|
||||
clearNotification();
|
||||
|
||||
// Remove service from foreground state.
|
||||
stopForeground(Service.STOP_FOREGROUND_REMOVE);
|
||||
|
||||
// Release wake lock to allow CPU to sleep at background.
|
||||
if (mWakeLock != null && mWakeLock.isHeld()) {
|
||||
mWakeLock.release();
|
||||
mWakeLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove notification.
|
||||
private void clearNotification() {
|
||||
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
notificationManager.deleteNotificationChannel(TAG);
|
||||
}
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
// Start the service.
|
||||
public static void start(Context c) {
|
||||
if (!isServiceRunning(c)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
ContextCompat.startForegroundService(c, new Intent(c, BackgroundService.class));
|
||||
} else {
|
||||
c.startService(new Intent(c, BackgroundService.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the service.
|
||||
public static void stop(Context context) {
|
||||
context.stopService(new Intent(context, BackgroundService.class));
|
||||
}
|
||||
|
||||
// Check if service is running.
|
||||
private static boolean isServiceRunning(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
|
||||
|
||||
for (ActivityManager.RunningServiceInfo runningServiceInfo : services) {
|
||||
if (runningServiceInfo.service.getClassName().equals(BackgroundService.class.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get sync status text for notification.
|
||||
private native String getSyncStatusText();
|
||||
// Get sync title text for notification.
|
||||
private native String getSyncTitle();
|
||||
|
||||
// Get start text for notification.
|
||||
private native String getStartText();
|
||||
// Get stop text for notification.
|
||||
private native String getStopText();
|
||||
|
||||
// Check if start node is possible.
|
||||
private native boolean canStartNode();
|
||||
// Check if stop node is possible.
|
||||
private native boolean canStopNode();
|
||||
|
||||
// Check if app from the app is needed after node stop.
|
||||
private native boolean exitAppAfterNodeStop();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package mw.gri.android;
|
||||
|
||||
public class FileProvider extends androidx.core.content.FileProvider {
|
||||
public FileProvider() {
|
||||
super(R.xml.paths);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,632 @@
|
||||
package mw.gri.android;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.*;
|
||||
import android.os.Process;
|
||||
import android.provider.Settings;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.camera.core.*;
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.DisplayCutoutCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import com.google.androidgamesdk.GameActivity;
|
||||
import com.google.androidgamesdk.gametextinput.State;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static android.content.ClipDescription.MIMETYPE_TEXT_HTML;
|
||||
import static android.content.ClipDescription.MIMETYPE_TEXT_PLAIN;
|
||||
|
||||
public class MainActivity extends GameActivity {
|
||||
public static String STOP_APP_ACTION = "STOP_APP";
|
||||
|
||||
private static final int NOTIFICATIONS_PERMISSION_CODE = 1;
|
||||
private static final int CAMERA_PERMISSION_CODE = 2;
|
||||
|
||||
static {
|
||||
System.loadLibrary("grim");
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context ctx, Intent i) {
|
||||
if (Objects.equals(i.getAction(), STOP_APP_ACTION)) {
|
||||
exit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final ImageAnalysis mImageAnalysis = new ImageAnalysis.Builder()
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
.build();
|
||||
|
||||
private ListenableFuture<ProcessCameraProvider> mCameraProviderFuture = null;
|
||||
private ProcessCameraProvider mCameraProvider = null;
|
||||
private ExecutorService mCameraExecutor = null;
|
||||
private boolean mUseBackCamera = true;
|
||||
|
||||
private ActivityResultLauncher<Intent> mFilePickResult = null;
|
||||
private ActivityResultLauncher<Intent> mOpenFilePermissionsResult = null;
|
||||
private ActivityResultLauncher<Intent> mFileSaveResult = null;
|
||||
// Source path (in the share cache) staged by Rust for the next saveFile().
|
||||
private String mPendingSavePath = null;
|
||||
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// Check if activity was launched to exclude from recent apps on exit.
|
||||
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
|
||||
super.onCreate(null);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear cache on start.
|
||||
if (savedInstanceState == null && getExternalCacheDir() != null) {
|
||||
Utils.deleteDirectoryContent(new File(getExternalCacheDir().getPath()), false);
|
||||
}
|
||||
|
||||
// Setup environment variables for native code.
|
||||
try {
|
||||
Os.setenv("HOME", Objects.requireNonNull(getExternalFilesDir("")).getPath(), true);
|
||||
Os.setenv("XDG_CACHE_HOME", Objects.requireNonNull(getExternalCacheDir()).getPath(), true);
|
||||
Os.setenv("ARTI_FS_DISABLE_PERMISSION_CHECKS", "true", true);
|
||||
Os.setenv("NATIVE_LIBS_DIR", getApplicationInfo().nativeLibraryDir, true);
|
||||
} catch (ErrnoException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
super.onCreate(null);
|
||||
|
||||
// Register receiver to finish activity from the BackgroundService.
|
||||
ContextCompat.registerReceiver(this, mBroadcastReceiver, new IntentFilter(STOP_APP_ACTION), ContextCompat.RECEIVER_NOT_EXPORTED);
|
||||
|
||||
// Register associated file opening result.
|
||||
mOpenFilePermissionsResult = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
if (Environment.isExternalStorageManager()) {
|
||||
onFile();
|
||||
}
|
||||
} else if (result.getResultCode() == RESULT_OK) {
|
||||
onFile();
|
||||
}
|
||||
}
|
||||
);
|
||||
// Register file pick result.
|
||||
mFilePickResult = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
int resultCode = result.getResultCode();
|
||||
Intent data = result.getData();
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
String path = "";
|
||||
if (data != null && data.getData() != null) {
|
||||
Uri uri = data.getData();
|
||||
String name = "pick" + Utils.getFileExtension(uri, this);
|
||||
File file = new File(getExternalCacheDir(), name);
|
||||
try (InputStream is = getContentResolver().openInputStream(uri);
|
||||
OutputStream os = new FileOutputStream(file)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while (true) {
|
||||
assert is != null;
|
||||
if (!((length = is.read(buffer)) > 0)) break;
|
||||
os.write(buffer, 0, length);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("grim", e.toString());
|
||||
}
|
||||
path = file.getPath();
|
||||
}
|
||||
onFilePick(path);
|
||||
} else {
|
||||
onFilePick("");
|
||||
}
|
||||
});
|
||||
|
||||
// Register file SAVE result (Storage Access Framework CREATE_DOCUMENT):
|
||||
// copy the staged source file into the user-chosen document.
|
||||
mFileSaveResult = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
String src = mPendingSavePath;
|
||||
mPendingSavePath = null;
|
||||
if (result.getResultCode() == Activity.RESULT_OK && src != null) {
|
||||
Intent data = result.getData();
|
||||
if (data != null && data.getData() != null) {
|
||||
Uri uri = data.getData();
|
||||
try (InputStream is = new FileInputStream(new File(src));
|
||||
OutputStream os = getContentResolver().openOutputStream(uri)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int length;
|
||||
while (is != null && (length = is.read(buffer)) > 0) {
|
||||
os.write(buffer, 0, length);
|
||||
}
|
||||
if (os != null) os.flush();
|
||||
} catch (Exception e) {
|
||||
Log.e("grim", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Listener for display insets (cutouts) to pass values into native code.
|
||||
View content = getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(content, (v, insets) -> {
|
||||
// Get display cutouts.
|
||||
DisplayCutoutCompat dc = insets.getDisplayCutout();
|
||||
int cutoutTop = 0;
|
||||
int cutoutRight = 0;
|
||||
int cutoutBottom = 0;
|
||||
int cutoutLeft = 0;
|
||||
if (dc != null) {
|
||||
cutoutTop = dc.getSafeInsetTop();
|
||||
cutoutRight = dc.getSafeInsetRight();
|
||||
cutoutBottom = dc.getSafeInsetBottom();
|
||||
cutoutLeft = dc.getSafeInsetLeft();
|
||||
}
|
||||
|
||||
// Get display insets.
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
|
||||
// Pass values into native code.
|
||||
int[] values = new int[]{0, 0, 0, 0};
|
||||
values[0] = Utils.pxToDp(Integer.max(cutoutTop, systemBars.top), this);
|
||||
values[1] = Utils.pxToDp(Integer.max(cutoutRight, systemBars.right), this);
|
||||
values[2] = Utils.pxToDp(Integer.max(cutoutBottom, systemBars.bottom), this);
|
||||
values[3] = Utils.pxToDp(Integer.max(cutoutLeft, systemBars.left), this);
|
||||
onDisplayInsets(values);
|
||||
|
||||
return insets;
|
||||
});
|
||||
|
||||
findViewById(android.R.id.content).post(() -> {
|
||||
// Request notifications permissions if needed.
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
String notificationsPermission = Manifest.permission.POST_NOTIFICATIONS;
|
||||
if (checkSelfPermission(notificationsPermission) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(new String[] { notificationsPermission }, NOTIFICATIONS_PERMISSION_CODE);
|
||||
} else {
|
||||
// Start notification service.
|
||||
BackgroundService.start(this);
|
||||
}
|
||||
} else {
|
||||
// Start notification service.
|
||||
BackgroundService.start(this);
|
||||
}
|
||||
});
|
||||
|
||||
// Check if intent has data on launch.
|
||||
if (savedInstanceState == null) {
|
||||
onNewIntent(getIntent());
|
||||
}
|
||||
}
|
||||
|
||||
// Pass display insets into native code.
|
||||
public native void onDisplayInsets(int[] cutouts);
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
String action = intent.getAction();
|
||||
// Check if file was open with the application.
|
||||
if (action != null && action.equals(Intent.ACTION_VIEW)) {
|
||||
Intent i = getIntent();
|
||||
i.setData(intent.getData());
|
||||
setIntent(i);
|
||||
onFile();
|
||||
}
|
||||
}
|
||||
|
||||
// Callback when associated file was open.
|
||||
private void onFile() {
|
||||
Uri data = getIntent().getData();
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
Intent i = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||
mOpenFilePermissionsResult.launch(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ParcelFileDescriptor parcelFile = getContentResolver().openFileDescriptor(data, "r");
|
||||
assert parcelFile != null;
|
||||
FileReader fileReader = new FileReader(parcelFile.getFileDescriptor());
|
||||
BufferedReader reader = new BufferedReader(fileReader);
|
||||
String line;
|
||||
StringBuilder buff = new StringBuilder();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
buff.append(line);
|
||||
}
|
||||
reader.close();
|
||||
fileReader.close();
|
||||
|
||||
// Provide file content into native code.
|
||||
onData(buff.toString());
|
||||
} catch (Exception e) {
|
||||
Log.e("grim", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Pass data into native code.
|
||||
public native void onData(String data);
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
// Called on screen orientation change to restart camera.
|
||||
if (mCameraProvider != null) {
|
||||
stopCamera();
|
||||
startCamera();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] results) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, results);
|
||||
if (results.length != 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
switch (requestCode) {
|
||||
case NOTIFICATIONS_PERMISSION_CODE: {
|
||||
BackgroundService.start(this);
|
||||
return;
|
||||
}
|
||||
case CAMERA_PERMISSION_CODE: {
|
||||
startCamera();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTextInputEventNative(long l, State state) {
|
||||
super.onTextInputEventNative(l, state);
|
||||
if (state.selectionEnd > state.composingRegionStart && state.composingRegionStart >= 0) {
|
||||
String input = String.valueOf(state.text.charAt(state.composingRegionStart));
|
||||
if (input.contains("\n")) {
|
||||
onEnterInput();
|
||||
} else {
|
||||
onTextInput(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||
onBack();
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
|
||||
onClearInput();
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
|
||||
onEnterInput();
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_0) {
|
||||
onTextInput("0");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_1) {
|
||||
onTextInput("1");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_2) {
|
||||
onTextInput("2");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_3) {
|
||||
onTextInput("3");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_4) {
|
||||
onTextInput("4");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_5) {
|
||||
onTextInput("5");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_6) {
|
||||
onTextInput("6");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_7) {
|
||||
onTextInput("7");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_8) {
|
||||
onTextInput("8");
|
||||
return false;
|
||||
} else if (event.getKeyCode() == KeyEvent.KEYCODE_9) {
|
||||
onTextInput("9");
|
||||
return false;
|
||||
}
|
||||
} else if (event.getAction() == KeyEvent.ACTION_MULTIPLE && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
if (!event.getCharacters().isEmpty()) {
|
||||
onTextInput(event.getCharacters());
|
||||
return false;
|
||||
}
|
||||
// Pass any other input values into native code.
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP &&
|
||||
event.getKeyCode() != KeyEvent.KEYCODE_ENTER &&
|
||||
event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
|
||||
onTextInput(String.valueOf((char)event.getUnicodeChar()));
|
||||
return false;
|
||||
}
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
// Pass back navigation event into native code.
|
||||
public native void onBack();
|
||||
|
||||
// Pass clear key event into native code.
|
||||
public native void onClearInput();
|
||||
|
||||
// Pass enter key event into native code.
|
||||
public native void onEnterInput();
|
||||
|
||||
// Pass last entered character from soft keyboard into native code.
|
||||
public native void onTextInput(String character);
|
||||
|
||||
// Called from native code to exit app.
|
||||
public void exit() {
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
unregisterReceiver(mBroadcastReceiver);
|
||||
|
||||
// Only tear the process down when the activity is actually finishing.
|
||||
// onDestroy also fires for configuration-change recreations (rotation,
|
||||
// density, uiMode); killing the process there takes the whole app down
|
||||
// right as Android is about to recreate the activity.
|
||||
if (isFinishing()) {
|
||||
BackgroundService.stop(this);
|
||||
|
||||
// Kill process after 3 secs if app was terminated from recent apps to prevent app hang.
|
||||
new Thread(() -> {
|
||||
try {
|
||||
onTermination();
|
||||
Thread.sleep(3000);
|
||||
Process.killProcess(Process.myPid());
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
// Notify native code to stop activity (e.g. node) if app was terminated from recent apps.
|
||||
public native void onTermination();
|
||||
|
||||
// Called from native code to set text into clipboard.
|
||||
public void copyText(String data) {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText(data, data);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
}
|
||||
|
||||
// Called from native code to get text from clipboard.
|
||||
public String pasteText() {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipDescription desc = clipboard.getPrimaryClipDescription();
|
||||
ClipData data = clipboard.getPrimaryClip();
|
||||
String text = "";
|
||||
if (!(clipboard.hasPrimaryClip())) {
|
||||
text = "";
|
||||
} else if (desc != null && (!(desc.hasMimeType(MIMETYPE_TEXT_PLAIN))
|
||||
&& !(desc.hasMimeType(MIMETYPE_TEXT_HTML)))) {
|
||||
text = "";
|
||||
} else if (data != null) {
|
||||
ClipData.Item item = data.getItemAt(0);
|
||||
text = item.getText().toString();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// Called from native code to start camera.
|
||||
public void startCamera() {
|
||||
String notificationsPermission = Manifest.permission.CAMERA;
|
||||
if (checkSelfPermission(notificationsPermission) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissions(new String[] { notificationsPermission }, CAMERA_PERMISSION_CODE);
|
||||
} else {
|
||||
if (mCameraProviderFuture == null) {
|
||||
mCameraProviderFuture = ProcessCameraProvider.getInstance(this);
|
||||
mCameraProviderFuture.addListener(() -> {
|
||||
try {
|
||||
mCameraProvider = mCameraProviderFuture.get();
|
||||
// Start camera.
|
||||
openCamera();
|
||||
} catch (Exception e) {
|
||||
View content = findViewById(android.R.id.content);
|
||||
if (content != null) {
|
||||
content.post(this::stopCamera);
|
||||
}
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(this));
|
||||
} else {
|
||||
View content = findViewById(android.R.id.content);
|
||||
if (content != null) {
|
||||
content.post(this::openCamera);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open camera after initialization or start after stop.
|
||||
private void openCamera() {
|
||||
// Set up the image analysis use case which will process frames in real time.
|
||||
if (mCameraExecutor == null) {
|
||||
mCameraExecutor = Executors.newSingleThreadExecutor();
|
||||
mImageAnalysis.setAnalyzer(mCameraExecutor, image -> {
|
||||
// Convert image to JPEG.
|
||||
byte[] data = Utils.convertCameraImage(image);
|
||||
// Send image to native code.
|
||||
onCameraImage(data, image.getImageInfo().getRotationDegrees());
|
||||
image.close();
|
||||
});
|
||||
}
|
||||
|
||||
// Select back camera initially.
|
||||
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
|
||||
if (!mUseBackCamera) {
|
||||
cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;
|
||||
}
|
||||
// Apply declared configs to CameraX using the same lifecycle owner
|
||||
mCameraProvider.unbindAll();
|
||||
mCameraProvider.bindToLifecycle(this, cameraSelector, mImageAnalysis);
|
||||
}
|
||||
|
||||
// Called from native code to stop camera.
|
||||
public void stopCamera() {
|
||||
View content = findViewById(android.R.id.content);
|
||||
if (content != null) {
|
||||
content.post(() -> {
|
||||
if (mCameraProvider != null) {
|
||||
mCameraProvider.unbindAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Called from native code to get number of cameras.
|
||||
public int camerasAmount() {
|
||||
if (mCameraProvider == null) {
|
||||
return 0;
|
||||
}
|
||||
return mCameraProvider.getAvailableCameraInfos().size();
|
||||
}
|
||||
|
||||
// Called from native code to switch camera.
|
||||
public void switchCamera() {
|
||||
mUseBackCamera = !mUseBackCamera;
|
||||
stopCamera();
|
||||
startCamera();
|
||||
}
|
||||
|
||||
// Pass image from camera into native code.
|
||||
public native void onCameraImage(byte[] buff, int rotation);
|
||||
|
||||
// Called from native code to share data from provided path.
|
||||
public void shareData(String path) {
|
||||
File file = new File(path);
|
||||
Uri uri = FileProvider.getUriForFile(this, "st.goblin.wallet.fileprovider", file);
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
intent.setType("text/*");
|
||||
startActivity(Intent.createChooser(intent, "Share data"));
|
||||
}
|
||||
|
||||
// Called from native code to SAVE a staged file to a user-chosen location.
|
||||
// Launches the Storage Access Framework create-document picker; the result
|
||||
// handler copies the staged source file into the chosen document.
|
||||
public void saveFile(String path, String name) {
|
||||
mPendingSavePath = path;
|
||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("application/octet-stream");
|
||||
intent.putExtra(Intent.EXTRA_TITLE, name);
|
||||
try {
|
||||
mFileSaveResult.launch(intent);
|
||||
} catch (android.content.ActivityNotFoundException ex) {
|
||||
Log.e("grim", ex.toString());
|
||||
mPendingSavePath = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Called from native code to share plain text (e.g. a payment link) via the
|
||||
// system share sheet.
|
||||
public void shareText(String text) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, text);
|
||||
intent.setType("text/plain");
|
||||
startActivity(Intent.createChooser(intent, "Share"));
|
||||
}
|
||||
|
||||
// Called from native code to play a short "error" haptic (rejected payment).
|
||||
public void vibrateError() {
|
||||
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (vibrator == null || !vibrator.hasVibrator()) {
|
||||
return;
|
||||
}
|
||||
// Two short pulses read as "no" / rejected, distinct from a tap.
|
||||
long[] pattern = new long[]{0, 40, 70, 40};
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
|
||||
} else {
|
||||
vibrator.vibrate(pattern, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// Called from native code to play a tiny "tick" haptic on a successful copy.
|
||||
public void vibrateCopy() {
|
||||
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (vibrator == null || !vibrator.hasVibrator()) {
|
||||
return;
|
||||
}
|
||||
// One short, light tick — a confirmation, not an alert.
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
vibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
|
||||
} else if (Build.VERSION.SDK_INT >= 26) {
|
||||
vibrator.vibrate(VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
} else {
|
||||
vibrator.vibrate(20);
|
||||
}
|
||||
}
|
||||
|
||||
// Called from native code to set status-bar icon color to contrast the
|
||||
// in-app theme. white = light icons for a dark background. The app draws
|
||||
// edge-to-edge, so the OS status-bar background is the app's own content;
|
||||
// the icons must follow the theme or they vanish (dark-on-dark).
|
||||
public void setStatusBarWhiteIcons(boolean white) {
|
||||
runOnUiThread(() -> {
|
||||
androidx.core.view.WindowInsetsControllerCompat c =
|
||||
androidx.core.view.WindowCompat.getInsetsController(getWindow(),
|
||||
getWindow().getDecorView());
|
||||
// isAppearanceLightStatusBars == true means DARK icons.
|
||||
c.setAppearanceLightStatusBars(!white);
|
||||
});
|
||||
}
|
||||
|
||||
// Called from native code to check if device is using dark theme.
|
||||
public boolean useDarkTheme() {
|
||||
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
|
||||
}
|
||||
|
||||
// Called from native code to pick the file.
|
||||
public void pickFile() {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("text/*");
|
||||
try {
|
||||
mFilePickResult.launch(Intent.createChooser(intent, "Pick file"));
|
||||
} catch (android.content.ActivityNotFoundException ex) {
|
||||
onFilePick("");
|
||||
}
|
||||
}
|
||||
|
||||
// Pass picked file into native code.
|
||||
public native void onFilePick(String path);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package mw.gri.android;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class NotificationActionsReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent i) {
|
||||
String a = i.getAction();
|
||||
if (Objects.equals(a, BackgroundService.ACTION_START_NODE)) {
|
||||
startNode();
|
||||
} else if (Objects.equals(a, BackgroundService.ACTION_STOP_NODE)) {
|
||||
stopNode();
|
||||
} else {
|
||||
stopNodeToExit();
|
||||
}
|
||||
}
|
||||
|
||||
// Start integrated node.
|
||||
native void startNode();
|
||||
// Stop integrated node.
|
||||
native void stopNode();
|
||||
// Stop node and exit from the app.
|
||||
native void stopNodeToExit();
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package mw.gri.android;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.YuvImage;
|
||||
import android.media.Image;
|
||||
import android.net.Uri;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import androidx.camera.core.ImageProxy;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class Utils {
|
||||
// Convert Pixels to DensityPixels
|
||||
public static int pxToDp(int px, Context context) {
|
||||
return (int) (px / context.getResources().getDisplayMetrics().density);
|
||||
}
|
||||
|
||||
/** Converts a YUV_420_888 image from CameraX API to a bitmap. */
|
||||
public static byte[] convertCameraImage(ImageProxy image) {
|
||||
// Convert image to nv21 and get buffer.
|
||||
ByteBuffer nv21Buffer =
|
||||
yuv420ThreePlanesToNV21(image.getPlanes(), image.getWidth(), image.getHeight());
|
||||
nv21Buffer.rewind();
|
||||
byte[] nv21 = new byte[nv21Buffer.limit()];
|
||||
nv21Buffer.get(nv21);
|
||||
// Convert to JPEG.
|
||||
YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21, image.getWidth(), image.getHeight(), null);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
yuvImage.compressToJpeg(new Rect(0, 0, yuvImage.getWidth(), yuvImage.getHeight()), 100, out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts YUV_420_888 to NV21 bytebuffer.
|
||||
*
|
||||
* <p>The NV21 format consists of a single byte array containing the Y, U and V values. For an
|
||||
* image of size S, the first S positions of the array contain all the Y values. The remaining
|
||||
* positions contain interleaved V and U values. U and V are subsampled by a factor of 2 in both
|
||||
* dimensions, so there are S/4 U values and S/4 V values. In summary, the NV21 array will contain
|
||||
* S Y values followed by S/4 VU values: YYYYYYYYYYYYYY(...)YVUVUVUVU(...)VU
|
||||
*
|
||||
* <p>YUV_420_888 is a generic format that can describe any YUV image where U and V are subsampled
|
||||
* by a factor of 2 in both dimensions. {@link Image#getPlanes} returns an array with the Y, U and
|
||||
* V planes. The Y plane is guaranteed not to be interleaved, so we can just copy its values into
|
||||
* the first part of the NV21 array. The U and V planes may already have the representation in the
|
||||
* NV21 format. This happens if the planes share the same buffer, the V buffer is one position
|
||||
* before the U buffer and the planes have a pixelStride of 2. If this is case, we can just copy
|
||||
* them to the NV21 array.
|
||||
*/
|
||||
private static ByteBuffer yuv420ThreePlanesToNV21(ImageProxy.PlaneProxy[] yuv420888planes, int width, int height) {
|
||||
int imageSize = width * height;
|
||||
byte[] out = new byte[imageSize + 2 * (imageSize / 4)];
|
||||
|
||||
if (areUVPlanesNV21(yuv420888planes, width, height)) {
|
||||
// Copy the Y values.
|
||||
yuv420888planes[0].getBuffer().get(out, 0, imageSize);
|
||||
|
||||
ByteBuffer uBuffer = yuv420888planes[1].getBuffer();
|
||||
ByteBuffer vBuffer = yuv420888planes[2].getBuffer();
|
||||
// Get the first V value from the V buffer, since the U buffer does not contain it.
|
||||
vBuffer.get(out, imageSize, 1);
|
||||
// Copy the first U value and the remaining VU values from the U buffer.
|
||||
uBuffer.get(out, imageSize + 1, 2 * imageSize / 4 - 1);
|
||||
} else {
|
||||
// Fallback to copying the UV values one by one, which is slower but also works.
|
||||
// Unpack Y.
|
||||
unpackPlane(yuv420888planes[0], width, height, out, 0, 1);
|
||||
// Unpack U.
|
||||
unpackPlane(yuv420888planes[1], width, height, out, imageSize + 1, 2);
|
||||
// Unpack V.
|
||||
unpackPlane(yuv420888planes[2], width, height, out, imageSize, 2);
|
||||
}
|
||||
|
||||
return ByteBuffer.wrap(out);
|
||||
}
|
||||
|
||||
/** Checks if the UV plane buffers of a YUV_420_888 image are in the NV21 format. */
|
||||
private static boolean areUVPlanesNV21(ImageProxy.PlaneProxy[] planes, int width, int height) {
|
||||
int imageSize = width * height;
|
||||
|
||||
ByteBuffer uBuffer = planes[1].getBuffer();
|
||||
ByteBuffer vBuffer = planes[2].getBuffer();
|
||||
|
||||
// Backup buffer properties.
|
||||
int vBufferPosition = vBuffer.position();
|
||||
int uBufferLimit = uBuffer.limit();
|
||||
|
||||
// Advance the V buffer by 1 byte, since the U buffer will not contain the first V value.
|
||||
vBuffer.position(vBufferPosition + 1);
|
||||
// Chop off the last byte of the U buffer, since the V buffer will not contain the last U value.
|
||||
uBuffer.limit(uBufferLimit - 1);
|
||||
|
||||
// Check that the buffers are equal and have the expected number of elements.
|
||||
boolean areNV21 =
|
||||
(vBuffer.remaining() == (2 * imageSize / 4 - 2)) && (vBuffer.compareTo(uBuffer) == 0);
|
||||
|
||||
// Restore buffers to their initial state.
|
||||
vBuffer.position(vBufferPosition);
|
||||
uBuffer.limit(uBufferLimit);
|
||||
|
||||
return areNV21;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack an image plane into a byte array.
|
||||
*
|
||||
* <p>The input plane data will be copied in 'out', starting at 'offset' and every pixel will be
|
||||
* spaced by 'pixelStride'. Note that there is no row padding on the output.
|
||||
*/
|
||||
private static void unpackPlane(
|
||||
ImageProxy.PlaneProxy plane, int width, int height, byte[] out, int offset, int pixelStride) {
|
||||
ByteBuffer buffer = plane.getBuffer();
|
||||
buffer.rewind();
|
||||
|
||||
// Compute the size of the current plane.
|
||||
// We assume that it has the aspect ratio as the original image.
|
||||
int numRow = (buffer.limit() + plane.getRowStride() - 1) / plane.getRowStride();
|
||||
if (numRow == 0) {
|
||||
return;
|
||||
}
|
||||
int scaleFactor = height / numRow;
|
||||
int numCol = width / scaleFactor;
|
||||
|
||||
// Extract the data in the output buffer.
|
||||
int outputPos = offset;
|
||||
int rowStart = 0;
|
||||
for (int row = 0; row < numRow; row++) {
|
||||
int inputPos = rowStart;
|
||||
for (int col = 0; col < numCol; col++) {
|
||||
out[outputPos] = buffer.get(inputPos);
|
||||
outputPos += pixelStride;
|
||||
inputPos += plane.getPixelStride();
|
||||
}
|
||||
rowStart += plane.getRowStride();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean deleteDirectoryContent(File directoryToBeDeleted, boolean deleteDirectory) {
|
||||
File[] allContents = directoryToBeDeleted.listFiles();
|
||||
if (allContents != null) {
|
||||
for (File file : allContents) {
|
||||
deleteDirectoryContent(file, true);
|
||||
}
|
||||
}
|
||||
return directoryToBeDeleted.delete();
|
||||
}
|
||||
|
||||
public static String getFileExtension(Uri uri, Context context) {
|
||||
String fileType = context.getContentResolver().getType(uri);
|
||||
return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="0.92"
|
||||
android:scaleY="0.92"
|
||||
android:translateX="0.96"
|
||||
android:translateY="0.96">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="0.92"
|
||||
android:scaleY="0.92"
|
||||
android:translateX="0.96"
|
||||
android:translateY="0.96">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M8,5v14l11,-7z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="0.92"
|
||||
android:scaleY="0.92"
|
||||
android:translateX="0.96"
|
||||
android:translateY="0.96">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,6h12v12H6z"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 272 B |
|
After Width: | Height: | Size: 984 B |
|
After Width: | Height: | Size: 140 B |
|
After Width: | Height: | Size: 239 B |
|
After Width: | Height: | Size: 171 B |
|
After Width: | Height: | Size: 615 B |
|
After Width: | Height: | Size: 119 B |
|
After Width: | Height: | Size: 383 B |
|
After Width: | Height: | Size: 282 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 168 B |
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 374 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 197 B |
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="yellow">#FFFEF102</color>
|
||||
<color name="black">#FF000000</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFD60A</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="Theme.Main" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="android:statusBarColor">@color/yellow</item>
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:navigationBarColor">@color/black</item>
|
||||
<item name="android:windowLightNavigationBar" tools:targetApi="27">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="o_mr1">shortEdges</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<external-cache-path name="share" path="share/" />
|
||||
</paths>
|
||||
@@ -0,0 +1,5 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version '8.10.0' apply false
|
||||
id 'com.android.library' version '8.10.0' apply false
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app"s APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
android.nonFinalResIds=false
|
||||
@@ -0,0 +1,6 @@
|
||||
#Mon May 02 15:39:12 BST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://code.gri.mw/DEV/gradle/releases/download/v8.11.1/gradle-8.11.1-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
@@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -0,0 +1,26 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
// Private mirror only when its coordinates are supplied (-PmavenHost=… as in upstream CI);
|
||||
// everyone else resolves plugins from the public repositories.
|
||||
def mavenHost = providers.gradleProperty("mavenHost")
|
||||
if (mavenHost.present) {
|
||||
def mavenUser = providers.gradleProperty("mavenUser").get()
|
||||
def mavenPassword = providers.gradleProperty("mavenPassword").get()
|
||||
["gradle-plugin-portal", "google-maven", "maven-central"].each { repo ->
|
||||
maven {
|
||||
credentials {
|
||||
username mavenUser
|
||||
password mavenPassword
|
||||
}
|
||||
url "${mavenHost.get()}/repository/${repo}/"
|
||||
allowInsecureProtocol = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
}
|
||||
include ':app'
|
||||
@@ -0,0 +1,88 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
/// The GRIM commit Goblin forked from; builds count commits on top of it.
|
||||
const GOBLIN_FORK_BASE: &str = "b51a46b";
|
||||
|
||||
fn main() {
|
||||
built::write_built_file().expect("Failed to acquire build-time information");
|
||||
|
||||
// Goblin versioning is build-based: Build N = commits since the fork.
|
||||
// An explicit GOBLIN_BUILD env wins (CI builds from the public single-commit
|
||||
// squash where the fork base isn't an ancestor, so the git count can't run);
|
||||
// otherwise count commits since the fork; "dev" only as a last resort.
|
||||
let build = env::var("GOBLIN_BUILD")
|
||||
.ok()
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.or_else(|| {
|
||||
Command::new("git")
|
||||
.args([
|
||||
"rev-list",
|
||||
"--count",
|
||||
&format!("{}..HEAD", GOBLIN_FORK_BASE),
|
||||
])
|
||||
.output()
|
||||
.ok()
|
||||
.filter(|o| o.status.success())
|
||||
.and_then(|o| String::from_utf8(o.stdout).ok())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
})
|
||||
.unwrap_or_else(|| "dev".to_string());
|
||||
println!("cargo:rustc-env=GOBLIN_BUILD={}", build);
|
||||
// .git/HEAD only changes on branch switches; the reflog is appended on
|
||||
// every commit, so the build number stays current.
|
||||
println!("cargo:rerun-if-changed=.git/HEAD");
|
||||
println!("cargo:rerun-if-changed=.git/logs/HEAD");
|
||||
println!("cargo:rerun-if-env-changed=GOBLIN_BUILD");
|
||||
|
||||
// Setting up git hooks in the project: rustfmt and so on.
|
||||
let git_hooks = format!(
|
||||
"git config core.hooksPath {}",
|
||||
PathBuf::from("./.hooks").to_str().unwrap()
|
||||
);
|
||||
|
||||
if cfg!(target_os = "windows") {
|
||||
Command::new("cmd")
|
||||
.args(&["/C", &git_hooks])
|
||||
.output()
|
||||
.expect("failed to execute git config for hooks");
|
||||
} else {
|
||||
Command::new("sh")
|
||||
.args(&["-c", &git_hooks])
|
||||
.output()
|
||||
.expect("failed to execute git config for hooks");
|
||||
}
|
||||
|
||||
// Goblin links the Nym mixnet SDK in-process (see src/nym/) — no sidecar
|
||||
// subprocess, no bundled/embedded helper binary, and no Tor/webtunnel. There
|
||||
// is nothing transport-related to build or embed here.
|
||||
|
||||
// Embed the Goblin icon into goblin.exe so Explorer, the taskbar and Alt-Tab
|
||||
// show it even for the bare exe (the .msi shortcuts already carry it). No-op
|
||||
// on every non-Windows platform.
|
||||
embed_windows_icon();
|
||||
}
|
||||
|
||||
/// Embed `wix/Product.ico` (the yellow Goblin icon) as goblin.exe's application
|
||||
/// icon resource. Gated to Windows hosts — that's where the `winresource`
|
||||
/// build-dependency is compiled and where the MSVC resource compiler (`rc.exe`,
|
||||
/// shipped on the windows-latest runner) is available; our Windows builds are
|
||||
/// always native MSVC, so host == target == windows.
|
||||
#[cfg(windows)]
|
||||
fn embed_windows_icon() {
|
||||
if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
|
||||
return;
|
||||
}
|
||||
let mut res = winresource::WindowsResource::new();
|
||||
res.set_icon("wix/Product.ico");
|
||||
if let Err(e) = res.compile() {
|
||||
// Don't fail the build over the icon — just flag it.
|
||||
println!("cargo:warning=winresource icon embed failed: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn embed_windows_icon() {}
|
||||
@@ -0,0 +1,377 @@
|
||||
# Goblin transactions — how a payment works, end to end
|
||||
|
||||
This document explains the full lifecycle of a Goblin payment: how money moves,
|
||||
every status it passes through, and the small guarantees that keep it safe. It is
|
||||
written against the code in `src/nostr/` and `src/wallet/` — function names and
|
||||
files are cited (line numbers drift, so they aren't).
|
||||
|
||||
---
|
||||
|
||||
## 1. The big picture: two layers
|
||||
|
||||
A Goblin payment is **a Grin transaction wrapped in a private nostr message**.
|
||||
|
||||
1. **Grin layer (the money).** Grin/Mimblewimble transactions are *interactive*:
|
||||
the sender and recipient exchange a "slate" that passes through states until
|
||||
it's finalized and posted on-chain. There are no addresses and no amounts on
|
||||
the chain. Goblin reuses GRIM's full Grin node + wallet engine for this.
|
||||
|
||||
2. **Nostr layer (the delivery).** Instead of making you hand slate files back
|
||||
and forth, Goblin delivers each slate as an **end-to-end-encrypted nostr
|
||||
direct message**, routed through the **Nym mixnet**. You pay a `username` or
|
||||
`npub`; the recipient's wallet applies the slate automatically.
|
||||
|
||||
The slate is the payload; nostr is the transport. Everything below is about how
|
||||
those two layers move together and what state is tracked at each step.
|
||||
|
||||
### Slate states (Grin layer)
|
||||
|
||||
Interactive Grin slates pass through numbered states. Goblin uses two flows:
|
||||
|
||||
| Flow | States | Who builds what |
|
||||
| --- | --- | --- |
|
||||
| **Standard** (sender pushes money) | `Standard1` → `Standard2` → `Standard3` | Sender builds S1 (locks their outputs), recipient replies S2, sender finalizes S3 and posts |
|
||||
| **Invoice** (recipient pulls money) | `Invoice1` → `Invoice2` → `Invoice3` | Requester builds I1 (the ask), payer replies I2 (pays), requester finalizes I3 and posts |
|
||||
|
||||
### Status + direction (Goblin's nostr metadata)
|
||||
|
||||
For each payment Goblin stores a `TxNostrMeta` (`src/nostr/types.rs`) keyed by
|
||||
slate id, with a **direction** and a **status**:
|
||||
|
||||
`NostrTxDirection`:
|
||||
- `Sent` — we pushed funds (we created S1).
|
||||
- `Received` — we were paid (we replied S2).
|
||||
- `RequestedByUs` — we issued an invoice (we created I1).
|
||||
- `RequestedOfUs` — someone invoiced us and we paid it (we replied I2).
|
||||
|
||||
`NostrSendStatus`:
|
||||
- `Created` — slate built locally, DM not dispatched yet (durable checkpoint).
|
||||
- `AwaitingS2` — S1 sent, waiting for the recipient's S2 reply.
|
||||
- `ReceivedNoReply` — we processed an incoming S1 (or I1) and built our reply, but haven't dispatched it yet (crash-recovery point).
|
||||
- `RepliedS2` — our S2 reply was dispatched (we received a payment).
|
||||
- `AwaitingI2` — our I1 invoice was sent, waiting for the payer's I2.
|
||||
- `PaidAwaitingFinalize` — we paid an invoice (sent I2); the requester finalizes.
|
||||
- `Finalized` — slate finalized and posted on-chain.
|
||||
- `SendFailed` — DM dispatch failed; eligible for retry.
|
||||
- `Cancelled` — cancelled locally (manual cancel or 24h expiry).
|
||||
|
||||
`Finalized` and `Cancelled` are **terminal**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Identity & addressing
|
||||
|
||||
Your nostr identity is a key that is **deliberately not derived from your wallet
|
||||
seed** (`src/nostr/identity.rs`) — so you can rotate it any time to stay
|
||||
unlinkable without ever touching your funds. It's stored encrypted at rest
|
||||
(NIP-49 ncryptsec under your wallet password).
|
||||
|
||||
You can optionally claim a human-readable **`username`** from a **name authority**
|
||||
(a NIP-05 server). The authority is configurable (Settings → Identity → Name
|
||||
authority; `NostrConfig::{nip05_server, home_domain, set_nip05_server}`), which is
|
||||
what makes Goblin **federated**: a user on `bob@otherinstance.com` can pay
|
||||
`alice@goblin.st`, because a full `name@domain` always resolves against that
|
||||
domain's `/.well-known/nostr.json`. Bare names (`alice`) resolve against *your*
|
||||
configured home authority.
|
||||
|
||||
Display rules (`data::display_name`, no `@` ever shown):
|
||||
- A local **petname** wins.
|
||||
- A verified name on **your home authority** shows bare (`alice`) + a check.
|
||||
- A verified name on a **foreign authority** shows `alice · domain` + a check, so
|
||||
it can never masquerade as a home name.
|
||||
- Otherwise: a short npub.
|
||||
|
||||
Names are kept fresh: see [§11 Name freshness](#11-contacts--name-freshness).
|
||||
|
||||
---
|
||||
|
||||
## 3. Transport: NIP-17 gift wraps over Nym
|
||||
|
||||
A payment DM is built and sent by `send_payment_dm`; control messages (voids) by
|
||||
`send_control_dm` (both in `src/nostr/client.rs`). The message structure
|
||||
(`src/nostr/protocol.rs`):
|
||||
|
||||
- The **payload** is the raw Grin slatepack armor (`BEGINSLATEPACK… ENDSLATEPACK`)
|
||||
inside a kind-14 rumor, prefixed with a human preamble (`[Goblin] GRIN payment
|
||||
message — open in Goblin …`) so a non-Goblin nostr client shows something sane.
|
||||
- **Tags:** a `["goblin","1"]` protocol marker always; an optional `["subject", …]`
|
||||
carrying the user's note (sanitized); control DMs carry
|
||||
`["goblin-action","void", slate_id]` and **no** slatepack.
|
||||
- The rumor is sealed and wrapped as a **kind-1059 gift wrap** (NIP-59 + NIP-44
|
||||
encryption) via nostr-sdk's `send_private_msg_to`. Relays only ever see
|
||||
ciphertext — never the amount, sender, or recipient.
|
||||
|
||||
**Where it's delivered:** the recipient's **kind-10050 DM-relay list**
|
||||
(`fetch_dm_relays`), with our own default relays as fallback, plus any relay
|
||||
hints carried by a pasted `nprofile`. Default relays: `relay.goblin.st`,
|
||||
`relay.damus.io`, `nos.lol` (`src/nostr/relays.rs`), capped at `MAX_DM_RELAYS`.
|
||||
|
||||
**How relays are reached:** every relay connection runs through the in-process
|
||||
**Nym mixnet** SOCKS5 proxy (`NymWebSocketTransport`; `run_service` waits for the
|
||||
proxy to be ready before dialing). The mixnet hides who-talks-to-whom at the
|
||||
network layer. The Grin *node* connection (block sync + broadcasting the final tx)
|
||||
is direct clearnet — it's public chain data, the same for everyone, not tied to
|
||||
your identity.
|
||||
|
||||
The UI tracks an outgoing attempt via a coarse **send phase**
|
||||
(`client::send_phase`): `IDLE / WORKING / SENT / FAILED / REQUEST_BLOCKED`, with a
|
||||
human-readable failure reason on `FAILED`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Flow A — Pay by username/npub (Standard, we send)
|
||||
|
||||
Dispatched as `WalletTask::NostrSend(amount, npub, note, relay_hints)`; handled in
|
||||
`wallet.rs`.
|
||||
|
||||
1. `w.send(amount)` builds the **S1** slate and **locks our outputs** (the funds
|
||||
are reserved but not yet spent).
|
||||
2. **Save meta `Created`** *before* any network call — this is the durable point
|
||||
a crash recovers from.
|
||||
3. `send_payment_dm` delivers S1 → **`AwaitingS2`** (storing the gift-wrap event
|
||||
id). On dispatch failure → **`SendFailed`** (retryable). On success the contact
|
||||
is created/refreshed (so people you pay appear in Suggested) and a background
|
||||
NIP-05 lookup resolves their name.
|
||||
4. The recipient replies S2 (Flow B). When their S2 gift wrap arrives, the ingest
|
||||
guard routes it to `nostr_finalize_post`, which finalizes **S3** and posts it
|
||||
on-chain → **`Finalized`**.
|
||||
|
||||
```
|
||||
Created ──(S1 sent)──▶ AwaitingS2 ──(their S2 arrives)──▶ Finalized
|
||||
└──(dispatch fails)──▶ SendFailed ──(retry)──▶ AwaitingS2
|
||||
└──(manual cancel / 24h expiry)──▶ Cancelled (outputs unlocked)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Flow B — Receiving (Standard, we're paid)
|
||||
|
||||
Our service subscribes to kind-1059 gift wraps addressed to us
|
||||
(`run_service`). When an **S1** arrives, `handle_wrap` runs the ingest pipeline
|
||||
(§7) and `decide()` classifies it by the **accept policy**:
|
||||
|
||||
- `Everyone` → **AutoReceive** (auto-reply S2).
|
||||
- `Contacts` → AutoReceive if the sender is a known contact, else **SurfaceIncoming** (an approval card).
|
||||
- `Ask` → always SurfaceIncoming.
|
||||
|
||||
**AutoReceive:** `nostr_receive` builds the **S2** reply; we save meta
|
||||
`Received` / **`ReceivedNoReply`**, mark the message processed, then dispatch S2 →
|
||||
**`RepliedS2`**. If the S2 dispatch fails we stay at `ReceivedNoReply` and resend
|
||||
on the next start (§9). The sender then finalizes S3 (Flow A step 4).
|
||||
|
||||
```
|
||||
(incoming S1) ──▶ ReceivedNoReply ──(S2 dispatched)──▶ RepliedS2
|
||||
└──(dispatch fails)──▶ stays ReceivedNoReply → resent on restart
|
||||
```
|
||||
|
||||
**SurfaceIncoming** instead stores a `PaymentRequest` (status `Pending`) for the
|
||||
user to approve or decline — see Flow D.
|
||||
|
||||
---
|
||||
|
||||
## 6. Flow C — Request money (Invoice)
|
||||
|
||||
**We request** — `WalletTask::NostrRequest(amount, npub, note, …)`:
|
||||
|
||||
1. First we check the recipient hasn't opted out: `accepts_requests` reads their
|
||||
kind-0 `goblin_accepts_requests` field; an explicit `false` → phase
|
||||
`REQUEST_BLOCKED` and we stop (fail-open: unknown/unreachable = allowed).
|
||||
2. `issue_invoice(amount)` builds **I1** (no outputs locked — it's just an ask).
|
||||
3. Save meta `RequestedByUs / Created`, dispatch I1 → **`AwaitingI2`**.
|
||||
4. When the payer's **I2** arrives, the ingest guard finalizes **I3** and posts →
|
||||
**`Finalized`**.
|
||||
|
||||
**They approve & pay** (the other side of the same flow) is Flow D.
|
||||
|
||||
```
|
||||
Created ──(I1 sent)──▶ AwaitingI2 ──(their I2 arrives)──▶ Finalized
|
||||
└──(SendFailed → retry) └──(cancel / expiry)──▶ Cancelled
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Flow D — Approving an incoming request (we pay an invoice)
|
||||
|
||||
Someone's **I1** is *always* surfaced as a `PaymentRequest`, **never auto-paid**
|
||||
(a hard security invariant). It shows in the Requests list. The user can:
|
||||
|
||||
- **Approve** → `WalletTask::NostrPayRequest(rumor_id)`: re-parse the stored
|
||||
slatepack (must still be I1), `nostr_pay` builds **I2** (this is where *we* pay),
|
||||
save meta `RequestedOfUs / ReceivedNoReply`, dispatch I2 → **`PaidAwaitingFinalize`**,
|
||||
mark the request `Approved`. The requester then finalizes I3.
|
||||
A "Paying…" spinner shows while this runs; the card clears on success.
|
||||
- **Decline** → `WalletTask::NostrDeclineRequest(rumor_id)`: mark `Declined` and
|
||||
send a **void** control DM so the requester's side clears too.
|
||||
|
||||
A surfaced incoming *Standard* S1 (from SurfaceIncoming) is approved the same way,
|
||||
but routes through `nostr_receive` (Flow B) rather than `nostr_pay`.
|
||||
|
||||
`RequestStatus`: `Pending → Approved | Declined | Expired | Cancelled`
|
||||
(`Cancelled` = the requester withdrew it via a void).
|
||||
|
||||
---
|
||||
|
||||
## 8. The ingest guard — `decide()`
|
||||
|
||||
Every incoming gift wrap is judged by `decide()` (`src/nostr/ingest.rs`), a
|
||||
**positive allow-list**: anything not explicitly accepted is `Drop`ped. This is
|
||||
the security core. Summary:
|
||||
|
||||
| Incoming state | Condition | Decision |
|
||||
| --- | --- | --- |
|
||||
| `Standard1` | amount 0, or slate already known | **Drop** |
|
||||
| `Standard1` | new, policy `Everyone` (or `Contacts` + known) | **AutoReceive** |
|
||||
| `Standard1` | new, policy `Contacts` + unknown, or `Ask` | **SurfaceIncoming** |
|
||||
| `Standard2` | matches our pending `Sent` tx (status `AwaitingS2/Created/SendFailed`) **and** sender == stored counterparty **and** the tx exists | **FinalizePost** |
|
||||
| `Standard2` | sender mismatch, or status `Cancelled`/`Finalized`, or no meta | **Drop** |
|
||||
| `Invoice1` | amount 0, already known, or incoming-requests disabled | **Drop** |
|
||||
| `Invoice1` | otherwise | **SurfaceRequest** (never auto-pay) |
|
||||
| `Invoice2` | matches our pending `RequestedByUs` tx + sender match | **FinalizePost** |
|
||||
| `Invoice3` / unknown | — | **Drop** |
|
||||
|
||||
Key consequences:
|
||||
- A **late S2 on a cancelled send** falls through to `Drop` — so cancelling is
|
||||
safe even if the recipient's reply is still in flight (the cancel marks the meta
|
||||
`Cancelled` *first*, and `decide()` then drops the S2).
|
||||
- Finalize only happens for a slate we are actually waiting on, from the exact key
|
||||
we sent to.
|
||||
- Invoices are never auto-paid.
|
||||
|
||||
---
|
||||
|
||||
## 9. Cancel & reclaim
|
||||
|
||||
A stuck outgoing send (recipient never replied) locks your outputs. You can
|
||||
reclaim them manually from the receipt's **"Cancel payment"** button, or the 24h
|
||||
auto-expiry does it for you (§10).
|
||||
|
||||
`WalletTask::NostrCancelSend(slate_id)` (`wallet.rs`):
|
||||
1. Take the `cancel_finalize_lock` — this **serializes against a concurrent S2
|
||||
finalize** so the two can't both win (cancel-and-post would be a double action).
|
||||
2. **Re-check live state under the lock.** If the tx is already `Finalized`, or
|
||||
confirmed/posted on-chain → do nothing and return `CancelOutcome::AlreadyCompleted`
|
||||
("This payment already went through and can't be cancelled"). If already
|
||||
`Cancelled` → idempotent success.
|
||||
3. Otherwise mark the meta **`Cancelled` first**, then `w.cancel(tx_id)` to unlock
|
||||
the Grin outputs, then best-effort send a **void** control DM to the recipient
|
||||
(they're likely offline). → `CancelOutcome::Cancelled` ("your funds are
|
||||
available again").
|
||||
|
||||
**Receipt button visibility** (`cancelable_send` gate): shown only while the send
|
||||
is still unanswered — direction `Sent`, status in `{Created, AwaitingS2,
|
||||
SendFailed}`, **not** confirmed, **not** already cancelled, and either it never
|
||||
reached a relay (`SendFailed`, shown immediately) or the grace window
|
||||
(`cancel_grace_secs`, default 600s) has passed. The instant the recipient accepts
|
||||
(status leaves that set) the button disappears.
|
||||
|
||||
**Recipient side / void ordering:** a void control message marks the slate so that
|
||||
if the recipient's wallet later (or earlier) sees the S1, it's dropped — including
|
||||
the **void-before-S1** race, where the void arrives first and is recorded as
|
||||
`void:{slate_id}:{sender}` so the subsequent S1 is dropped.
|
||||
|
||||
There are sibling tasks for the other directions: `NostrCancelOutgoing` (withdraw
|
||||
an invoice we issued) and `NostrDeclineRequest` (decline an incoming request) —
|
||||
both send a void and mark the local record.
|
||||
|
||||
---
|
||||
|
||||
## 10. Auto-expiry (24h)
|
||||
|
||||
`expire_stale` (`src/nostr/client.rs`) runs from the sync loop. Any non-terminal
|
||||
meta older than `expiry_secs` (default 24h) is expired:
|
||||
|
||||
- If it **locked our outputs** (`expiry_locks_outputs`: a `Sent` send in
|
||||
`Created/AwaitingS2/SendFailed`, or a `RequestedOfUs` invoice we paid in
|
||||
`PaidAwaitingFinalize`) → cancel the Grin tx to unlock, and mark meta `Cancelled`.
|
||||
- If it locked nothing of ours (incoming payments, invoices we issued) → just
|
||||
annotate `Cancelled`.
|
||||
- Pending incoming `PaymentRequest`s flip to `Expired`.
|
||||
|
||||
This is the same unlock path as manual cancel; the manual button just lets you act
|
||||
before the 24h.
|
||||
|
||||
---
|
||||
|
||||
## 11. Crash recovery (`reconcile`)
|
||||
|
||||
On service start, `reconcile` (`client.rs`) re-dispatches any pending outgoing
|
||||
message within a 7-day window, by `(direction, status)`:
|
||||
|
||||
| Direction · status | Slate | Action |
|
||||
| --- | --- | --- |
|
||||
| `Sent` · `Created`/`SendFailed` | Standard1 | resend S1 → `AwaitingS2` |
|
||||
| `RequestedByUs` · `Created`/`SendFailed` | Invoice1 | resend I1 → `AwaitingI2` |
|
||||
| `Received` · `ReceivedNoReply` | Standard2 | resend S2 → `RepliedS2` |
|
||||
| `RequestedOfUs` · `ReceivedNoReply` | Invoice2 | resend I2 → `PaidAwaitingFinalize` |
|
||||
|
||||
Because the slatepack text is persisted and the meta is written *before* every
|
||||
dispatch, a crash at any point is recoverable: re-sending an already-delivered
|
||||
message is harmless (the peer dedups it; see §12).
|
||||
|
||||
---
|
||||
|
||||
## 12. Confirmations (X / N)
|
||||
|
||||
A posted Grin tx matures over `min_confirmations` blocks (default 10) before it's
|
||||
spendable. Grin marks a tx `confirmed` at the **first** block, but Goblin's
|
||||
receipt counts toward the spendable threshold so the number actually moves
|
||||
(`data::receipt_detail`):
|
||||
|
||||
- broadcast, no block yet → `0 / N`
|
||||
- on-chain, immature → `count / N` where `count = tip − inclusion_height + 1`
|
||||
- `count ≥ N` → matured (shown as complete; the receipt's network-fee row is shown
|
||||
only for outgoing payments — a recipient pays no fee).
|
||||
|
||||
---
|
||||
|
||||
## 13. Reliability primitives
|
||||
|
||||
- **Dedup / processed markers** (`store::{is_processed, mark_processed,
|
||||
prune_processed}`): every wrap is recorded at three levels — the gift-wrap event
|
||||
id, the inner rumor id, and `slate:{id}:{state}` — so a replayed or re-sent
|
||||
message is processed exactly once. Markers TTL out after 30 days
|
||||
(pruned on start + hourly).
|
||||
- **Rate limiting** (`allow_sender`): per-sender sliding window — 30 events/hour
|
||||
for known contacts, 10/hour for unknowns — plus a global decrypt ceiling
|
||||
(~120 NIP-44 unwraps/min) to bound CPU/battery against fresh-keypair spam. A
|
||||
message dropped purely for the *global* ceiling isn't marked processed, so it can
|
||||
be retried later.
|
||||
- **Seal integrity:** the gift-wrap seal signer must equal the inner rumor author,
|
||||
and self-addressed messages are dropped.
|
||||
- **The cancel/finalize lock** (§9) prevents a cancel and a finalize from both
|
||||
succeeding on the same slate.
|
||||
|
||||
---
|
||||
|
||||
## 14. Name freshness (contacts)
|
||||
|
||||
Cached `@usernames` are re-validated against the name authority on a periodic
|
||||
sweep (`NAME_REVERIFY_INTERVAL_SECS`, ~78s, capped per tick), and once at app open
|
||||
(persisted `last_name_sweep_at`, gated to the interval).
|
||||
`nip05::check` returns `Verified / Mismatch / Unreachable`: a name is only
|
||||
**cleared** (falls back to the npub) on a definitive `Mismatch` (the server says
|
||||
it's gone or now maps to a different key) — never on a transient network failure.
|
||||
This catches released or reassigned names and stops a freed name from
|
||||
impersonating someone. A user-set petname is never touched.
|
||||
|
||||
---
|
||||
|
||||
## 15. File map
|
||||
|
||||
| Concern | File |
|
||||
| --- | --- |
|
||||
| Status / direction / meta types | `src/nostr/types.rs` |
|
||||
| Gift-wrap + control message build/parse | `src/nostr/protocol.rs` |
|
||||
| Service loop, send/receive/finalize, expiry, reconcile, name sweep | `src/nostr/client.rs` |
|
||||
| Ingest allow-list | `src/nostr/ingest.rs` |
|
||||
| Wallet task handlers (NostrSend / Request / PayRequest / CancelSend / finalize) | `src/wallet/wallet.rs` |
|
||||
| Task definitions | `src/wallet/types.rs` |
|
||||
| Metadata + dedup + contacts store | `src/nostr/store.rs` |
|
||||
| NIP-05 resolve / verify / register, name authority | `src/nostr/nip05.rs` |
|
||||
| Identity (key, NIP-49 backup) | `src/nostr/identity.rs` |
|
||||
| Receipt / activity / confirmations / display name | `src/gui/views/goblin/data.rs` |
|
||||
| Relay defaults + name-authority defaults | `src/nostr/relays.rs` |
|
||||
|
||||
---
|
||||
|
||||
🤖 Documentation written with AI pair-programming assistance (Claude).
|
||||
@@ -0,0 +1,93 @@
|
||||
Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="600.000000pt" height="601.000000pt" viewBox="0 0 600.000000 601.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,601.000000) scale(0.050000,-0.050000)"
|
||||
fill="#ffffff" stroke="none">
|
||||
<path d="M195 11784 c-515 -1551 98 -2966 1520 -3514 171 -66 171 -72 2 -72
|
||||
-415 0 -893 215 -1273 572 -178 167 -181 163 -77 -83 478 -1130 1770 -1734
|
||||
2963 -1384 283 83 309 101 420 292 376 648 1038 1116 1763 1245 l143 26 100
|
||||
202 c887 1780 -911 3344 -3076 2675 l-170 -52 220 -14 c480 -32 818 -114 1118
|
||||
-273 258 -137 548 -414 624 -597 l32 -76 -87 76 c-435 375 -938 524 -1557 461
|
||||
-273 -28 -340 -39 -740 -120 -893 -182 -1449 24 -1756 650 l-98 200 -71 -214z"/>
|
||||
<path d="M9720 10053 c0 -146 -256 -556 -441 -705 -381 -308 -766 -380 -1559
|
||||
-290 -1209 137 -2026 -255 -2519 -1208 -78 -151 -82 -156 -72 -77 18 140 128
|
||||
450 210 592 43 74 73 135 67 135 -25 0 -397 -184 -512 -253 -966 -580 -1594
|
||||
-1674 -1594 -2777 0 -104 -4 -190 -10 -190 -5 0 -65 43 -132 95 -650 503
|
||||
-1118 775 -1606 935 -290 95 -302 94 -242 -15 52 -95 166 -372 191 -465 9 -33
|
||||
34 -121 56 -195 48 -161 120 -508 194 -935 118 -684 437 -1371 795 -1715 185
|
||||
-178 324 -239 594 -262 144 -13 150 -15 147 -63 -1 -27 -14 -174 -28 -326 -83
|
||||
-902 258 -1568 1037 -2024 139 -81 161 -80 127 4 -37 96 -85 369 -96 546 l-10
|
||||
170 46 -60 c328 -431 869 -753 1497 -891 351 -77 1285 -67 1426 15 9 5 -104
|
||||
50 -250 98 -649 217 -1341 668 -1680 1096 -81 102 -88 147 -11 78 157 -142
|
||||
653 -406 925 -493 783 -249 1542 -242 2332 20 l274 91 106 -83 c298 -236 798
|
||||
-328 1259 -231 197 42 196 38 32 169 -156 124 -344 356 -443 546 l-60 116 70
|
||||
52 c544 408 783 906 627 1300 l-41 102 170 138 c442 355 584 624 813 1537 186
|
||||
742 257 952 452 1328 l118 227 -145 -14 c-693 -68 -1425 -411 -1729 -810 -99
|
||||
-130 -112 -127 -165 44 -54 175 -217 511 -308 636 -64 88 -70 91 -224 117
|
||||
-314 52 -637 184 -956 390 -260 168 -509 436 -280 302 127 -74 302 -160 430
|
||||
-211 97 -38 146 -55 348 -118 335 -105 983 -129 1280 -47 961 264 1554 1048
|
||||
1729 2286 28 199 45 685 23 677 -9 -4 -73 -77 -141 -163 -650 -810 -1594
|
||||
-1147 -2451 -873 -251 80 -246 76 -171 121 395 240 638 767 578 1253 -25 209
|
||||
-77 395 -77 278z m-682 -4962 c306 -158 601 -1396 416 -1747 -118 -224 -283
|
||||
-345 -526 -387 -455 -78 -577 227 -456 1143 96 725 320 1118 566 991z m-3115
|
||||
-156 c371 -172 699 -815 799 -1565 34 -251 19 -307 -108 -422 -575 -519 -1624
|
||||
-162 -1931 657 -265 709 593 1630 1240 1330z m5261 -120 c20 -377 -135 -912
|
||||
-290 -995 -70 -38 -72 -35 -157 230 l-64 200 -24 -90 c-50 -192 -144 -340
|
||||
-215 -340 -111 0 -316 804 -228 893 53 53 200 -130 226 -283 l14 -80 36 105
|
||||
c100 293 222 333 332 109 l54 -110 35 105 c89 260 271 427 281 256z m-8491
|
||||
-284 l75 -230 52 160 c89 272 210 336 295 154 126 -268 210 -757 142 -825 -44
|
||||
-44 -163 120 -247 340 -12 33 -19 24 -39 -50 -89 -330 -286 -346 -374 -30
|
||||
l-22 80 -35 -125 c-54 -196 -223 -428 -275 -377 -37 37 -29 448 11 608 70 276
|
||||
219 524 314 524 17 0 56 -87 103 -229z m4164 -2698 c137 -311 883 -373 1408
|
||||
-116 162 78 171 64 42 -61 -317 -305 -854 -408 -1271 -244 -223 87 -516 338
|
||||
-516 442 0 53 140 267 212 324 l58 46 14 -152 c8 -84 31 -191 53 -239z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="600.000000pt" height="601.000000pt" viewBox="0 0 600.000000 601.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,601.000000) scale(0.050000,-0.050000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M195 11784 c-515 -1551 98 -2966 1520 -3514 171 -66 171 -72 2 -72
|
||||
-415 0 -893 215 -1273 572 -178 167 -181 163 -77 -83 478 -1130 1770 -1734
|
||||
2963 -1384 283 83 309 101 420 292 376 648 1038 1116 1763 1245 l143 26 100
|
||||
202 c887 1780 -911 3344 -3076 2675 l-170 -52 220 -14 c480 -32 818 -114 1118
|
||||
-273 258 -137 548 -414 624 -597 l32 -76 -87 76 c-435 375 -938 524 -1557 461
|
||||
-273 -28 -340 -39 -740 -120 -893 -182 -1449 24 -1756 650 l-98 200 -71 -214z"/>
|
||||
<path d="M9720 10053 c0 -146 -256 -556 -441 -705 -381 -308 -766 -380 -1559
|
||||
-290 -1178 133 -2048 -270 -2488 -1152 -57 -115 -92 -149 -92 -89 0 78 123
|
||||
415 199 548 43 74 73 135 67 135 -25 0 -397 -184 -512 -253 -962 -578 -1594
|
||||
-1675 -1594 -2767 0 -209 -2 -210 -154 -95 -393 297 -523 388 -751 525 -321
|
||||
192 -571 311 -843 400 -290 95 -302 94 -242 -15 52 -95 166 -372 191 -465 9
|
||||
-33 34 -121 56 -195 48 -161 120 -508 194 -935 118 -684 437 -1371 795 -1715
|
||||
185 -178 324 -239 594 -262 144 -13 150 -15 147 -63 -1 -27 -14 -174 -28 -326
|
||||
-83 -902 258 -1568 1037 -2024 139 -81 161 -80 127 4 -37 96 -85 369 -96 546
|
||||
l-10 170 46 -60 c328 -431 869 -753 1497 -891 351 -77 1285 -67 1426 15 9 5
|
||||
-104 50 -250 98 -653 218 -1499 778 -1704 1129 -43 73 -54 78 188 -79 230
|
||||
-149 543 -303 750 -369 783 -249 1542 -242 2332 20 l274 91 106 -83 c298 -236
|
||||
798 -328 1259 -231 197 42 196 38 32 169 -156 124 -344 356 -443 546 l-60 116
|
||||
70 52 c544 408 783 906 627 1300 l-41 102 170 138 c442 355 584 624 813 1537
|
||||
186 742 257 952 452 1328 l118 227 -145 -14 c-693 -68 -1425 -411 -1729 -810
|
||||
-99 -130 -112 -127 -165 44 -54 175 -217 511 -308 636 -64 88 -70 91 -224 117
|
||||
-419 70 -947 328 -1223 597 -98 96 -153 192 -70 122 41 -35 332 -177 487 -238
|
||||
97 -38 146 -55 348 -118 335 -105 983 -129 1280 -47 1070 294 1688 1238 1761
|
||||
2691 16 304 7 325 -82 190 -88 -134 -504 -535 -669 -645 -618 -412 -1228 -507
|
||||
-1895 -296 -192 60 -199 77 -67 163 428 277 628 871 480 1428 -20 75 -38 98
|
||||
-38 48z m-682 -4962 c306 -158 601 -1396 416 -1747 -118 -224 -283 -345 -526
|
||||
-387 -455 -78 -577 227 -456 1143 96 725 320 1118 566 991z m-3115 -156 c371
|
||||
-172 699 -815 799 -1565 34 -251 19 -307 -108 -422 -575 -519 -1624 -162
|
||||
-1931 657 -265 709 593 1630 1240 1330z m5261 -120 c20 -377 -135 -912 -290
|
||||
-995 -70 -38 -72 -35 -157 230 l-64 200 -24 -90 c-50 -192 -144 -340 -215
|
||||
-340 -111 0 -316 804 -228 893 53 53 200 -130 226 -283 l14 -80 36 105 c100
|
||||
293 222 333 332 109 l54 -110 35 105 c89 260 271 427 281 256z m-8491 -284
|
||||
l75 -230 52 160 c89 272 210 336 295 154 126 -268 210 -757 142 -825 -44 -44
|
||||
-163 120 -247 340 -12 33 -19 24 -39 -50 -89 -330 -286 -346 -374 -30 l-22 80
|
||||
-35 -125 c-54 -196 -223 -428 -275 -377 -37 37 -29 448 11 608 70 276 219 524
|
||||
314 524 17 0 56 -87 103 -229z m4164 -2698 c137 -310 856 -372 1401 -121 175
|
||||
81 194 76 89 -23 -356 -336 -878 -447 -1310 -278 -224 88 -517 339 -517 443 0
|
||||
53 140 267 212 324 l58 46 14 -152 c8 -84 31 -191 53 -239z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="600.000000pt" height="601.000000pt" viewBox="0 0 600.000000 601.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,601.000000) scale(0.050000,-0.050000)"
|
||||
fill="#ffffff" stroke="none">
|
||||
<path d="M195 11784 c-515 -1551 98 -2966 1520 -3514 171 -66 171 -72 2 -72
|
||||
-415 0 -893 215 -1273 572 -178 167 -181 163 -77 -83 478 -1130 1770 -1734
|
||||
2963 -1384 283 83 309 101 420 292 376 648 1038 1116 1763 1245 l143 26 100
|
||||
202 c887 1780 -911 3344 -3076 2675 l-170 -52 220 -14 c480 -32 818 -114 1118
|
||||
-273 258 -137 548 -414 624 -597 l32 -76 -87 76 c-435 375 -938 524 -1557 461
|
||||
-273 -28 -340 -39 -740 -120 -893 -182 -1449 24 -1756 650 l-98 200 -71 -214z"/>
|
||||
<path d="M9720 10053 c0 -146 -256 -556 -441 -705 -381 -308 -766 -380 -1559
|
||||
-290 -1209 137 -2026 -255 -2519 -1208 -78 -151 -82 -156 -72 -77 18 140 128
|
||||
450 210 592 43 74 73 135 67 135 -25 0 -397 -184 -512 -253 -966 -580 -1594
|
||||
-1674 -1594 -2777 0 -104 -4 -190 -10 -190 -5 0 -65 43 -132 95 -650 503
|
||||
-1118 775 -1606 935 -290 95 -302 94 -242 -15 52 -95 166 -372 191 -465 9 -33
|
||||
34 -121 56 -195 48 -161 120 -508 194 -935 118 -684 437 -1371 795 -1715 185
|
||||
-178 324 -239 594 -262 144 -13 150 -15 147 -63 -1 -27 -14 -174 -28 -326 -83
|
||||
-902 258 -1568 1037 -2024 139 -81 161 -80 127 4 -37 96 -85 369 -96 546 l-10
|
||||
170 46 -60 c328 -431 869 -753 1497 -891 351 -77 1285 -67 1426 15 9 5 -104
|
||||
50 -250 98 -649 217 -1341 668 -1680 1096 -81 102 -88 147 -11 78 157 -142
|
||||
653 -406 925 -493 783 -249 1542 -242 2332 20 l274 91 106 -83 c298 -236 798
|
||||
-328 1259 -231 197 42 196 38 32 169 -156 124 -344 356 -443 546 l-60 116 70
|
||||
52 c544 408 783 906 627 1300 l-41 102 170 138 c442 355 584 624 813 1537 186
|
||||
742 257 952 452 1328 l118 227 -145 -14 c-693 -68 -1425 -411 -1729 -810 -99
|
||||
-130 -112 -127 -165 44 -54 175 -217 511 -308 636 -64 88 -70 91 -224 117
|
||||
-314 52 -637 184 -956 390 -260 168 -509 436 -280 302 127 -74 302 -160 430
|
||||
-211 97 -38 146 -55 348 -118 335 -105 983 -129 1280 -47 961 264 1554 1048
|
||||
1729 2286 28 199 45 685 23 677 -9 -4 -73 -77 -141 -163 -650 -810 -1594
|
||||
-1147 -2451 -873 -251 80 -246 76 -171 121 395 240 638 767 578 1253 -25 209
|
||||
-77 395 -77 278z m-682 -4962 c306 -158 601 -1396 416 -1747 -118 -224 -283
|
||||
-345 -526 -387 -455 -78 -577 227 -456 1143 96 725 320 1118 566 991z m-3115
|
||||
-156 c371 -172 699 -815 799 -1565 34 -251 19 -307 -108 -422 -575 -519 -1624
|
||||
-162 -1931 657 -265 709 593 1630 1240 1330z m5261 -120 c20 -377 -135 -912
|
||||
-290 -995 -70 -38 -72 -35 -157 230 l-64 200 -24 -90 c-50 -192 -144 -340
|
||||
-215 -340 -111 0 -316 804 -228 893 53 53 200 -130 226 -283 l14 -80 36 105
|
||||
c100 293 222 333 332 109 l54 -110 35 105 c89 260 271 427 281 256z m-8491
|
||||
-284 l75 -230 52 160 c89 272 210 336 295 154 126 -268 210 -757 142 -825 -44
|
||||
-44 -163 120 -247 340 -12 33 -19 24 -39 -50 -89 -330 -286 -346 -374 -30
|
||||
l-22 80 -35 -125 c-54 -196 -223 -428 -275 -377 -37 37 -29 448 11 608 70 276
|
||||
219 524 314 524 17 0 56 -87 103 -229z m4164 -2698 c137 -311 883 -373 1408
|
||||
-116 162 78 171 64 42 -61 -317 -305 -854 -408 -1271 -244 -223 87 -516 338
|
||||
-516 442 0 53 140 267 212 324 l58 46 14 -152 c8 -84 31 -191 53 -239z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1 @@
|
||||
goblin.png
|
||||
@@ -0,0 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Name=Goblin
|
||||
Exec=goblin
|
||||
Icon=goblin
|
||||
Type=Application
|
||||
Categories=Finance
|
||||
MimeType=application/x-slatepack;text/plain;
|
||||
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Build a portable, single-file Goblin AppImage.
|
||||
#
|
||||
# Usage: linux/build_release.sh [platform]
|
||||
# platform: 'x86_64' (default) or 'arm'
|
||||
#
|
||||
# Goblin links the Nym SDK IN-PROCESS (src/nym/), so the AppImage is one
|
||||
# self-contained binary with no sidecar to embed or ship beside it.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
platform="${1:-x86_64}"
|
||||
case "${platform}" in
|
||||
x86_64) arch="x86_64-unknown-linux-gnu" ;;
|
||||
arm) arch="aarch64-unknown-linux-gnu" ;;
|
||||
*) echo "Usage: build_release.sh [platform] (platform: 'x86_64' | 'arm')" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Repo root (this script lives in linux/).
|
||||
BASEDIR=$(cd "$(dirname "$0")" && pwd)
|
||||
cd "${BASEDIR}/.."
|
||||
|
||||
# Prefer the GRIM-canonical toolchains (zig + appimagetool from code.gri.mw/DEV);
|
||||
# scripts/toolchain.sh fetches them and writes this env. Falls back to system
|
||||
# installs when it's absent.
|
||||
[ -f .toolchains/env.sh ] && source .toolchains/env.sh
|
||||
|
||||
rustup target add "${arch}"
|
||||
command -v cargo-zigbuild >/dev/null || cargo install cargo-zigbuild
|
||||
|
||||
# Portable cross-build to glibc 2.17. Three zig-specific fixes:
|
||||
# - CRoaring's AVX512 path won't compile under zig's clang (evex512 error).
|
||||
# - OpenSSL is vendored in Cargo.toml, so no system libssl is needed.
|
||||
# - v4l2-sys (camera/QR backend) runs bindgen over linux/videodev2.h, a kernel
|
||||
# UAPI header missing from zig 0.12.1's glibc-2.17 sysroot; point bindgen at
|
||||
# the host's kernel headers. This only reads struct layouts — the actual libc
|
||||
# linkage stays glibc-2.17, so portability is unaffected.
|
||||
export CFLAGS_x86_64_unknown_linux_gnu="-DCROARING_COMPILER_SUPPORTS_AVX512=0"
|
||||
export CXXFLAGS_x86_64_unknown_linux_gnu="-DCROARING_COMPILER_SUPPORTS_AVX512=0"
|
||||
export BINDGEN_EXTRA_CLANG_ARGS="${BINDGEN_EXTRA_CLANG_ARGS:-} -I/usr/include"
|
||||
cargo zigbuild --release --target "${arch}.2.17"
|
||||
|
||||
# Assemble the AppDir: AppRun IS the goblin binary (Nym SDK linked in), plus the
|
||||
# icon + desktop entry. Nothing else.
|
||||
appdir="linux/Goblin.AppDir"
|
||||
cp "target/${arch}/release/goblin" "${appdir}/AppRun"
|
||||
chmod +x "${appdir}/AppRun"
|
||||
|
||||
out="target/${arch}/release/Goblin-${platform}.AppImage"
|
||||
rm -f "target/${arch}/release/"*.AppImage
|
||||
# Use the DEV appimagetool + type2 runtime when fetched, else the system tool.
|
||||
appimagetool_bin="${GOBLIN_APPIMAGETOOL:-appimagetool}"
|
||||
runtime_arg=()
|
||||
[ -n "${GOBLIN_APPIMAGE_RUNTIME:-}" ] && runtime_arg=(--runtime-file "${GOBLIN_APPIMAGE_RUNTIME}")
|
||||
ARCH=x86_64 "${appimagetool_bin}" "${runtime_arg[@]}" "${appdir}" "${out}"
|
||||
echo "built: ${out}"
|
||||
@@ -0,0 +1,808 @@
|
||||
lang_name: Deutsch
|
||||
copy: Kopieren
|
||||
paste: Einfügen
|
||||
continue: Weiter
|
||||
complete: Fertig
|
||||
error: Error
|
||||
retry: Erneut versuchen
|
||||
close: Schließen
|
||||
change: Ändern
|
||||
show: Zeigen
|
||||
delete: Löschen
|
||||
clear: Clear
|
||||
create: Erstellen
|
||||
id: ID
|
||||
kernel: Kernel
|
||||
settings: Einstellungen
|
||||
language: Sprache
|
||||
scan: Scannen
|
||||
qr_code: QR-Code
|
||||
scan_qr: QR-Code scannen
|
||||
repeat: wiederholen
|
||||
scan_result: Scan Ergebnis
|
||||
back: zurück
|
||||
share: teilen
|
||||
theme: 'Theme:'
|
||||
dark: Dunkel
|
||||
light: Hell
|
||||
file: Datei
|
||||
choose_file: Datei auswählen
|
||||
choose_folder: Ordner auswählen
|
||||
crash_report: Absturzbericht
|
||||
crash_report_warning: Anwendung wurde beim letzten Mal unerwartet geschlossen, Sie können den Absturzbericht mit Entwicklern teilen.
|
||||
confirmation: Bestätigung
|
||||
enter_url: URL eingeben
|
||||
max_short: MAX
|
||||
files_location: Dateistandort
|
||||
moving_files: Dateien verschieben
|
||||
wrong_path_error: Falscher Weg angegeben
|
||||
check_updates: Suchen Sie beim Start nach Updates
|
||||
update_available: Update ist verfügbar!
|
||||
changelog: 'Wechselbuch:'
|
||||
wallets:
|
||||
await_conf_amount: Erwarte Bestätigung
|
||||
await_fin_amount: Warten auf die Fertigstellung
|
||||
locked_amount: Gesperrt
|
||||
txs_empty: 'Um Geld manuell oder per Transport zu empfangen oder zu senden, verwenden Sie die Schaltflächen %{message} oder %{transport} unten auf dem Bildschirm. Um die Wallet-Einstellungen zu ändern, drücken Sie %{settings}.'
|
||||
title: Goblin
|
||||
create_desc: Erstellen oder importieren Sie ein bestehendes Wallet mit dem Seed-Phrase.
|
||||
add: Wallet hinzufügen
|
||||
name: 'Name:'
|
||||
pass: 'Passwort:'
|
||||
pass_empty: Wallet Passwort eingeben
|
||||
current_pass: 'Aktuelles Passwort:'
|
||||
new_pass: 'Neues Passwort:'
|
||||
min_tx_conf_count: 'Mindestanzahl an Bestätigungen für Transaktionen:'
|
||||
recover: Wiederherstellen
|
||||
recovery_phrase: Wiederherstellungssatz
|
||||
words_count: 'Wortanzahl:'
|
||||
enter_word: 'Wort #%{number} eingeben:'
|
||||
not_valid_word: Das eingegebene Wort ist ungültig
|
||||
not_valid_phrase: Der eingegebene Satz ist ungültig
|
||||
create_phrase_desc: Schreiben Sie Ihre Wiederherstellungsphrase sicher auf und speichern Sie sie.
|
||||
restore_phrase_desc: Geben Sie Wörter aus Ihrer gespeicherten Wiederherstellungsphrase ein.
|
||||
setup_conn_desc: Wählen Sie aus, wie Ihr Wallet eine Verbindung zum Netzwerk herstellt.
|
||||
conn_method: Verbindungsmethode
|
||||
ext_conn: 'Externe Verbindungen:'
|
||||
add_node: Node hinzufügen
|
||||
node_url: 'Node URL:'
|
||||
node_secret: 'API Secret (optional):'
|
||||
invalid_url: Die eingegebene URL ist ungültig
|
||||
open: Wallet öffnen
|
||||
wrong_pass: Das eingegebene Passwort ist falsch
|
||||
locked: Gesperrt
|
||||
unlocked: Entsperrt
|
||||
enable_node: 'Aktivieren Sie die integrierte Node, um das Wallet zu verwenden, oder ändern Sie die Verbindungseinstellungen, indem Sie unten auf dem Bildschirm %{settings} auswählen.'
|
||||
node_loading: 'Das Wallet wird nach der synchronisation der integrierten Node geladen. Sie können die Verbindungseinstellungen ändern, indem Sie unten auf dem Bildschirm %{settings} auswählen.'
|
||||
loading: Wird geladen
|
||||
closing: Schließen
|
||||
checking: Überprüfung
|
||||
default_wallet: Standard-Wallet
|
||||
new_account_desc: 'Namen des neuen Accounts eingeben:'
|
||||
wallet_loading: Wallet wird geladen
|
||||
wallet_closing: Wallet schließen
|
||||
wallet_checking: Wallet prüfen
|
||||
tx_loading: Laden von Transaktionen
|
||||
default_account: Standardaccount
|
||||
accounts: Accounts
|
||||
tx_sent: Gesendet
|
||||
tx_received: Erhalten
|
||||
tx_sending: Senden
|
||||
tx_receiving: Erhalten
|
||||
tx_confirming: Erwarte Bestätigung
|
||||
tx_canceled: Abgebrochen
|
||||
tx_cancelling: Abbrechen
|
||||
tx_finalizing: Finalisierung
|
||||
tx_posting: Buchungsvorgang
|
||||
tx_confirmed: Bestätigt
|
||||
txs: Transaktionen
|
||||
tx: Transaktion
|
||||
messages: Nachrichten
|
||||
transport: Transport
|
||||
input_slatepack_desc: 'Geben Sie eine Nachricht ein, um eine Antwort zu erstellen oder die Transaktion abzuschließen:'
|
||||
parse_slatepack_err: 'Bei der Verarbeitung der Nachricht ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:'
|
||||
pay_balance_error: 'Der Kontostand reicht nicht aus, um %{amount} ツ und die Netzwerkgebühr zu bezahlen.'
|
||||
parse_i1_slatepack_desc: 'Um %{amount} zu zahlen, senden Sie diese Nachricht an den Empfänger:'
|
||||
parse_i2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu erhalten:'
|
||||
parse_i3_slatepack_desc: 'Transaktion posten, um den Erhalt von %{amount} abzuschließen ツ:'
|
||||
parse_s1_slatepack_desc: 'Um %{amount} zu erhalten, senden Sie diese Nachricht an den Absender:'
|
||||
parse_s2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu senden:'
|
||||
parse_s3_slatepack_desc: 'Transaktion posten, um das Senden von %{amount} abzuschließen ツ:'
|
||||
resp_slatepack_err: 'Beim Erstellen der Antwort ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:'
|
||||
resp_exists_err: 'Eine solche Transaktion existiert bereits.'
|
||||
resp_canceled_err: 'Eine solche Transaktion wurde schon abgebrochen.'
|
||||
create_request_desc: 'Erstellen Sie eine Anfrage zum Senden oder Empfangen der Gelder:'
|
||||
send_request_desc: 'Sie haben eine Anfrage zum Senden von %{amount} ツ erstellt. Senden Sie diese Nachricht an den Empfänger:'
|
||||
send_slatepack_err: Beim Erstellen der Anfrage zum Senden von Geldern ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten.
|
||||
invoice_desc: 'Sie haben eine Anfrage zum Erhalt von %{amount} ツ erstellt. Senden Sie diese Nachricht an den Absender der Gelder:'
|
||||
invoice_slatepack_err: Bei der Rechnungsstellung ist ein Fehler aufgetreten, überprüfen Sie die Eingabedaten.
|
||||
finalize_slatepack_err: 'Bei der Finalisierung ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:'
|
||||
finalize: Abschließen
|
||||
use_dandelion: Dandelion verwenden
|
||||
enter_amount_send: 'Sie haben %{amount} ツ. Geben Sie den zu sendenden Betrag ein:'
|
||||
enter_amount_receive: 'Geben Sie den zu erhaltenden Betrag ein:'
|
||||
recovery: Wiederherstellung
|
||||
repair_wallet: Wallet reparieren
|
||||
repair_desc: Überprüfen Sie ein Wallet und reparieren und stellen Sie bei Bedarf fehlende Ausgaben wieder her. Dieser Vorgang wird einige Zeit dauern.
|
||||
repair_unavailable: Sie benötigen eine aktive Verbindung zum Knoten und eine abgeschlossene Wallet-Synchronisierung.
|
||||
delete: Wallet löschen
|
||||
delete_conf: Sind Sie sicher, dass Sie das Wallet löschen möchten?
|
||||
delete_desc: Stellen Sie sicher, dass Sie Ihre Wiederherstellungsphrase gespeichert haben, um auf Gelder zugreifen zu können.
|
||||
wallet_loading_err: 'Bei der Synchronisierung des Wallets ist ein Fehler aufgetreten. Sie können es erneut versuchen oder die Verbindungseinstellungen ändern, indem Sie unten auf dem Bildschirm %{settings} auswählen.'
|
||||
wallet: Wallet
|
||||
send: Senden
|
||||
receive: Empfangen
|
||||
settings: Wallet Einstellungen
|
||||
tx_send_cancel_conf: 'Sind Sie sicher, dass Sie das Senden von %{amount} ツ abbrechen wollen?'
|
||||
tx_receive_cancel_conf: 'Sind Sie sicher, dass Sie das Empfangen von %{amount} ツ abbrechen wollen?'
|
||||
rec_phrase_not_found: Wiederhestellungsphrase nicht gefunden.
|
||||
restore_wallet_desc: Stellen Sie das Wallet wieder her, indem Sie alle Dateien löschen. Wenn die normale Reparatur nicht geholfen hat, müssen Sie Ihr Wallet erneut öffnen.
|
||||
fee_base_desc: 'Gebühr (basiswert%{value}):'
|
||||
payment_proof: Zahlungsnachweis
|
||||
payment_proof_desc: 'Geben Sie den erhaltenen Zahlungsnachweis ein, um die Transaktion zu verifizieren:'
|
||||
payment_proof_valid: 'Der eingegebene Zahlungsnachweis ist gültig:'
|
||||
payment_proof_error: 'Der eingetragene Zahlungsnachweis ist nicht gültig:'
|
||||
tx_delete_confirmation: Bist du sicher, dass du die Transaktion aus dem Verlauf löschen möchtest?
|
||||
transport:
|
||||
desc: 'Transport verwenden, um Nachrichten synchron zu empfangen oder zu senden:'
|
||||
tor_network: Tor Netzwek
|
||||
connected: verbunden
|
||||
connecting: verbinden
|
||||
disconnecting: Verbindung trennen
|
||||
conn_error: Verbindungsproblem
|
||||
disconnected: Verbindung getrennt
|
||||
receiver_address: 'Empfängeraddresse:'
|
||||
incorrect_addr_err: 'Eingegebene Addresse ist inkorrekt:'
|
||||
tor_send_error: Beim Senden über Tor ist ein Fehler aufgetreten. Stellen Sie sicher, dass der Empfänger online ist. Die Transaktion wurde abgebrochen.
|
||||
tor_autorun_desc: Gibt an, ob beim Öffnen des Wallets der Tor-Dienst gestartet werden soll, um Transaktionen synchron zu empfangen.
|
||||
tor_sending: Sende über Tor
|
||||
tor_settings: Tor Einstellungen
|
||||
bridges: Brücken
|
||||
bridges_desc: Richten Sie Brücken ein, um die Zensur des Tor-Netzwerks zu umgehen, wenn die normale Verbindung nicht funktioniert.
|
||||
bin_file: 'Binärdatei:'
|
||||
conn_line: 'Verbindungsleitung:'
|
||||
bridges_disabled: Brücken deaktiviert
|
||||
bridge_name: 'Brücke %{b}'
|
||||
network:
|
||||
self: Netzwerk
|
||||
type: 'Netzwerk Typ:'
|
||||
mainnet: Main
|
||||
testnet: Test
|
||||
connections: Verbindungen
|
||||
node: Integrierte Node
|
||||
metrics: Metriken
|
||||
mining: Mining
|
||||
settings: Node Einstellungen
|
||||
enable_node: Node aktivieren
|
||||
autorun: Autorun
|
||||
disabled_server: 'Aktivieren Sie die integrierte Node oder fügen Sie eine weitere Verbindungsmethode hinzu, indem Sie oben links auf dem Bildschirm auf %{dots} drücken.'
|
||||
no_ips: Auf Ihrem System sind keine IP-Adressen verfügbar. Der Server kann nicht gestartet werden. Überprüfen Sie Ihre Netzwerkkonnektivität.
|
||||
available: Verfügbar
|
||||
not_available: Nicht verfügbar
|
||||
availability_check: Verfügbarkeitsprüfung
|
||||
android_warning: Achtung an Android-Benutzer. Um integrierte Nodes erfolgreich zu synchronisieren, müssen Sie in den Systemeinstellungen Ihres Telefons den Zugriff auf Benachrichtigungen zulassen und die Beschränkungen für die Akkunutzung für die Grim-Anwendung entfernen. Dies ist ein notwendiger Vorgang, damit die Anwendung im Hintergrund korrekt funktioniert.
|
||||
sync_status:
|
||||
node_restarting: Node wird neu gestartet
|
||||
node_down: Node ist ausgefallen
|
||||
initial: Node startet
|
||||
no_sync: Node läuft
|
||||
awaiting_peers: Warten auf Peers
|
||||
header_sync: Header werden heruntergeladen
|
||||
header_sync_percent: 'Header werden heruntergeladen: %{percent}%'
|
||||
tx_hashset_pibd: Downloadstatus (PIBD)
|
||||
tx_hashset_pibd_percent: 'Downloadstatus (PIBD): %{percent}%'
|
||||
tx_hashset_download: Downloadstatus
|
||||
tx_hashset_download_percent: 'Downloadstatus: %{percent}%'
|
||||
tx_hashset_setup_history: 'Vorbereitungsstatus (Verlauf): %{percent}%'
|
||||
tx_hashset_setup_position: 'Vorbereitungsstatus (Position): %{percent}%'
|
||||
tx_hashset_setup: Vorbereitungszustand
|
||||
tx_hashset_range_proofs_validation: 'Validierungsstatus (range proofs): %{percent}%'
|
||||
tx_hashset_kernels_validation: 'Validierungsstatus (Kernel): %{percent}%'
|
||||
tx_hashset_save: Finalisierender Chainstatus
|
||||
body_sync: Blöcke herunterladen
|
||||
body_sync_percent: 'Blöcke herunterladen: %{percent}%'
|
||||
shutdown: Node wird heruntergefahren
|
||||
network_node:
|
||||
header: Header
|
||||
block: Block
|
||||
hash: Hash
|
||||
height: Höhe
|
||||
difficulty: Schwierigkeit
|
||||
time: Zeit
|
||||
main_pool: Hauptpool
|
||||
stem_pool: Stem-Pool
|
||||
data: Daten
|
||||
size: Größe (GB)
|
||||
peers: Peers
|
||||
error_clean:
|
||||
resync: Neu synchronisieren
|
||||
error_p2p_api: 'Während der Initialisierung des %{p2p_api}-Servers ist ein Fehler aufgetreten. Überprüfen Sie die %{p2p_api}-Einstellungen, indem Sie unten auf dem Bildschirm %{settings} auswählen.'
|
||||
error_config: 'Während der Initialisierung der Konfiguration ist ein Fehler aufgetreten. Überprüfen Sie die Einstellungen, indem Sie unten auf dem Bildschirm %{settings} auswählen.'
|
||||
error_unknown: 'Während der Initialisierung ist ein Fehler aufgetreten. Überprüfen Sie die integrierten Knoteneinstellungen, indem Sie unten auf dem Bildschirm %{settings} auswählen oder erneut synchronisieren.'
|
||||
network_metrics:
|
||||
loading: Metriken werden nach der Synchronisierung verfügbar sein
|
||||
emission: Emission
|
||||
inflation: Inflation
|
||||
supply: Supply
|
||||
block_time: Blockzeit
|
||||
reward: Belohnung
|
||||
difficulty_window: 'Schwierigkeitsfenster %{size}'
|
||||
network_mining:
|
||||
loading: Mining wird nach der Synchronisierung verfügbar sein
|
||||
info: 'Mining-Server aktiviert ist, können Sie seine Einstellungen ändern, indem Sie unten auf dem Bildschirm %{settings} wählen. Die Daten werden aktualisiert, wenn Geräte angeschlossen sind.'
|
||||
restart_server_required: Ein Neustart des Servers ist erforderlich, um die Änderungen zu übernehmen.
|
||||
rewards_wallet: Brieftasche für Belohnungen
|
||||
server: Stratum Server
|
||||
address: Addresse
|
||||
miners: Miner
|
||||
devices: Geräte
|
||||
blocks_found: Gefundene Blöcke
|
||||
hashrate: 'Hashrate (C%{bits})'
|
||||
connected: Verbunden
|
||||
disconnected: Getrennt
|
||||
network_settings:
|
||||
change_value: Wert ändern
|
||||
stratum_ip: 'Stratum IP Addresse:'
|
||||
stratum_port: 'Stratum Port:'
|
||||
port_unavailable: Der angegebene Port ist nicht verfügbar
|
||||
restart_node_required: Ein Neustart der Node ist erforderlich, um die Änderungen zu übernehmen.
|
||||
choose_wallet: Wählen Wallet
|
||||
stratum_wallet_warning: Wallet muss geöffnet sein, um Belohnungen zu erhalten.
|
||||
enable: Aktivieren
|
||||
disable: Deaktivieren
|
||||
restart: Neustarten
|
||||
server: Server
|
||||
api_ip: 'API IP Addresse:'
|
||||
api_port: 'API Port:'
|
||||
api_secret: 'Rest API and V2 Owner API token:'
|
||||
foreign_api_secret: 'Fremdes API token:'
|
||||
disabled: Deaktiviert
|
||||
enabled: Aktiviert
|
||||
ftl: 'Das Future Time Limit (FTL):'
|
||||
ftl_description: Begrenzung, wie weit in der Zukunft, relativ zur lokalen Zeit eines Knotens in Sekunden, der Zeitstempel eines neuen Blocks liegen darf, damit der Block akzeptiert wird.
|
||||
not_valid_value: Eingegebener Wert ist nicht gültig
|
||||
full_validation: Vollständige Validierung
|
||||
full_validation_description: Ob bei der Verarbeitung jedes Blocks eine vollständige Kettenvalidierung durchgeführt werden soll (außer bei der Synchronisierung).
|
||||
archive_mode: Archiv Modus
|
||||
archive_mode_desc: Führen Sie den Knoten im vollständigen Archivmodus aus (für die Synchronisierung wird mehr Speicherplatz und Zeit benötigt).
|
||||
attempt_time: 'Zeit des Miningsversuches (in Sekunden):'
|
||||
attempt_time_desc: Die Zeitspanne, in der versucht wird, eine bestimmte Kopfzeile abzubauen, bevor der Abbau gestoppt und die Transaktionen erneut aus dem Pool gesammelt werden
|
||||
min_share_diff: 'Der Mindestschwierigkeitsgrad des Shares:'
|
||||
reset_settings_desc: Nodeeinstellungen auf Standardwerte zurücksetzen
|
||||
reset_settings: Einstellungen zurücksetzen
|
||||
reset: zurücksetzen
|
||||
tx_pool: Transaktionspool
|
||||
pool_fee: 'Grundgebühr, die in den Pool aufgenommen wird:'
|
||||
reorg_period: 'Aufbewahrungsdauer des Reorg-Caches (in Minuten):'
|
||||
max_tx_pool: 'Maximale Anzahl von Transaktionen im Pool:'
|
||||
max_tx_stempool: 'Maximale Anzahl von Transaktionen im Stamm-Pool:'
|
||||
max_tx_weight: 'Maximales Gesamtgewicht der Transaktionen, die zur Bildung eines Blocks ausgewählt werden können:'
|
||||
epoch_duration: 'Epochendauer (in Sekunden):'
|
||||
embargo_timer: 'Embargo-Timer (in Sekunden):'
|
||||
aggregation_period: 'Aggregationszeitraum (in Sekunden):'
|
||||
stem_probability: 'Wahrscheinlichkeit der Stem-Phase:'
|
||||
stem_txs: Stem Transaktionen
|
||||
p2p_server: P2P Server
|
||||
p2p_port: 'P2P port:'
|
||||
add_seed: DNS-Seed hinzufügen
|
||||
seed_address: 'DNS Seed Addresse:'
|
||||
add_peer: Peer hinzufügen
|
||||
peer_address: 'Peer Addresse:'
|
||||
peer_address_error: 'Geben Sie die IP-Adresse oder den DNS-Namen (stellen Sie sicher, dass der angegebene Host verfügbar ist) im richtigen Format ein, z. B: 192.168.0.1:1234 oder example.com:5678'
|
||||
default: Standardeinstellung
|
||||
allow_list: Erlaubt-Liste
|
||||
allow_list_desc: Nur mit diesen Peers verbinden
|
||||
deny_list: Ablehnungsliste
|
||||
deny_list_desc: Niemals mit diesen Peers verbinden
|
||||
favourites: Favoriten
|
||||
favourites_desc: Eine Liste der bevorzugten Peers, mit denen eine Verbindung hergestellt werden soll.
|
||||
ban_window: 'Wie viel Zeit (in Sekunden) ein gesperrter Peer gesperrt bleiben soll:'
|
||||
ban_window_desc: Die Entscheidung über das Verbot trifft der Knoten auf der Grundlage der Korrektheit der von der Gegenstelle erhaltenen Daten.
|
||||
max_inbound_count: 'Maximale Anzahl der eingehenden Peer-Verbindungen:'
|
||||
max_outbound_count: 'Maximale Anzahl von ausgehenden Peer-Verbindungen:'
|
||||
reset_data_desc: Reset-Knotendaten. Verwenden Sie diese Funktion nur, wenn es Probleme mit der Synchronisation gibt.
|
||||
reset_data: Daten zurücksetzten
|
||||
modal:
|
||||
cancel: Abbrechen
|
||||
save: Speichern
|
||||
add: Hinzufügen
|
||||
modal_exit:
|
||||
description: Sind Sie sicher, dass Sie die Anwendung beenden wollen?
|
||||
exit: Schließen
|
||||
app_settings:
|
||||
proxy: Proxy
|
||||
proxy_desc: Lohnt es sich, einen Proxy für Netzwerkanfragen von der Anwendung zu verwenden.
|
||||
keyboard:
|
||||
1: 1
|
||||
2: 2
|
||||
3: 3
|
||||
4: 4
|
||||
5: 5
|
||||
6: 6
|
||||
7: 7
|
||||
8: 8
|
||||
9: 9
|
||||
0: 0
|
||||
01: ß
|
||||
q: q
|
||||
w: w
|
||||
e: e
|
||||
r: r
|
||||
t: t
|
||||
y: z
|
||||
u: u
|
||||
i: i
|
||||
o: o
|
||||
p: p
|
||||
p1: ü
|
||||
a: a
|
||||
s: s
|
||||
d: d
|
||||
f: f
|
||||
g: g
|
||||
h: h
|
||||
j: j
|
||||
k: k
|
||||
l: l
|
||||
l1: ö
|
||||
l2: ä
|
||||
z: y
|
||||
x: x
|
||||
c: c
|
||||
v: v
|
||||
b: b
|
||||
n: n
|
||||
m: m
|
||||
m1: ','
|
||||
m2: .
|
||||
m3: '/'
|
||||
goblin:
|
||||
home:
|
||||
anonymous: "Anonym"
|
||||
connected_nym: "Über Nym verbunden"
|
||||
nym_ready: "Nym bereit · Relays…"
|
||||
connecting_nym: "Verbinde mit Nym…"
|
||||
cant_reach_node: "Node nicht erreichbar"
|
||||
node_synced: "Node synchronisiert"
|
||||
syncing: "Synchronisiere…"
|
||||
block: "Block %{height}"
|
||||
waiting_for_chain: "Warte auf Chain…"
|
||||
nav_wallet: "Wallet"
|
||||
nav_pay: "Zahlen"
|
||||
nav_activity: "Aktivität"
|
||||
nav_receive: "Empfangen"
|
||||
nav_settings: "Einstellungen"
|
||||
activity: "Aktivität"
|
||||
empty_title: "Noch keine Aktivität"
|
||||
empty_sub: "Sende oder empfange grin, um zu starten."
|
||||
recent: "Zuletzt"
|
||||
scan_to_pay: "Zum Zahlen scannen"
|
||||
type_amount: "Betrag eingeben"
|
||||
request: "Anfordern"
|
||||
pay: "Zahlen"
|
||||
enter_amount: "Betrag zum Zahlen oder Anfordern eingeben"
|
||||
activity:
|
||||
canceled: "abgebrochen"
|
||||
pending: "ausstehend"
|
||||
earlier: "Früher"
|
||||
today: "Heute"
|
||||
yesterday: "Gestern"
|
||||
title: "Aktivität"
|
||||
requests: "Anfragen"
|
||||
empty_title: "Noch keine Aktivität"
|
||||
empty_sub: "Deine Zahlungen erscheinen hier."
|
||||
pending_header: "Ausstehend"
|
||||
receipt:
|
||||
title: "Beleg"
|
||||
not_found: "Transaktion nicht gefunden"
|
||||
for_note: "Für %{note}"
|
||||
details: "Transaktionsdetails"
|
||||
canceled: "Abgebrochen"
|
||||
expired: "Abgelaufen"
|
||||
funds_returned: "Guthaben zurückerstattet"
|
||||
complete: "Abgeschlossen"
|
||||
payment_received: "Zahlung empfangen"
|
||||
payment_sent: "Zahlung erfolgreich gesendet"
|
||||
pending: "Ausstehend"
|
||||
confs: "%{c}/%{r} Bestätigungen"
|
||||
waiting_to_confirm: "Warte auf Bestätigung"
|
||||
paying: "Zahlung läuft…"
|
||||
you: "Du"
|
||||
to: "An"
|
||||
from: "Von"
|
||||
nostr: "nostr"
|
||||
fee_none: "Keine"
|
||||
network_fee: "Netzwerkgebühr"
|
||||
privacy: "Privatsphäre"
|
||||
privacy_value: "Mimblewimble + Nym"
|
||||
transaction: "Transaktion"
|
||||
cancel_request: "Anfrage abbrechen"
|
||||
cancel_send: "Zahlung abbrechen"
|
||||
cancel_send_confirm: "Zum Abbrechen erneut tippen — sie könnten sie noch erhalten"
|
||||
cancel_send_done: "Zahlung abgebrochen — dein Guthaben ist wieder verfügbar"
|
||||
cancel_send_too_late: "Diese Zahlung ist bereits durchgegangen und kann nicht abgebrochen werden"
|
||||
waiting_to_receive: "Warte, bis %{name} empfängt…"
|
||||
request:
|
||||
title: "%{name} fordert an"
|
||||
approve: "Annehmen"
|
||||
decline: "Ablehnen"
|
||||
review_title: "Anfrage prüfen"
|
||||
hold_to_accept: "Zum Annehmen halten"
|
||||
hold_accept_hint: "Halte gedrückt, um diese Anfrage zu bezahlen"
|
||||
receive:
|
||||
title: "Empfangen"
|
||||
requesting: "Fordere %{amt}%{tsu} an — teilen, um bezahlt zu werden"
|
||||
clear_request: "Anfrage löschen"
|
||||
share_handle: "Teile deinen Handle, um bezahlt zu werden"
|
||||
copied: "Kopiert"
|
||||
copy_nostr_id: "nostr-ID kopieren"
|
||||
copy_address: "Adresse kopieren"
|
||||
copy_npub: "npub kopieren"
|
||||
share_message: "Bezahl mich auf Goblin (goblin.st) — %{npub}"
|
||||
privacy_note: "Dein Benutzername ist öffentlich. Zahlungsinhalte bleiben im Netzwerk verschlüsselt."
|
||||
profile:
|
||||
title: "Profil"
|
||||
activity: "Aktivität"
|
||||
no_activity: "Noch keine Aktivität mit ihnen."
|
||||
unblock: "Entsperren"
|
||||
block: "Sperren"
|
||||
blocked_blurb: "Gesperrt — ihre Zahlungen und Anfragen werden verworfen."
|
||||
block_blurb: "Sperren verwirft eingehende Zahlungen und Anfragen von ihnen."
|
||||
settings:
|
||||
title: "Einstellungen"
|
||||
connected_nostr: "Mit nostr verbunden"
|
||||
connecting_relays: "Verbinde mit Relays…"
|
||||
identity: "Identität"
|
||||
copy_npub: "npub kopieren (öffentlich)"
|
||||
rotate_key: "nostr-Schlüssel wechseln"
|
||||
import_identity: "Identität importieren (.backup / nsec)"
|
||||
backup_note: "Gerät wechseln? Sichere BEIDES: deine Seed-Phrase (Guthaben) und deine Identitäts-.backup-Datei (Name + Schlüssel)."
|
||||
wallet: "Wallet"
|
||||
display_unit: "Anzeigeeinheit"
|
||||
relays: "Relays"
|
||||
node: "Node"
|
||||
slatepacks: "Slatepacks"
|
||||
slatepacks_value: "Manuelle Transaktion"
|
||||
lock_wallet: "Wallet sperren"
|
||||
switch_wallet: "Wallet wechseln"
|
||||
advanced: "Erweitert"
|
||||
privacy: "Privatsphäre"
|
||||
mixnet_routing: "Mixnet-Routing"
|
||||
messages_lookups: "Nachrichten & Abfragen"
|
||||
auto_accept: "Automatisch annehmen"
|
||||
pairing: "Kopplung"
|
||||
accept_anyone: "Jeder"
|
||||
accept_contacts: "Nur Kontakte"
|
||||
accept_ask: "Immer fragen"
|
||||
requests: "Anfragen"
|
||||
incoming_requests: "Eingehende Anfragen"
|
||||
incoming_requests_sub: "Erlaube anderen, Geld von dir anzufordern"
|
||||
appearance: "Erscheinungsbild"
|
||||
theme: "Design"
|
||||
theme_light: "Hell"
|
||||
theme_dark: "Dunkel"
|
||||
theme_yellow: "Gelb"
|
||||
archive: "Archiv"
|
||||
export_archive: "Archiv exportieren"
|
||||
wipe_history: "Zahlungsverlauf löschen"
|
||||
about: "Über"
|
||||
goblin: "Goblin"
|
||||
build: "Build %{build}"
|
||||
network: "Netzwerk"
|
||||
network_value: "MW + Nym mixnet + nostr"
|
||||
third_party: "Drittanbieter"
|
||||
grim: "GRIM (Upstream-Wallet)"
|
||||
grin_node: "Grin-Node"
|
||||
sp_intro: "Erweitert — rohe slatepacks von Hand austauschen, so wie GRIM es macht. Nur nutzen, wenn du nicht über einen username zahlen oder bezahlt werden kannst."
|
||||
sp_receive_group: "Empfangen oder abschließen"
|
||||
sp_receive_blurb: "Füge einen slatepack ein, den dir jemand gegeben hat. Goblin empfängt die Zahlung, begleicht die Rechnung oder schließt sie ab und sendet sie."
|
||||
sp_process: "Slatepack verarbeiten"
|
||||
sp_paste_first: "Füge zuerst einen slatepack ein."
|
||||
sp_reply_ready: "Antwort bereit — sende sie an den Absender zurück."
|
||||
sp_finalizing: "Schließe ab und sende an die Chain…"
|
||||
sp_create_group: "Zahlung erstellen"
|
||||
sp_create_blurb: "Erstelle einen slatepack zum Übergeben. Der Empfänger nimmt ihn an, sendet die Antwort zurück, und du schließt sie oben ab."
|
||||
sp_amount_hint: "Betrag in grin"
|
||||
sp_addr_hint: "Empfängeradresse (optional)"
|
||||
sp_create: "Slatepack erstellen"
|
||||
sp_ready: "Slatepack bereit — übergib ihn dem Empfänger."
|
||||
sp_amount_gt_zero: "Gib einen Betrag größer als null ein."
|
||||
sp_to_send: "Zu sendender slatepack"
|
||||
sp_copy: "Slatepack kopieren"
|
||||
rotate_line1: "• Du bekommst einen brandneuen ZUFÄLLIGEN Schlüssel; das alte npub empfängt nichts mehr. Es gibt keine Ableitungskette zwischen beiden."
|
||||
rotate_line2: "• Der neue Schlüssel ist NICHT aus deinem Seed wiederherstellbar — sichere die neue nsec direkt nach dem Wechsel."
|
||||
rotate_line3: "• Dein Benutzername wird FREIGEGEBEN — beanspruche direkt danach denselben oder einen neuen Namen (sobald frei, kann ihn auch jede andere Person nehmen)."
|
||||
rotate_line4: "• Zahlungen, die noch an den alten Schlüssel unterwegs sind, WERDEN gestört — warte zuerst, bis ausstehende Zahlungen abgeschlossen sind."
|
||||
rotate_line5: "• Kontakte, die dein npub direkt gespeichert haben, müssen dich neu finden — teile dein neues npub oder den neu gesicherten username."
|
||||
cancel: "Abbrechen"
|
||||
continue: "Weiter"
|
||||
final_confirmation: "Endgültige Bestätigung"
|
||||
rotate_confirm_blurb: "Dies kann in der App nicht rückgängig gemacht werden. Tippe RESET und gib dein Wallet-Passwort ein, um zu wechseln."
|
||||
type_reset: "RESET tippen"
|
||||
wallet_password: "Wallet-Passwort"
|
||||
rotate_key_btn: "Schlüssel wechseln"
|
||||
rotating_key: "Wechsle Schlüssel…"
|
||||
key_rotated: "Schlüssel gewechselt"
|
||||
new_npub: "Neues npub: %{npub}"
|
||||
backup_new_key: "Sichere jetzt den NEUEN geheimen Schlüssel — dein Seed kann ihn nicht wiederherstellen."
|
||||
copy_new_nsec: "Neues nsec-Backup kopieren"
|
||||
done: "Fertig"
|
||||
rotation_failed: "Wechsel fehlgeschlagen"
|
||||
close: "Schließen"
|
||||
import_identity_title: "Identität importieren"
|
||||
import_blurb: "Ersetzt die nostr-Identität dieser Wallet — wähle eine GOBLIN-.backup-Datei oder füge einen nsec ein. Eine Sicherung stellt auch Benutzername und Verlauf wieder her. Sichere zuerst den aktuellen Schlüssel, falls du ihn noch brauchst."
|
||||
import_nsec_hint: "nsec1… oder eingefügte Sicherung"
|
||||
backup_password_hint: "Backup-Passwort (nur wenn anderswo exportiert)"
|
||||
import_btn: "Importieren"
|
||||
importing: "Importiere…"
|
||||
identity_replaced: "Identität ersetzt"
|
||||
now_using: "Jetzt aktiv: %{npub}"
|
||||
import_failed: "Import fehlgeschlagen"
|
||||
name_authority: "Namensautorität"
|
||||
name_authority_title: "Namensautorität ändern"
|
||||
name_authority_blurb: "Der Server, der Namen registriert und verifiziert. Auf eine andere Instanz zeigen, um dort gehostete Namen zu nutzen und zu bezahlen."
|
||||
name_authority_invalid: "Vollständige URL eingeben (https://…)."
|
||||
reset: "Zurücksetzen"
|
||||
save: "Speichern"
|
||||
backup_file: "In Datei sichern"
|
||||
choose_backup_file: "Eine .backup-Datei wählen"
|
||||
backup_read_failed: "Datei konnte nicht gelesen werden."
|
||||
backup_saved: "Sicherung gespeichert"
|
||||
backup_saved_sub: "Bewahre die .backup-Datei sicher auf — wer sie UND dein Passwort hat, kann deine Identität wiederherstellen."
|
||||
backup_file_title: "Identität sichern"
|
||||
backup_file_blurb: "Erstellt eine verschlüsselte .backup-Datei mit Benutzername und Schlüssel. Gib dein Wallet-Passwort ein, um sie zu versiegeln."
|
||||
backup_write_failed: "Datei konnte nicht gespeichert werden."
|
||||
create_backup: "Sicherung erstellen"
|
||||
registered: "%{name} registriert"
|
||||
released_msg: "Freigegeben — der Name ist frei"
|
||||
release_confirm: "%{name} freigeben?"
|
||||
release_blurb: "Sobald er frei ist, ist er verfügbar — jeder kann ihn beanspruchen, auch dein nächster rotierter Schlüssel. Du kannst 10 Minuten lang keinen anderen Benutzernamen registrieren."
|
||||
releasing: "Gebe frei…"
|
||||
keep_it: "Behalten"
|
||||
release_it: "Freigeben"
|
||||
username: "Benutzername"
|
||||
username_note: "Wird als you angezeigt. Öffentlich auf goblin.st. Zahlungen bleiben verschlüsselt."
|
||||
release_username: "Benutzername freigeben"
|
||||
pick_username: "Benutzernamen wählen — optional"
|
||||
working: "Arbeite…"
|
||||
claim: "Sichern"
|
||||
err_just_taken: "Dieser Benutzername wurde gerade vergeben"
|
||||
err_cooldown: "Du hast kürzlich einen Benutzernamen freigegeben — du kannst innerhalb von 10 Minuten einen neuen registrieren."
|
||||
err_unreachable: "goblin.st nicht erreichbar — Verbindungsproblem. Versuche es erneut."
|
||||
err_release: "Freigabe fehlgeschlagen: %{err}"
|
||||
avail_available: "Verfügbar!"
|
||||
avail_taken: "Vergeben"
|
||||
avail_reserved: "Reserviert"
|
||||
avail_invalid: "Namen haben 3–20 Zeichen: a–z, 0–9, _ oder -"
|
||||
avail_quarantined: "Nicht verfügbar"
|
||||
avail_unknown: "Prüfung fehlgeschlagen — Verbindungsproblem. Versuche es erneut."
|
||||
advanced:
|
||||
title: "Erweitert"
|
||||
intro: "Wallet-Werkzeuge auf niedriger Ebene von GRIM. Normalerweise brauchst du diese nicht."
|
||||
own_node_desc: "Synchronisiere einen vollständigen Grin-Node auf diesem Gerät, statt einem öffentlichen zu vertrauen."
|
||||
own_node_active: "Eigener Node läuft"
|
||||
repair: "Wallet reparieren"
|
||||
repair_desc: "Die Kette neu scannen und fehlende Outputs wiederherstellen. Das kann dauern."
|
||||
repair_unavailable: "Benötigt zuerst eine synchronisierte Node-Verbindung."
|
||||
repairing: "Repariere… %{pct}%"
|
||||
restore: "Wallet wiederherstellen"
|
||||
restore_desc: "Lokale Daten löschen und aus deinem Seed neu aufbauen. Nutze das, wenn eine Reparatur nicht half — danach öffnest du die Wallet erneut."
|
||||
restore_confirm: "Zum Wiederherstellen erneut tippen"
|
||||
show_phrase: "Wiederherstellungsphrase"
|
||||
phrase_desc: "Deine 24 grin-Seed-Wörter — der einzige Weg, Guthaben wiederherzustellen. Halte sie offline und privat."
|
||||
reveal: "Phrase anzeigen"
|
||||
hide: "Verbergen"
|
||||
password: "Wallet-Passwort"
|
||||
wrong_password: "Falsches Passwort."
|
||||
delete: "Wallet löschen"
|
||||
delete_desc: "Diese Wallet dauerhaft von diesem Gerät entfernen. Ohne deinen Seed sind Guthaben nicht wiederherstellbar."
|
||||
delete_confirm: "Zum Löschen erneut tippen"
|
||||
manage_node: "Node-Verbindung verwalten"
|
||||
repair_confirm: "Ja, jetzt reparieren"
|
||||
repair_confirm_note: "Die Reparatur scannt die Chain neu und kann einige Minuten dauern."
|
||||
restore_confirm_note: "Dies löscht lokale Daten und baut sie aus deinem Seed neu auf — das kann einige Minuten dauern."
|
||||
privacy:
|
||||
title: "Netzwerk-Privatsphäre"
|
||||
intro: "Goblin sendet seinen privaten Datenverkehr durch das Nym mixnet — ein Netzwerk mit fünf Sprüngen, das verbirgt, wer mit wem kommuniziert, sodass ein Relay eine Zahlung nicht zu dir zurückverfolgen kann."
|
||||
payments: "Zahlungen"
|
||||
payments_blurb: "Jede nostr-Nachricht, die einen slatepack trägt."
|
||||
usernames: "usernames"
|
||||
usernames_blurb: "NIP-05-Namensabfragen zu und von goblin.st."
|
||||
price_avatars: "Preis"
|
||||
price_avatars_blurb: "Der Live-Wechselkurs neben den Beträgen."
|
||||
over_mixnet: "Über das mixnet"
|
||||
direct_connection: "Direkte Verbindung"
|
||||
grin_node: "Grin-Node"
|
||||
grin_node_blurb: "Block-Synchronisierung und Übertragung deiner Transaktion ins Netzwerk. Dies sind öffentliche Chain-Daten, für alle gleich, und nicht mit deiner Identität verknüpft."
|
||||
pairing:
|
||||
title: "Kopplung"
|
||||
intro: "Womit dein Guthaben und deine Beträge verglichen werden."
|
||||
pair_with: "Koppeln mit"
|
||||
rates_note: "Kurse werden über das Nym mixnet abgerufen, nur solange eine Kopplung aktiv ist — aus bedeutet, dass keine Kursanfrage dein Gerät verlässt."
|
||||
relays:
|
||||
title: "Relays"
|
||||
intro: "Zahlungsnachrichten werden an jedes Relay unten gespiegelt; ein erreichbares Relay genügt zum Empfangen."
|
||||
your_relays: "Deine Relays"
|
||||
add_relay: "Relay hinzufügen"
|
||||
add_relay_btn: "Relay hinzufügen"
|
||||
save_reconnect: "Speichern & neu verbinden"
|
||||
none: "keine"
|
||||
count: "%{n} Relays"
|
||||
node:
|
||||
title: "Node"
|
||||
connection: "Verbindung"
|
||||
integrated: "Integrierter Node"
|
||||
applies_after: "Wird wirksam, nachdem das Wallet gesperrt und wieder entsperrt wurde."
|
||||
add_external: "Externen Node hinzufügen"
|
||||
api_secret_hint: "API-Secret (optional)"
|
||||
add_node: "Node hinzufügen"
|
||||
integrated_host: "integrierter Node"
|
||||
summary_syncing: "%{conn} · synchronisiere"
|
||||
summary_block: "Block %{height} · %{conn}"
|
||||
nips:
|
||||
title: "nostr & NIPs"
|
||||
intro1: "Goblin spricht nostr — ein offenes Protokoll signierter Nachrichten, die über einfache Relay-Server weitergereicht werden. Dein Wallet trägt seine eigene nostr-Identität: einen eigenständigen Zufallsschlüssel, bewusst unabhängig von deinem Guthaben und Seed gehalten. Jede Zahlung reist als Ende-zu-Ende-verschlüsselte Direktnachricht zwischen Identitäten, mit dem slatepack im Inneren."
|
||||
intro2: "goblin.st ist Goblins Namensdienst: Das Sichern eines Benutzernamens veröffentlicht dort eine Name → Schlüssel-Zuordnung (NIP-05), sodass Leute you statt eines langen npub bezahlen können. Der Benutzername ist öffentlich; Zahlungsinhalte sind es nie. NIPs sind die Bausteine des Protokolls — tippe auf einen, um die Spezifikation zu lesen."
|
||||
n05_title: "Namen"
|
||||
n05_blurb: "Ordnet username@goblin.st deinem Schlüssel zu, sodass Handles wie Adressen funktionieren."
|
||||
n17_title: "Private Nachrichten"
|
||||
n17_blurb: "Die verschlüsselte DM-Hülle, in der jede Zahlung reist."
|
||||
n44_title: "Verschlüsselung"
|
||||
n44_blurb: "Die authentifizierte Chiffre, die in diesen Nachrichten verwendet wird."
|
||||
n49_title: "Schlüsselverschlüsselung"
|
||||
n49_blurb: "Wie der geheime Schlüssel im Ruhezustand gespeichert wird, gesperrt durch dein Passwort."
|
||||
n59_title: "Gift Wrap"
|
||||
n59_blurb: "Verpackt Nachrichten, sodass Relays nicht sehen können, wer mit wem kommuniziert."
|
||||
n98_title: "HTTP-Auth"
|
||||
n98_blurb: "Signiert die Benutzernamen-Registrierungsanfrage an goblin.st."
|
||||
onboarding:
|
||||
intro:
|
||||
private_money_head: "Privates Geld"
|
||||
private_money_body: "Goblin ist ein Wallet für grin — digitales Bargeld ohne Beträge oder Adressen auf seiner Chain."
|
||||
send_like_message_head: "Senden wie eine Nachricht"
|
||||
send_like_message_body: "Zahle an einen username oder npub und es kommt als Ende-zu-Ende-verschlüsselte Nachricht über nostr und das Nym mixnet an — niemand dazwischen sieht den Betrag oder die Beteiligten."
|
||||
yours_alone_head: "Nur deins"
|
||||
yours_alone_body: "Schlüssel, Namen und Verlauf bleiben auf diesem Gerät. Basiert auf dem GRIM-Wallet."
|
||||
get_started: "Loslegen"
|
||||
footnote: "Dauert etwa eine Minute. Du kannst später alles ändern."
|
||||
node:
|
||||
kicker: "SCHRITT 1 VON 3 · NETZWERK"
|
||||
title: "Wie soll Goblin\ndie Chain beobachten?"
|
||||
own_title: "Eigenen Node betreiben"
|
||||
own_badge: "Privat"
|
||||
own_body: "Vertraut niemandem — dein Wallet prüft die Chain selbst. Synchronisiert im Hintergrund, während du die Einrichtung beendest."
|
||||
connect_title: "Mit einem Node verbinden"
|
||||
connect_badge: "Sofort"
|
||||
connect_body: "Kein Warten auf Sync. Der gewählte Node kann die Abfragen deines Wallets sehen."
|
||||
changeable: "Jederzeit änderbar unter Einstellungen → Node."
|
||||
continue: "Weiter"
|
||||
url_invalid: "Node-URL muss mit http:// oder https:// beginnen"
|
||||
wallet:
|
||||
kicker: "SCHRITT 2 VON 3 · WALLET"
|
||||
title: "Richte dein Wallet ein"
|
||||
create_new: "Neu erstellen"
|
||||
restore_from_seed: "Aus Seed wiederherstellen"
|
||||
name_hint: "Wallet-Name"
|
||||
password_hint: "Passwort"
|
||||
repeat_password_hint: "Passwort wiederholen"
|
||||
restore_hint: "Halte deine Seed-Wörter bereit — du gibst sie als Nächstes ein."
|
||||
create_hint: "Als Nächstes erhältst du 24 Seed-Wörter zum Aufschreiben. Sie sind das Geld — wer sie hat, hält dein Guthaben."
|
||||
continue: "Weiter"
|
||||
passwords_no_match: "Passwörter stimmen nicht überein"
|
||||
words:
|
||||
kicker: "SCHRITT 2 VON 3 · WALLET"
|
||||
title_restore: "Gib deine Seed-Wörter ein"
|
||||
title_create: "Schreibe diese Wörter auf"
|
||||
write_down_hint: "Auf Papier, in Reihenfolge. Wer diese Wörter hat, kann dein Guthaben nehmen; ohne sie bedeutet ein verlorenes Gerät verlorenes Guthaben."
|
||||
paste: "Einfügen"
|
||||
scan_qr: "QR scannen"
|
||||
copy_clipboard: "In Zwischenablage kopieren (vermeiden)"
|
||||
restore_wallet: "Wallet wiederherstellen"
|
||||
wrote_them_down: "Ich habe sie aufgeschrieben"
|
||||
fill_every_word: "Fülle jedes Wort aus — tippe ein Wort an, um es zu bearbeiten, oder füge die Phrase ein."
|
||||
confirm:
|
||||
kicker: "SCHRITT 2 VON 3 · WALLET"
|
||||
title: "Jetzt beweise es"
|
||||
enter_hint: "Gib die soeben aufgeschriebenen Wörter ein. Tippe ein Wort an, um es zu tippen."
|
||||
paste: "Einfügen"
|
||||
create_wallet: "Wallet erstellen"
|
||||
keep_going: "Weiter so — jedes Wort, in Reihenfolge."
|
||||
identity:
|
||||
kicker: "SCHRITT 3 VON 3 · IDENTITÄT"
|
||||
title: "Deine Zahlungsidentität"
|
||||
key_being_made: "Schlüssel wird erstellt…"
|
||||
connected_nym: "über Nym verbunden"
|
||||
connecting_nym: "verbinde über Nym…"
|
||||
fresh_key_blurb: "Ein Zahlungsschlüssel, der nicht Teil deines Seeds ist — jederzeit rotierbar, ohne deine Mittel zu berühren."
|
||||
clean_slate_blurb: "Lust auf einen Neuanfang? Tausche jederzeit einen brandneuen Schlüssel ein — das neue Du ist nicht mit dem alten verknüpft. Gleiches Wallet, frisches Gesicht."
|
||||
pick_username: "Benutzernamen wählen — optional"
|
||||
username_blurb: "Freunde zahlen an deinen Namen statt an einen langen Schlüssel. Optional — jederzeit beanspruchbar."
|
||||
username_field_hint: "deinname"
|
||||
working: "Arbeite…"
|
||||
claim_username: "Benutzernamen sichern"
|
||||
available_when_connected: "Verfügbar, sobald das mixnet verbindet — oder überspringen und später sichern."
|
||||
youre: "Du bist %{name}"
|
||||
claimed_title: "%{name} gehört dir"
|
||||
claimed_blurb: "Freunde können dich jetzt per Namen bezahlen. Alles bereit — öffne dein Wallet."
|
||||
open_wallet: "Mein Wallet öffnen"
|
||||
skip_for_now: "Vorerst überspringen"
|
||||
import_existing: "Schon eine Goblin-Identität? Importieren"
|
||||
import_title: "Identität importieren"
|
||||
import_blurb: "Füge deinen nsec ein oder wähle eine .backup-Datei, um deinen vorhandenen Schlüssel und Benutzernamen zu behalten statt diesen neuen."
|
||||
errors:
|
||||
cant_open: "Wallet konnte nicht geöffnet werden: %{err}"
|
||||
cant_create: "Wallet konnte nicht erstellt werden: %{err}"
|
||||
send:
|
||||
scan_to_request: "Zum Anfordern scannen"
|
||||
scan_to_pay: "Zum Zahlen scannen"
|
||||
tab_scan: "Scannen"
|
||||
tab_my_code: "Mein Code"
|
||||
request_from: "Anfordern von"
|
||||
send_to: "Senden an"
|
||||
search_hint: "handle, npub oder Name"
|
||||
suggested: "%{icon} Vorgeschlagen"
|
||||
no_contacts: "Noch keine Kontakte. Finde jemanden über seinen handle."
|
||||
no_profile: "kein Profil"
|
||||
tag_contact: "Kontakt"
|
||||
tag_on_nostr: "auf nostr"
|
||||
searching_nostr: "Durchsuche nostr…"
|
||||
unverified_title: "Unverifizierten Schlüssel bezahlen?"
|
||||
unverified_body: "Für diesen Schlüssel ist kein nostr-Profil veröffentlicht — er könnte brandneu, anonym oder vertippt sein. Prüfe genau, ob es der richtige ist, bevor du sendest."
|
||||
keep_looking: "Weitersuchen"
|
||||
pay_anyway: "Trotzdem zahlen"
|
||||
scan_not_recipient: "Dieser QR ist kein goblin-Empfänger — erwartet wurde ein npub oder handle"
|
||||
scan_prompt: "Halte einen goblin-Code ins Bild, um zu aktivieren"
|
||||
scan_to_pay_me: "Scannen, um mich zu bezahlen"
|
||||
share_btn: "%{icon} Teilen"
|
||||
share_message: "Bezahl mich auf Goblin — %{handle}\n%{link}\nnpub: %{npub}"
|
||||
none_found: "Niemand gefunden für %{label}"
|
||||
enter_recipient: "Gib einen handle, npub oder Namen ein"
|
||||
amount_title: "Betrag"
|
||||
to_name: "An %{name}"
|
||||
not_enough: "Du hast nicht genug grin"
|
||||
max: "Max"
|
||||
note_label: "Notiz"
|
||||
note_hint: "Notiz hinzufügen…"
|
||||
add_note: "Notiz hinzufügen"
|
||||
edit_note: "Notiz bearbeiten"
|
||||
note_cancel: "Abbrechen"
|
||||
note_save: "Speichern"
|
||||
review_btn: "Prüfen"
|
||||
confirm_request: "Anfrage bestätigen"
|
||||
review_title: "Prüfen"
|
||||
requesting_from: "Fordere an von %{name}"
|
||||
youre_sending: "Du sendest %{name}"
|
||||
row_from: "Von"
|
||||
row_to: "An"
|
||||
row_note: "Notiz"
|
||||
row_they_pay: "Sie zahlen"
|
||||
row_they_pay_val: "Nur wenn sie zustimmen"
|
||||
row_delivery: "Zustellung"
|
||||
row_delivery_val: "NIP-44-verschlüsselt, über Nym"
|
||||
row_network_fee: "Netzwerkgebühr"
|
||||
row_network_fee_val: "Von deinem Guthaben abgezogen"
|
||||
row_privacy: "Privatsphäre"
|
||||
row_privacy_val: "Mimblewimble + Nym"
|
||||
send_request_btn: "Anfrage senden"
|
||||
request_approve_hint: "Sie erhalten eine Anfrage zum Zustimmen"
|
||||
hold_to_send: "Zum Senden halten"
|
||||
lower_amount: "Zurückgehen und Betrag verringern"
|
||||
hold_confirm_hint: "Gedrückt halten zum Bestätigen"
|
||||
requesting: "Fordere an…"
|
||||
sending: "Sende…"
|
||||
they: "Sie"
|
||||
request_blocked: "%{who} nimmt keine Anfragen an. Bitte sie, dir stattdessen grin zu senden."
|
||||
failed_request_title: "Anfrage fehlgeschlagen"
|
||||
failed_send_title: "Senden fehlgeschlagen"
|
||||
failed_request_body: "Die Anfrage konnte nicht zugestellt werden. Bitte sie, dir stattdessen grin zu senden."
|
||||
failed_send_body: "Die Zahlung wurde nicht zugestellt. Dein grin ist sicher — versuche es erneut."
|
||||
try_again_btn: "Erneut versuchen"
|
||||
close_btn: "Schließen"
|
||||
success:
|
||||
requested: "Angefordert"
|
||||
sent: "Gesendet"
|
||||
from: "von"
|
||||
to: "an"
|
||||
subtitle: "%{dir} %{who} · gerade eben"
|
||||
done_btn: "Fertig"
|
||||
receipt_btn: "Beleg"
|
||||
@@ -0,0 +1,808 @@
|
||||
lang_name: English
|
||||
copy: Copy
|
||||
paste: Paste
|
||||
continue: Continue
|
||||
complete: Complete
|
||||
error: Error
|
||||
retry: Retry
|
||||
close: Close
|
||||
change: Change
|
||||
show: Show
|
||||
delete: Delete
|
||||
clear: Clear
|
||||
create: Create
|
||||
id: Identifier
|
||||
kernel: Kernel
|
||||
settings: Settings
|
||||
language: Language
|
||||
scan: Scan
|
||||
qr_code: QR code
|
||||
scan_qr: Scan QR code
|
||||
repeat: Repeat
|
||||
scan_result: Scan result
|
||||
back: Back
|
||||
share: Share
|
||||
theme: 'Theme:'
|
||||
dark: Dark
|
||||
light: Light
|
||||
file: File
|
||||
choose_file: Choose file
|
||||
choose_folder: Choose folder
|
||||
crash_report: Crash report
|
||||
crash_report_warning: Application closed unexpectedly last time, you can share crash report with developers.
|
||||
confirmation: Confirmation
|
||||
enter_url: Enter URL
|
||||
max_short: MAX
|
||||
files_location: Files location
|
||||
moving_files: Moving files
|
||||
wrong_path_error: Wrong path specified
|
||||
check_updates: Check for updates at startup
|
||||
update_available: Update is available!
|
||||
changelog: 'Changelog:'
|
||||
wallets:
|
||||
await_conf_amount: Awaiting confirmation
|
||||
await_fin_amount: Awaiting finalization
|
||||
locked_amount: Locked
|
||||
txs_empty: 'To receive funds manually or over transport use %{message} or %{transport} buttons at the bottom of the screen, to change wallet settings press %{settings} button.'
|
||||
title: Goblin
|
||||
create_desc: Create or import existing wallet from saved recovery phrase.
|
||||
add: Add wallet
|
||||
name: 'Name:'
|
||||
pass: 'Password:'
|
||||
pass_empty: Enter the wallet password
|
||||
current_pass: 'Current password:'
|
||||
new_pass: 'New password:'
|
||||
min_tx_conf_count: 'Minimum amount of confirmations for transactions:'
|
||||
recover: Restore
|
||||
recovery_phrase: Recovery phrase
|
||||
words_count: 'Words count:'
|
||||
enter_word: 'Enter word #%{number}:'
|
||||
not_valid_word: Entered word is not valid
|
||||
not_valid_phrase: Entered phrase is not valid
|
||||
create_phrase_desc: Safely write down and save your recovery phrase.
|
||||
restore_phrase_desc: Enter words from your saved recovery phrase.
|
||||
setup_conn_desc: Choose how your wallet connects to the network.
|
||||
conn_method: Connection method
|
||||
ext_conn: 'External connections:'
|
||||
add_node: Add node
|
||||
node_url: 'Node URL:'
|
||||
node_secret: 'API Secret (optional):'
|
||||
invalid_url: Entered URL is invalid
|
||||
open: Open the wallet
|
||||
wrong_pass: Entered password is wrong
|
||||
locked: Locked
|
||||
unlocked: Unlocked
|
||||
enable_node: 'Enable integrated node to use the wallet or change connection settings by selecting %{settings} at the bottom of the screen.'
|
||||
node_loading: 'Wallet will be loaded after integrated node synchronization, you can change connection by selecting %{settings} at the bottom of the screen.'
|
||||
loading: Loading
|
||||
closing: Closing
|
||||
checking: Checking
|
||||
default_wallet: Default wallet
|
||||
new_account_desc: 'Enter name of new account:'
|
||||
wallet_loading: Loading wallet
|
||||
wallet_closing: Closing wallet
|
||||
wallet_checking: Checking wallet
|
||||
tx_loading: Loading transactions
|
||||
default_account: Default account
|
||||
accounts: Accounts
|
||||
tx_sent: Sent
|
||||
tx_received: Received
|
||||
tx_sending: Sending
|
||||
tx_receiving: Receiving
|
||||
tx_confirming: Awaiting confirmation
|
||||
tx_canceled: Canceled
|
||||
tx_cancelling: Cancelling
|
||||
tx_finalizing: Finalizing
|
||||
tx_posting: Posting
|
||||
tx_confirmed: Confirmed
|
||||
txs: Transactions
|
||||
tx: Transaction
|
||||
messages: Messages
|
||||
transport: Transport
|
||||
input_slatepack_desc: 'Enter received Slatepack message to create response or finalize request:'
|
||||
parse_slatepack_err: 'An error occurred during reading of the message, check input:'
|
||||
pay_balance_error: 'Account balance is insufficient to pay %{amount} ツ and network fee.'
|
||||
parse_i1_slatepack_desc: 'To pay %{amount} ツ send this message to the receiver:'
|
||||
parse_i2_slatepack_desc: 'Finalize transaction to receive %{amount} ツ:'
|
||||
parse_i3_slatepack_desc: 'Post transaction to finalize receiving of %{amount} ツ:'
|
||||
parse_s1_slatepack_desc: 'To receive %{amount} ツ send this message to the sender:'
|
||||
parse_s2_slatepack_desc: 'Finalize transaction to send %{amount} ツ:'
|
||||
parse_s3_slatepack_desc: 'Post transaction to finalize sending of %{amount} ツ:'
|
||||
resp_slatepack_err: 'An error occurred during creation of the response, check input data or try again:'
|
||||
resp_exists_err: Such transaction already exists.
|
||||
resp_canceled_err: Such transaction was already canceled.
|
||||
create_request_desc: 'Create request to send or receive the funds:'
|
||||
send_request_desc: 'You have created a request to send %{amount} ツ. Send this message to the receiver:'
|
||||
send_slatepack_err: An error occurred during creation of request to send funds, check input data or try again.
|
||||
invoice_desc: 'You have created request to receive %{amount} ツ. Send this message to the sender:'
|
||||
invoice_slatepack_err: An error occurred during issuing of the invoice, check input data or try again.
|
||||
finalize_slatepack_err: 'An error occurred during finalization, check input data or try again:'
|
||||
finalize: Finalize
|
||||
use_dandelion: Use Dandelion
|
||||
enter_amount_send: 'You have %{amount} ツ. Enter amount to send:'
|
||||
enter_amount_receive: 'Enter amount to receive:'
|
||||
recovery: Recovery
|
||||
repair_wallet: Repair wallet
|
||||
repair_desc: Check a wallet, repairing and restoring missing outputs if required. This operation will take time.
|
||||
repair_unavailable: You need an active connection to the node and completed wallet synchronization.
|
||||
delete: Delete wallet
|
||||
delete_conf: Are you sure you want to delete the wallet?
|
||||
delete_desc: Make sure you have saved your recovery phrase to access funds later.
|
||||
wallet_loading_err: 'An error occurred during synchronization of the wallet, you can retry or change connection settings by selecting %{settings} at the bottom of the screen.'
|
||||
wallet: Wallet
|
||||
send: Send
|
||||
receive: Receive
|
||||
settings: Wallet settings
|
||||
tx_send_cancel_conf: 'Are you sure you want to cancel sending of %{amount} ツ?'
|
||||
tx_receive_cancel_conf: 'Are you sure you want to cancel receiving of %{amount} ツ?'
|
||||
rec_phrase_not_found: Recovery phrase not found.
|
||||
restore_wallet_desc: Restore wallet by deleting all files if usual repair not helped, you will need to re-open your wallet.
|
||||
fee_base_desc: 'Fee (base value%{value}):'
|
||||
payment_proof: Payment proof
|
||||
payment_proof_desc: 'Enter received payment proof to verify transaction:'
|
||||
payment_proof_valid: 'Entered payment proof is valid:'
|
||||
payment_proof_error: 'Entered payment proof is not valid:'
|
||||
tx_delete_confirmation: Are you sure you want to delete the transaction from history?
|
||||
transport:
|
||||
desc: 'Use transport to receive or send messages synchronously:'
|
||||
tor_network: Tor network
|
||||
connected: Connected
|
||||
connecting: Connecting
|
||||
disconnecting: Disconnecting
|
||||
conn_error: Connection error
|
||||
disconnected: Disconnected
|
||||
receiver_address: 'Address of the receiver:'
|
||||
incorrect_addr_err: 'Entered address is incorrect:'
|
||||
tor_send_error: An error occurred during sending over Tor, make sure receiver is online, transaction was canceled.
|
||||
tor_autorun_desc: Whether to launch Tor service on wallet opening to receive transactions synchronously.
|
||||
tor_sending: Sending over Tor
|
||||
tor_settings: Tor Settings
|
||||
bridges: Bridges
|
||||
bridges_desc: Setup bridges to bypass Tor network censorship if usual connection is not working.
|
||||
bin_file: 'Binary file:'
|
||||
conn_line: 'Connection line:'
|
||||
bridges_disabled: Bridges disabled
|
||||
bridge_name: 'Bridge %{b}'
|
||||
network:
|
||||
self: Network
|
||||
type: 'Network type:'
|
||||
mainnet: Main
|
||||
testnet: Test
|
||||
connections: Connections
|
||||
node: Integrated node
|
||||
metrics: Metrics
|
||||
mining: Mining
|
||||
settings: Node settings
|
||||
enable_node: Enable node
|
||||
autorun: Autorun
|
||||
disabled_server: 'Enable integrated node or add another connection method by pressing %{dots} in the top-left corner of the screen.'
|
||||
no_ips: There are no available IP addresses on your system, server cannot be started, check your network connectivity.
|
||||
available: Available
|
||||
not_available: Not available
|
||||
availability_check: Availability check
|
||||
android_warning: Attention to Android users. To synchronize integrated node successfully, you must allow access to notifications and remove battery usage restrictions for the Goblin application at system settings of your phone. This is necessary operation for correct work of application in the background.
|
||||
sync_status:
|
||||
node_restarting: Node is restarting
|
||||
node_down: Node is down
|
||||
initial: Node is starting
|
||||
no_sync: Node is running
|
||||
awaiting_peers: Waiting for peers
|
||||
header_sync: Downloading headers
|
||||
header_sync_percent: 'Downloading headers: %{percent}%'
|
||||
tx_hashset_pibd: Downloading state (PIBD)
|
||||
tx_hashset_pibd_percent: 'Downloading state (PIBD): %{percent}%'
|
||||
tx_hashset_download: Downloading state
|
||||
tx_hashset_download_percent: 'Downloading state: %{percent}%'
|
||||
tx_hashset_setup_history: 'Preparing state (history): %{percent}%'
|
||||
tx_hashset_setup_position: 'Preparing state (position): %{percent}%'
|
||||
tx_hashset_setup: Preparing state
|
||||
tx_hashset_range_proofs_validation: 'Validating state (range proofs): %{percent}%'
|
||||
tx_hashset_kernels_validation: 'Validating state (kernels): %{percent}%'
|
||||
tx_hashset_save: Finalizing chain state
|
||||
body_sync: Downloading blocks
|
||||
body_sync_percent: 'Downloading blocks: %{percent}%'
|
||||
shutdown: Node is shutting down
|
||||
network_node:
|
||||
header: Header
|
||||
block: Block
|
||||
hash: Hash
|
||||
height: Height
|
||||
difficulty: Difficulty
|
||||
time: Time
|
||||
main_pool: Main pool
|
||||
stem_pool: Stem pool
|
||||
data: Data
|
||||
size: Size (GB)
|
||||
peers: Peers
|
||||
error_clean: Node data got corrupted, resync required.
|
||||
resync: Resync
|
||||
error_p2p_api: 'An error occurred during %{p2p_api} server initialization, check %{p2p_api} settings by selecting %{settings} at the bottom of the screen.'
|
||||
error_config: 'An error occurred during configuration initialization, check settings by selecting %{settings} at the bottom of the screen.'
|
||||
error_unknown: 'An error occurred during initialization, check integrated node settings by selecting %{settings} at the bottom of the screen or resync.'
|
||||
network_metrics:
|
||||
loading: Metrics will be available after the synchronization
|
||||
emission: Emission
|
||||
inflation: Inflation
|
||||
supply: Supply
|
||||
block_time: Block time
|
||||
reward: Reward
|
||||
difficulty_window: 'Difficulty window %{size}'
|
||||
network_mining:
|
||||
loading: Mining will be available after the synchronization
|
||||
info: 'Mining server is enabled, you can change its settings by selecting %{settings} at the bottom of the screen. Data is updating when devices are connected.'
|
||||
restart_server_required: Server restart is required to apply changes.
|
||||
rewards_wallet: Wallet for rewards
|
||||
server: Stratum server
|
||||
address: Address
|
||||
miners: Miners
|
||||
devices: Devices
|
||||
blocks_found: Blocks found
|
||||
hashrate: 'Hashrate (C%{bits})'
|
||||
connected: Connected
|
||||
disconnected: Disconnected
|
||||
network_settings:
|
||||
change_value: Change value
|
||||
stratum_ip: 'Stratum IP address:'
|
||||
stratum_port: 'Stratum port:'
|
||||
port_unavailable: Specified port is unavailable
|
||||
restart_node_required: Node restart is required to apply changes.
|
||||
choose_wallet: Choose wallet
|
||||
stratum_wallet_warning: Wallet must be opened to receive rewards.
|
||||
enable: Enable
|
||||
disable: Disable
|
||||
restart: Restart
|
||||
server: Server
|
||||
api_ip: 'API IP address:'
|
||||
api_port: 'API port:'
|
||||
api_secret: 'Rest API and V2 Owner API token:'
|
||||
foreign_api_secret: 'Foreign API token:'
|
||||
disabled: Disabled
|
||||
enabled: Enabled
|
||||
ftl: 'The Future Time Limit (FTL):'
|
||||
ftl_description: Limit on how far into the future, relative to a node's local time in seconds, the timestamp on a new block can be, in order for the block to be accepted.
|
||||
not_valid_value: Entered value is not valid
|
||||
full_validation: Full validation
|
||||
full_validation_description: Whether to run a full chain validation when processing each block (except during synchronization).
|
||||
archive_mode: Archive mode
|
||||
archive_mode_desc: Run the node in full archive mode (more disk space and time will be required for synchronization).
|
||||
attempt_time: 'Mining attempt time (in seconds):'
|
||||
attempt_time_desc: The amount of time to attempt to mine on a particular header before stopping and re-collecting transactions from the pool
|
||||
min_share_diff: 'The minimum acceptable share difficulty:'
|
||||
reset_settings_desc: Reset node settings to default values
|
||||
reset_settings: Reset settings
|
||||
reset: Reset
|
||||
tx_pool: Transaction pool
|
||||
pool_fee: 'Base fee that is accepted into the pool:'
|
||||
reorg_period: 'Reorg cache retention period (in minutes):'
|
||||
max_tx_pool: 'Maximum number of transactions in the pool:'
|
||||
max_tx_stempool: 'Maximum number of transactions in the stem-pool:'
|
||||
max_tx_weight: 'Maximum total weight of transactions that can get selected to build a block:'
|
||||
epoch_duration: 'Epoch duration (in seconds):'
|
||||
embargo_timer: 'Embargo timer (in seconds):'
|
||||
aggregation_period: 'Aggregation period (in seconds):'
|
||||
stem_probability: 'Stem phase probability:'
|
||||
stem_txs: Stem transactions
|
||||
p2p_server: P2P server
|
||||
p2p_port: 'P2P port:'
|
||||
add_seed: Add DNS Seed
|
||||
seed_address: 'DNS Seed address:'
|
||||
add_peer: Add peer
|
||||
peer_address: 'Peer address:'
|
||||
peer_address_error: 'Enter IP address or DNS name (make sure specified host is available) in correct format, e.g.: 192.168.0.1:1234 or example.com:5678'
|
||||
default: Default
|
||||
allow_list: Allow list
|
||||
allow_list_desc: Connect only to peers in this list.
|
||||
deny_list: Deny list
|
||||
deny_list_desc: Never connect to peers in this list.
|
||||
favourites: Favourites
|
||||
favourites_desc: A list of preferred peers to connect to.
|
||||
ban_window: 'How much time (in seconds) a banned peer should stay banned:'
|
||||
ban_window_desc: The decision to ban is made by node, based on the correctness of the data received from the peer.
|
||||
max_inbound_count: 'Maximum number of inbound peer connections:'
|
||||
max_outbound_count: 'Maximum number of outbound peer connections:'
|
||||
reset_data_desc: Reset the node data. Use it with a caution only if there are problems with synchronization.
|
||||
reset_data: Reset data
|
||||
modal:
|
||||
cancel: Cancel
|
||||
save: Save
|
||||
add: Add
|
||||
modal_exit:
|
||||
description: Are you sure you want to quit the application?
|
||||
exit: Exit
|
||||
app_settings:
|
||||
proxy: Proxy
|
||||
proxy_desc: Whether to use proxy for network requests from the application.
|
||||
keyboard:
|
||||
1: 1
|
||||
2: 2
|
||||
3: 3
|
||||
4: 4
|
||||
5: 5
|
||||
6: 6
|
||||
7: 7
|
||||
8: 8
|
||||
9: 9
|
||||
0: 0
|
||||
01: '-'
|
||||
q: q
|
||||
w: w
|
||||
e: e
|
||||
r: r
|
||||
t: t
|
||||
y: y
|
||||
u: u
|
||||
i: i
|
||||
o: o
|
||||
p: p
|
||||
p1: '"'
|
||||
a: a
|
||||
s: s
|
||||
d: d
|
||||
f: f
|
||||
g: g
|
||||
h: h
|
||||
j: j
|
||||
k: k
|
||||
l: l
|
||||
l1: \
|
||||
l2: ':'
|
||||
z: z
|
||||
x: x
|
||||
c: c
|
||||
v: v
|
||||
b: b
|
||||
n: n
|
||||
m: m
|
||||
m1: ','
|
||||
m2: .
|
||||
m3: /
|
||||
goblin:
|
||||
home:
|
||||
anonymous: "Anonymous"
|
||||
connected_nym: "Connected over Nym"
|
||||
nym_ready: "Nym ready · relays…"
|
||||
connecting_nym: "Connecting to Nym…"
|
||||
cant_reach_node: "Can't reach node"
|
||||
node_synced: "Node synced"
|
||||
syncing: "Syncing…"
|
||||
block: "Block %{height}"
|
||||
waiting_for_chain: "Waiting for chain…"
|
||||
nav_wallet: "Wallet"
|
||||
nav_pay: "Pay"
|
||||
nav_activity: "Activity"
|
||||
nav_receive: "Receive"
|
||||
nav_settings: "Settings"
|
||||
activity: "Activity"
|
||||
empty_title: "No activity yet"
|
||||
empty_sub: "Send or receive grin to get started."
|
||||
recent: "Recent"
|
||||
scan_to_pay: "Scan to pay"
|
||||
type_amount: "Type an amount"
|
||||
request: "Request"
|
||||
pay: "Pay"
|
||||
enter_amount: "Enter an amount to pay or request"
|
||||
activity:
|
||||
canceled: "canceled"
|
||||
pending: "pending"
|
||||
earlier: "Earlier"
|
||||
today: "Today"
|
||||
yesterday: "Yesterday"
|
||||
title: "Activity"
|
||||
requests: "Requests"
|
||||
empty_title: "No activity yet"
|
||||
empty_sub: "Your payments will appear here."
|
||||
pending_header: "Pending"
|
||||
receipt:
|
||||
title: "Receipt"
|
||||
not_found: "Transaction not found"
|
||||
for_note: "For %{note}"
|
||||
details: "Transaction details"
|
||||
canceled: "Canceled"
|
||||
expired: "Expired"
|
||||
funds_returned: "Funds returned"
|
||||
complete: "Complete"
|
||||
payment_received: "Payment received"
|
||||
payment_sent: "Payment sent successfully"
|
||||
pending: "Pending"
|
||||
confs: "%{c}/%{r} confirmations"
|
||||
waiting_to_confirm: "Waiting to confirm"
|
||||
paying: "Paying…"
|
||||
you: "You"
|
||||
to: "To"
|
||||
from: "From"
|
||||
nostr: "nostr"
|
||||
fee_none: "None"
|
||||
network_fee: "Network fee"
|
||||
privacy: "Privacy"
|
||||
privacy_value: "Mimblewimble + Nym"
|
||||
transaction: "Transaction"
|
||||
cancel_request: "Cancel request"
|
||||
cancel_send: "Cancel payment"
|
||||
cancel_send_confirm: "Tap again to cancel — they may still receive it"
|
||||
cancel_send_done: "Payment cancelled — your funds are available again"
|
||||
cancel_send_too_late: "This payment already went through and can't be cancelled"
|
||||
waiting_to_receive: "Waiting for %{name} to receive…"
|
||||
request:
|
||||
title: "%{name} requests"
|
||||
approve: "Approve"
|
||||
decline: "Decline"
|
||||
review_title: "Review request"
|
||||
hold_to_accept: "Hold to accept"
|
||||
hold_accept_hint: "Press and hold to pay this request"
|
||||
receive:
|
||||
title: "Receive"
|
||||
requesting: "Requesting %{amt}%{tsu} — share to get paid"
|
||||
clear_request: "Clear request"
|
||||
share_handle: "Share your handle to get paid"
|
||||
copied: "Copied"
|
||||
copy_nostr_id: "Copy nostr ID"
|
||||
copy_address: "Copy address"
|
||||
copy_npub: "Copy npub"
|
||||
share_message: "Pay me on Goblin (goblin.st) — %{npub}"
|
||||
privacy_note: "Your username is public. Payment contents stay encrypted over the network."
|
||||
profile:
|
||||
title: "Profile"
|
||||
activity: "Activity"
|
||||
no_activity: "No activity with them yet."
|
||||
unblock: "Unblock"
|
||||
block: "Block"
|
||||
blocked_blurb: "Blocked — their payments and requests are dropped."
|
||||
block_blurb: "Blocking drops their incoming payments and requests."
|
||||
settings:
|
||||
title: "Settings"
|
||||
connected_nostr: "Connected to nostr"
|
||||
connecting_relays: "Connecting to relays…"
|
||||
identity: "Identity"
|
||||
copy_npub: "Copy npub (public)"
|
||||
rotate_key: "Rotate nostr key"
|
||||
import_identity: "Import identity (.backup / nsec)"
|
||||
backup_note: "Moving devices? Back up BOTH: your seed phrase (funds) and your identity .backup file (name + key)."
|
||||
wallet: "Wallet"
|
||||
display_unit: "Display unit"
|
||||
relays: "Relays"
|
||||
node: "Node"
|
||||
slatepacks: "Slatepacks"
|
||||
slatepacks_value: "Manual transaction"
|
||||
lock_wallet: "Lock wallet"
|
||||
switch_wallet: "Switch wallet"
|
||||
advanced: "Advanced"
|
||||
privacy: "Privacy"
|
||||
mixnet_routing: "Mixnet routing"
|
||||
messages_lookups: "Messages & lookups"
|
||||
auto_accept: "Auto-accept"
|
||||
pairing: "Pairing"
|
||||
accept_anyone: "Anyone"
|
||||
accept_contacts: "Contacts only"
|
||||
accept_ask: "Always ask"
|
||||
requests: "Requests"
|
||||
incoming_requests: "Incoming requests"
|
||||
incoming_requests_sub: "Let others request money from you"
|
||||
appearance: "Appearance"
|
||||
theme: "Theme"
|
||||
theme_light: "Light"
|
||||
theme_dark: "Dark"
|
||||
theme_yellow: "Yellow"
|
||||
archive: "Archive"
|
||||
export_archive: "Export archive"
|
||||
wipe_history: "Wipe payment history"
|
||||
about: "About"
|
||||
goblin: "Goblin"
|
||||
build: "Build %{build}"
|
||||
network: "Network"
|
||||
network_value: "MW + Nym mixnet + nostr"
|
||||
third_party: "Third party"
|
||||
grim: "GRIM (upstream wallet)"
|
||||
grin_node: "Grin node"
|
||||
sp_intro: "Advanced — exchange raw slatepacks by hand, the way GRIM does. Use this only when you can't pay or get paid through a username."
|
||||
sp_receive_group: "Receive or finalize"
|
||||
sp_receive_blurb: "Paste a slatepack someone gave you. Goblin receives the payment, pays the invoice, or finalizes and posts it."
|
||||
sp_process: "Process slatepack"
|
||||
sp_paste_first: "Paste a slatepack first."
|
||||
sp_reply_ready: "Reply ready — send it back to the sender."
|
||||
sp_finalizing: "Finalizing and posting to the chain…"
|
||||
sp_create_group: "Create a payment"
|
||||
sp_create_blurb: "Make a slatepack to hand to someone. They receive it, send the reply back, and you finalize it above."
|
||||
sp_amount_hint: "Amount in grin"
|
||||
sp_addr_hint: "Recipient address (optional)"
|
||||
sp_create: "Create slatepack"
|
||||
sp_ready: "Slatepack ready — hand it to the recipient."
|
||||
sp_amount_gt_zero: "Enter an amount greater than zero."
|
||||
sp_to_send: "Slatepack to send"
|
||||
sp_copy: "Copy slatepack"
|
||||
rotate_line1: "• You get a brand-new RANDOM key; the old npub stops receiving. There is no derivation chain between them."
|
||||
rotate_line2: "• The new key is NOT recoverable from your seed — back up the new nsec right after rotating."
|
||||
rotate_line3: "• Your username is RELEASED — claim the same or a new name right after (anyone else can grab it too once it's free)."
|
||||
rotate_line4: "• Payments still in flight to the old key WILL be disrupted — wait for pending payments to finish first."
|
||||
rotate_line5: "• Contacts who saved your npub directly must re-find you — share your new npub or re-claimed username."
|
||||
cancel: "Cancel"
|
||||
continue: "Continue"
|
||||
final_confirmation: "Final confirmation"
|
||||
rotate_confirm_blurb: "This cannot be undone from the app. Type RESET and enter your wallet password to rotate."
|
||||
type_reset: "Type RESET"
|
||||
wallet_password: "Wallet password"
|
||||
rotate_key_btn: "Rotate key"
|
||||
rotating_key: "Rotating key…"
|
||||
key_rotated: "Key rotated"
|
||||
new_npub: "New npub: %{npub}"
|
||||
backup_new_key: "Back up the NEW secret key now — your seed cannot recover it."
|
||||
copy_new_nsec: "Copy new nsec backup"
|
||||
done: "Done"
|
||||
rotation_failed: "Rotation failed"
|
||||
close: "Close"
|
||||
import_identity_title: "Import identity"
|
||||
import_blurb: "Replaces this wallet's nostr identity — choose a GOBLIN .backup file, or paste a bare nsec. A backup also restores your username and history. Back up the current key first if you still need it."
|
||||
import_nsec_hint: "nsec1… or pasted backup"
|
||||
backup_password_hint: "Backup password (only if exported elsewhere)"
|
||||
import_btn: "Import"
|
||||
importing: "Importing…"
|
||||
identity_replaced: "Identity replaced"
|
||||
now_using: "Now using: %{npub}"
|
||||
import_failed: "Import failed"
|
||||
name_authority: "Name authority"
|
||||
name_authority_title: "Change name authority"
|
||||
name_authority_blurb: "The server that registers and verifies names. Point it at another instance to use and pay names hosted there."
|
||||
name_authority_invalid: "Enter a full URL (https://…)."
|
||||
reset: "Reset"
|
||||
save: "Save"
|
||||
backup_file: "Back up to a file"
|
||||
choose_backup_file: "Choose a .backup file"
|
||||
backup_read_failed: "Couldn't read that file."
|
||||
backup_saved: "Backup saved"
|
||||
backup_saved_sub: "Keep the .backup file safe — anyone with it AND your password can restore your identity."
|
||||
backup_file_title: "Back up identity"
|
||||
backup_file_blurb: "Creates one encrypted .backup file with your username and key. Enter your wallet password to seal it."
|
||||
backup_write_failed: "Couldn't save the file."
|
||||
create_backup: "Create backup"
|
||||
registered: "Registered %{name}"
|
||||
released_msg: "Released — the name is up for grabs"
|
||||
release_confirm: "Release %{name}?"
|
||||
release_blurb: "It's up for grabs the moment it's free — anyone can claim it, including the next key you rotate to. You won't be able to register another username for 10 minutes."
|
||||
releasing: "Releasing…"
|
||||
keep_it: "Keep it"
|
||||
release_it: "Release it"
|
||||
username: "Username"
|
||||
username_note: "Shown as you. Public on goblin.st. Payments stay encrypted."
|
||||
release_username: "Release username"
|
||||
pick_username: "Pick a username — optional"
|
||||
working: "Working…"
|
||||
claim: "Claim"
|
||||
err_just_taken: "That username was just taken"
|
||||
err_cooldown: "You recently released a username — you can register a new one within 10 minutes."
|
||||
err_unreachable: "Couldn't reach goblin.st — connection hiccup. Try again."
|
||||
err_release: "Couldn't release: %{err}"
|
||||
avail_available: "Available!"
|
||||
avail_taken: "Taken"
|
||||
avail_reserved: "Reserved"
|
||||
avail_invalid: "Names are 3–20 chars: a–z, 0–9, _ or -"
|
||||
avail_quarantined: "Not available"
|
||||
avail_unknown: "Couldn't check — connection hiccup. Try again."
|
||||
advanced:
|
||||
title: "Advanced"
|
||||
intro: "Low-level wallet tools from GRIM. You won't normally need these."
|
||||
own_node_desc: "Sync a full Grin node on this device instead of trusting a public one."
|
||||
own_node_active: "Running your own node"
|
||||
repair: "Repair wallet"
|
||||
repair_desc: "Re-scan the chain and restore any missing outputs. This can take a while."
|
||||
repair_unavailable: "Needs a synced node connection first."
|
||||
repairing: "Repairing… %{pct}%"
|
||||
restore: "Restore wallet"
|
||||
restore_desc: "Delete local data and rebuild from your seed. Use this if a repair didn't help — you'll re-open the wallet after."
|
||||
restore_confirm: "Tap again to restore"
|
||||
show_phrase: "Recovery phrase"
|
||||
phrase_desc: "Your 24 grin seed words — the only way to recover funds. Keep them offline and private."
|
||||
reveal: "Show phrase"
|
||||
hide: "Hide"
|
||||
password: "Wallet password"
|
||||
wrong_password: "Wrong password."
|
||||
delete: "Delete wallet"
|
||||
delete_desc: "Permanently remove this wallet from this device. Without your seed, funds can't be recovered."
|
||||
delete_confirm: "Tap again to delete"
|
||||
manage_node: "Manage node connection"
|
||||
repair_confirm: "Yes, repair now"
|
||||
repair_confirm_note: "Repair re-scans the chain and can take a few minutes."
|
||||
restore_confirm_note: "This erases local data and rebuilds it from your seed — it can take several minutes."
|
||||
privacy:
|
||||
title: "Network privacy"
|
||||
intro: "Goblin sends its private traffic through the Nym mixnet — a five-hop network that hides who is talking to whom, so a relay can't link a payment back to you."
|
||||
payments: "Payments"
|
||||
payments_blurb: "Every nostr message carrying a slatepack."
|
||||
usernames: "usernames"
|
||||
usernames_blurb: "NIP-05 name lookups to and from goblin.st."
|
||||
price_avatars: "Price"
|
||||
price_avatars_blurb: "The live fiat rate shown next to amounts."
|
||||
over_mixnet: "Over the mixnet"
|
||||
direct_connection: "Direct connection"
|
||||
grin_node: "Grin node"
|
||||
grin_node_blurb: "Block sync and broadcasting your transaction to the network. This is public chain data, the same for everyone, and isn't linked to your identity."
|
||||
pairing:
|
||||
title: "Pairing"
|
||||
intro: "What your balance and amounts are shown against."
|
||||
pair_with: "Pair with"
|
||||
rates_note: "Rates fetch over the Nym mixnet, only while a pairing is on — off means no rate request leaves your device."
|
||||
relays:
|
||||
title: "Relays"
|
||||
intro: "Payment messages are mirrored to every relay below; one reachable relay is enough to receive."
|
||||
your_relays: "Your relays"
|
||||
add_relay: "Add relay"
|
||||
add_relay_btn: "Add relay"
|
||||
save_reconnect: "Save & reconnect"
|
||||
none: "none"
|
||||
count: "%{n} relays"
|
||||
node:
|
||||
title: "Node"
|
||||
connection: "Connection"
|
||||
integrated: "Integrated node"
|
||||
applies_after: "Applies after the wallet is locked and unlocked again."
|
||||
add_external: "Add external node"
|
||||
api_secret_hint: "API secret (optional)"
|
||||
add_node: "Add node"
|
||||
integrated_host: "integrated node"
|
||||
summary_syncing: "%{conn} · syncing"
|
||||
summary_block: "Block %{height} · %{conn}"
|
||||
nips:
|
||||
title: "nostr & NIPs"
|
||||
intro1: "Goblin speaks nostr — an open protocol of signed messages passed through simple relay servers. Your wallet carries its own nostr identity: a standalone random key, kept deliberately independent of your funds and seed. Every payment travels as an end-to-end encrypted direct message between identities, with the slatepack riding inside."
|
||||
intro2: "goblin.st is Goblin's name service: claiming a username publishes a name → key mapping there (NIP-05), so people can pay you instead of a long npub. The username is public; payment contents never are. NIPs are the protocol's building blocks — tap one to read the spec."
|
||||
n05_title: "Names"
|
||||
n05_blurb: "Maps username@goblin.st to your key, so handles work like addresses."
|
||||
n17_title: "Private messages"
|
||||
n17_blurb: "The encrypted DM envelope every payment travels in."
|
||||
n44_title: "Encryption"
|
||||
n44_blurb: "The authenticated cipher used inside those messages."
|
||||
n49_title: "Key encryption"
|
||||
n49_blurb: "How the secret key is stored at rest, locked by your password."
|
||||
n59_title: "Gift wrap"
|
||||
n59_blurb: "Wraps messages so relays can't see who is talking to whom."
|
||||
n98_title: "HTTP auth"
|
||||
n98_blurb: "Signs the username registration request to goblin.st."
|
||||
onboarding:
|
||||
intro:
|
||||
private_money_head: "Private money"
|
||||
private_money_body: "Goblin is a wallet for grin — digital cash with no amounts or addresses on its chain."
|
||||
send_like_message_head: "Send like a message"
|
||||
send_like_message_body: "Pay a username or npub and it arrives as an end-to-end encrypted message over nostr and the Nym mixnet — no one in between can see the amount or who's involved."
|
||||
yours_alone_head: "Yours alone"
|
||||
yours_alone_body: "Keys, names and history live on this device. Built on the GRIM wallet."
|
||||
get_started: "Get started"
|
||||
footnote: "Takes about a minute. You can change everything later."
|
||||
node:
|
||||
kicker: "STEP 1 OF 3 · NETWORK"
|
||||
title: "How should Goblin\nwatch the chain?"
|
||||
own_title: "Run my own node"
|
||||
own_badge: "Private"
|
||||
own_body: "Trusts no one — your wallet checks the chain itself. Syncs in the background while you finish setup."
|
||||
connect_title: "Connect to a node"
|
||||
connect_badge: "Instant"
|
||||
connect_body: "No sync wait. The node you pick can see your wallet's queries."
|
||||
changeable: "Changeable any time in Settings → Node."
|
||||
continue: "Continue"
|
||||
url_invalid: "Node URL must start with http:// or https://"
|
||||
wallet:
|
||||
kicker: "STEP 2 OF 3 · WALLET"
|
||||
title: "Set up your wallet"
|
||||
create_new: "Create new"
|
||||
restore_from_seed: "Restore from seed"
|
||||
name_hint: "Wallet name"
|
||||
password_hint: "Password"
|
||||
repeat_password_hint: "Repeat password"
|
||||
restore_hint: "Have your seed words ready — you'll enter them next."
|
||||
create_hint: "Next you'll get 24 seed words to write down. They are the money — anyone holding them holds your funds."
|
||||
continue: "Continue"
|
||||
passwords_no_match: "Passwords don't match"
|
||||
words:
|
||||
kicker: "STEP 2 OF 3 · WALLET"
|
||||
title_restore: "Enter your seed words"
|
||||
title_create: "Write these words down"
|
||||
write_down_hint: "On paper, in order. Anyone with these words can take your funds; without them a lost device means lost funds."
|
||||
paste: "Paste"
|
||||
scan_qr: "Scan QR"
|
||||
copy_clipboard: "Copy to clipboard (avoid this)"
|
||||
restore_wallet: "Restore wallet"
|
||||
wrote_them_down: "I wrote them down"
|
||||
fill_every_word: "Fill every word — tap a word to edit it, or paste the phrase."
|
||||
confirm:
|
||||
kicker: "STEP 2 OF 3 · WALLET"
|
||||
title: "Now prove it"
|
||||
enter_hint: "Enter the words you just wrote down. Tap a word to type it."
|
||||
paste: "Paste"
|
||||
create_wallet: "Create wallet"
|
||||
keep_going: "Keep going — every word, in order."
|
||||
identity:
|
||||
kicker: "STEP 3 OF 3 · IDENTITY"
|
||||
title: "Your payment identity"
|
||||
key_being_made: "key being made…"
|
||||
connected_nym: "connected over Nym"
|
||||
connecting_nym: "connecting over Nym…"
|
||||
fresh_key_blurb: "A payment key that isn't part of your seed — rotate it anytime to stay private, without touching your funds."
|
||||
clean_slate_blurb: "Want a clean slate? Swap in a brand-new key any time — the new you isn't linked to the old one. Same wallet, fresh face."
|
||||
pick_username: "Pick a username — optional"
|
||||
username_blurb: "Friends pay your name instead of a long key. Optional — claim one any time."
|
||||
username_field_hint: "yourname"
|
||||
working: "Working…"
|
||||
claim_username: "Claim username"
|
||||
available_when_connected: "Available once the mixnet connects — or skip and claim later."
|
||||
youre: "You're %{name}"
|
||||
claimed_title: "%{name} is yours"
|
||||
claimed_blurb: "Friends can now pay you by name. You're all set — open your wallet."
|
||||
open_wallet: "Open my wallet"
|
||||
skip_for_now: "Skip for now"
|
||||
import_existing: "Already have a Goblin identity? Import it"
|
||||
import_title: "Import your identity"
|
||||
import_blurb: "Paste your nsec or pick a .backup file to keep your existing key and username instead of this new one."
|
||||
errors:
|
||||
cant_open: "Couldn't open the wallet: %{err}"
|
||||
cant_create: "Couldn't create the wallet: %{err}"
|
||||
send:
|
||||
scan_to_request: "Scan to request"
|
||||
scan_to_pay: "Scan to pay"
|
||||
tab_scan: "Scan"
|
||||
tab_my_code: "My Code"
|
||||
request_from: "Request from"
|
||||
send_to: "Send to"
|
||||
search_hint: "handle, npub, or name"
|
||||
suggested: "%{icon} Suggested"
|
||||
no_contacts: "No contacts yet. Find someone by their handle."
|
||||
no_profile: "no profile"
|
||||
tag_contact: "contact"
|
||||
tag_on_nostr: "on nostr"
|
||||
searching_nostr: "Searching nostr…"
|
||||
unverified_title: "Pay an unverified key?"
|
||||
unverified_body: "No nostr profile is published for this key — it may be brand new, anonymous, or mistyped. Double-check it's the right one before sending."
|
||||
keep_looking: "Keep looking"
|
||||
pay_anyway: "Pay anyway"
|
||||
scan_not_recipient: "That QR isn't a goblin recipient — expected an npub or handle"
|
||||
scan_prompt: "Position a goblin code in view to activate"
|
||||
scan_to_pay_me: "Scan to pay me"
|
||||
share_btn: "%{icon} Share"
|
||||
share_message: "Pay me on Goblin — %{handle}\n%{link}\nnpub: %{npub}"
|
||||
none_found: "No one found for %{label}"
|
||||
enter_recipient: "Enter a handle, npub, or name"
|
||||
amount_title: "Amount"
|
||||
to_name: "To %{name}"
|
||||
not_enough: "You don't have enough grin"
|
||||
max: "Max"
|
||||
note_label: "Note"
|
||||
note_hint: "Add a note…"
|
||||
add_note: "Add a note"
|
||||
edit_note: "Edit note"
|
||||
note_cancel: "Cancel"
|
||||
note_save: "Save"
|
||||
review_btn: "Review"
|
||||
confirm_request: "Confirm request"
|
||||
review_title: "Review"
|
||||
requesting_from: "Requesting from %{name}"
|
||||
youre_sending: "You're sending %{name}"
|
||||
row_from: "From"
|
||||
row_to: "To"
|
||||
row_note: "Note"
|
||||
row_they_pay: "They pay"
|
||||
row_they_pay_val: "Only if they approve"
|
||||
row_delivery: "Delivery"
|
||||
row_delivery_val: "NIP-44 encrypted, over Nym"
|
||||
row_network_fee: "Network fee"
|
||||
row_network_fee_val: "Deducted from your balance"
|
||||
row_privacy: "Privacy"
|
||||
row_privacy_val: "Mimblewimble + Nym"
|
||||
send_request_btn: "Send request"
|
||||
request_approve_hint: "They'll get a request to approve"
|
||||
hold_to_send: "Hold to send"
|
||||
lower_amount: "Go back and lower the amount"
|
||||
hold_confirm_hint: "Press and hold to confirm"
|
||||
requesting: "Requesting…"
|
||||
sending: "Sending…"
|
||||
they: "They"
|
||||
request_blocked: "%{who} isn't accepting requests. Ask them to send you grin instead."
|
||||
failed_request_title: "Couldn't request"
|
||||
failed_send_title: "Couldn't send"
|
||||
failed_request_body: "We couldn't deliver the request. Ask them to send you grin instead."
|
||||
failed_send_body: "The payment wasn't delivered. Your grin is safe — try again."
|
||||
try_again_btn: "Try again"
|
||||
close_btn: "Close"
|
||||
success:
|
||||
requested: "Requested"
|
||||
sent: "Sent"
|
||||
from: "from"
|
||||
to: "to"
|
||||
subtitle: "%{dir} %{who} · just now"
|
||||
done_btn: "Done"
|
||||
receipt_btn: "Receipt"
|
||||
@@ -0,0 +1,808 @@
|
||||
lang_name: Français
|
||||
copy: Copier
|
||||
paste: Coller
|
||||
continue: Continuer
|
||||
complete: Terminer
|
||||
error: Erreur
|
||||
retry: Réessayer
|
||||
close: Fermer
|
||||
change: Changer
|
||||
show: Afficher
|
||||
delete: Supprimer
|
||||
clear: Effacer
|
||||
create: Créer
|
||||
id: Identifiant
|
||||
kernel: Noyau
|
||||
settings: Paramètres
|
||||
language: Langue
|
||||
scan: Scanner
|
||||
qr_code: QR Code
|
||||
scan_qr: Scanner le QR code
|
||||
repeat: Répéter
|
||||
scan_result: Résultat du scan
|
||||
back: Retour
|
||||
share: Partager
|
||||
theme: 'Thème:'
|
||||
dark: Sombre
|
||||
light: Clair
|
||||
file: Fichier
|
||||
choose_file: Choisir un fichier
|
||||
choose_folder: Choisir un dossier
|
||||
crash_report: Rapport d'échec
|
||||
crash_report_warning: L'application s'est fermée de manière inattendue la dernière fois, vous pouvez partager un rapport d'incident avec les développeurs.
|
||||
confirmation: Confirmation
|
||||
enter_url: Entrez l'URL
|
||||
max_short: MAX
|
||||
files_location: Emplacement du fichier
|
||||
moving_files: Déplacer des fichiers
|
||||
wrong_path_error: Chemin incorrect spécifié
|
||||
check_updates: Vérifiez les mises à jour au démarrage
|
||||
update_available: Mise à jour disponible!
|
||||
changelog: 'Journal des modifications:'
|
||||
wallets:
|
||||
await_conf_amount: En attente de confirmation
|
||||
await_fin_amount: En attente de finalisation
|
||||
locked_amount: Verrouillé
|
||||
txs_empty: "Pour recevoir des fonds manuellement ou par transport, utilisez les boutons %{message} ou %{transport} en bas de l'écran. Pour modifier les paramètres du portefeuille, appuyez sur le bouton %{settings}."
|
||||
title: Goblin
|
||||
create_desc: Créer ou importer un portefeuille existant à partir de la phrase de récupération sauvegardée.
|
||||
add: Ajouter un portefeuille
|
||||
name: 'Nom:'
|
||||
pass: 'Mot de passe:'
|
||||
pass_empty: Entrez le mot de passe du portefeuille
|
||||
current_pass: 'Mot de passe actuel:'
|
||||
new_pass: 'Nouveau mot de passe:'
|
||||
min_tx_conf_count: 'Nombre minimum de confirmations pour les transactions:'
|
||||
recover: Restaurer
|
||||
recovery_phrase: Phrase de récupération
|
||||
words_count: 'Nombre de mots:'
|
||||
enter_word: 'Entrez le mot #%{number}:'
|
||||
not_valid_word: Mot entré non valide
|
||||
not_valid_phrase: Phrase entrée non valide
|
||||
create_phrase_desc: Notez et sauvegardez votre phrase de récupération en toute sécurité.
|
||||
restore_phrase_desc: Entrez les mots de votre phrase de récupération sauvegardée.
|
||||
setup_conn_desc: Choisissez comment votre portefeuille se connecte au réseau.
|
||||
conn_method: Méthode de connexion
|
||||
ext_conn: 'Connexions externes:'
|
||||
add_node: Ajouter un noeud
|
||||
node_url: 'URL du noeud:'
|
||||
node_secret: 'Secret API (facultatif):'
|
||||
invalid_url: URL entrée non valide
|
||||
open: Ouvrir le portefeuille
|
||||
wrong_pass: Mot de passe entré incorrect
|
||||
locked: Verrouillé
|
||||
unlocked: Déverrouillé
|
||||
enable_node: "Activez le noeud intégré pour utiliser le portefeuille ou changez les paramètres de connexion en sélectionnant %{settings} en bas de l'écran."
|
||||
node_loading: "Le portefeuille sera chargé après la synchronisation du noeud intégré. Vous pouvez changer la connexion en sélectionnant %{settings} en bas de l'écran."
|
||||
loading: Chargement
|
||||
closing: Fermeture
|
||||
checking: Vérification
|
||||
default_wallet: Portefeuille par défaut
|
||||
new_account_desc: 'Entrez le nom du nouveau compte:'
|
||||
wallet_loading: Chargement du portefeuille
|
||||
wallet_closing: Fermeture du portefeuille
|
||||
wallet_checking: Vérification du portefeuille
|
||||
tx_loading: Chargement des transactions
|
||||
default_account: Compte par défaut
|
||||
accounts: Comptes
|
||||
tx_sent: Envoyé
|
||||
tx_received: Reçu
|
||||
tx_sending: Envoi
|
||||
tx_receiving: Réception
|
||||
tx_confirming: En attente de confirmation
|
||||
tx_canceled: Annulé
|
||||
tx_cancelling: Annulation
|
||||
tx_finalizing: Finalisation
|
||||
tx_posting: Publication
|
||||
tx_confirmed: Confirmé
|
||||
txs: Transactions
|
||||
tx: Transaction
|
||||
messages: Messages
|
||||
transport: Transport
|
||||
input_slatepack_desc: 'Entrez le message Slatepack reçu pour créer une réponse ou finaliser la demande:'
|
||||
parse_slatepack_err: "Une erreur s'est produite lors de la lecture du message, vérifiez l'entrée:"
|
||||
pay_balance_error: 'Le solde du compte est insuffisant pour payer %{amount} ツ et les frais de réseau.'
|
||||
parse_i1_slatepack_desc: 'Pour payer %{amount} ツ, envoyez ce message au destinataire:'
|
||||
parse_i2_slatepack_desc: 'Finalisez la transaction pour recevoir %{amount} ツ:'
|
||||
parse_i3_slatepack_desc: 'Publiez la transaction pour finaliser la réception de %{amount} ツ:'
|
||||
parse_s1_slatepack_desc: "Pour recevoir %{amount} ツ, envoyez ce message à l'expéditeur:"
|
||||
parse_s2_slatepack_desc: 'Finalisez la transaction pour envoyer %{amount} ツ:'
|
||||
parse_s3_slatepack_desc: "Publiez la transaction pour finaliser l'envoi de %{amount} ツ:"
|
||||
resp_slatepack_err: "Une erreur s'est produite lors de la création de la réponse, vérifiez les données saisies ou réessayez:"
|
||||
resp_exists_err: Une telle transaction existe déjà.
|
||||
resp_canceled_err: Une telle transaction a déjà été annulée.
|
||||
create_request_desc: 'Créez une demande pour envoyer ou recevoir des fonds:'
|
||||
send_request_desc: 'Vous avez créé une demande pour envoyer %{amount} ツ. Envoyez ce message au destinataire:'
|
||||
send_slatepack_err: "Une erreur s'est produite lors de la création de la demande d'envoi de fonds, vérifiez les données saisies ou réessayez."
|
||||
invoice_desc: "Vous avez créé une demande pour recevoir %{amount} ツ. Envoyez ce message à l'expéditeur:"
|
||||
invoice_slatepack_err: "Une erreur s'est produite lors de l'émission de la facture, vérifiez les données saisies ou réessayez."
|
||||
finalize_slatepack_err: "Une erreur s'est produite lors de la finalisation, vérifiez les données saisies ou réessayez:"
|
||||
finalize: Finaliser
|
||||
use_dandelion: Utiliser Dandelion
|
||||
enter_amount_send: 'Vous avez %{amount} ツ. Entrez le montant à envoyer:'
|
||||
enter_amount_receive: 'Entrez le montant à recevoir:'
|
||||
recovery: Récupération
|
||||
repair_wallet: Réparer le portefeuille
|
||||
repair_desc: Vérifiez un portefeuille, réparez et restaurez les sorties manquantes si nécessaire. Cette opération prendra du temps.
|
||||
repair_unavailable: "Vous avez besoin d'une connexion active au noeud et d'une synchronisation complète du portefeuille."
|
||||
delete: Supprimer le portefeuille
|
||||
delete_conf: Êtes-vous sûr de vouloir supprimer le portefeuille?
|
||||
delete_desc: "Assurez-vous d'avoir sauvegardé votre phrase de récupération pour accéder aux fonds plus tard."
|
||||
wallet_loading_err: "Une erreur s'est produite lors de la synchronisation du portefeuille. Vous pouvez réessayer ou changer les paramètres de connexion en sélectionnant %{settings} en bas de l'écran."
|
||||
wallet: Portefeuille
|
||||
send: Envoyer
|
||||
receive: Recevoir
|
||||
settings: Paramètres du portefeuille
|
||||
tx_send_cancel_conf: "Êtes-vous sûr de vouloir annuler l'envoi de %{amount} ツ?"
|
||||
tx_receive_cancel_conf: 'Êtes-vous sûr de vouloir annuler la réception de %{amount} ツ?'
|
||||
rec_phrase_not_found: Phrase de récupération non trouvée.
|
||||
restore_wallet_desc: "Restaurer le portefeuille en supprimant tous les fichiers si la réparation habituelle n'a pas aidé. Vous devrez rouvrir votre portefeuille."
|
||||
fee_base_desc: 'Frais (valeur de base%{value}):'
|
||||
payment_proof: Preuve de paiement
|
||||
payment_proof_desc: 'Saisissez la preuve de paiement reçue pour vérifier la transaction:'
|
||||
payment_proof_valid: 'La preuve de paiement saisie est valide:'
|
||||
payment_proof_error: "La preuve de paiement saisie n'est pas valide:"
|
||||
tx_delete_confirmation: Êtes-vous sûr de vouloir supprimer la transaction de l'historique?
|
||||
transport:
|
||||
desc: 'Utilisez le transport pour recevoir ou envoyer des messages de manière synchronisée:'
|
||||
tor_network: Réseau Tor
|
||||
connected: Connecté
|
||||
connecting: Connexion en cours
|
||||
disconnecting: Déconnexion en cours
|
||||
conn_error: Erreur de connexion
|
||||
disconnected: Déconnecté
|
||||
receiver_address: 'Adresse du destinataire:'
|
||||
incorrect_addr_err: 'Adresse entrée incorrecte:'
|
||||
tor_send_error: "Une erreur s'est produite lors de l'envoi via Tor. Assurez-vous que le destinataire est en ligne, la transaction a été annulée."
|
||||
tor_autorun_desc: "Lancer automatiquement le service Tor à l'ouverture du portefeuille pour recevoir les transactions de manière synchronisée."
|
||||
tor_sending: Envoi via Tor
|
||||
tor_settings: Paramètres Tor
|
||||
bridges: Passerelles
|
||||
bridges_desc: Configurez des passerelles pour contourner la censure du réseau Tor si la connexion habituelle ne fonctionne pas.
|
||||
bin_file: 'Fichier binaire:'
|
||||
conn_line: 'Ligne de connexion:'
|
||||
bridges_disabled: Passerelles désactivés
|
||||
bridge_name: 'Passerelles %{b}'
|
||||
network:
|
||||
self: Réseau
|
||||
type: 'Type de réseau:'
|
||||
mainnet: Principal
|
||||
testnet: Test
|
||||
connections: Connexions
|
||||
node: Noeud intégré
|
||||
metrics: Métriques
|
||||
mining: Minage
|
||||
settings: Paramètres du noeud
|
||||
enable_node: Activer le noeud
|
||||
autorun: Exécution automatique
|
||||
disabled_server: "Activez le noeud intégré ou ajoutez une autre méthode de connexion en appuyant sur %{dots} dans le coin supérieur gauche de l'écran."
|
||||
no_ips: "Il n'y a pas d'adresses IP disponibles sur votre système, le serveur ne peut pas démarrer, vérifiez votre connectivité réseau"
|
||||
available: Disponible
|
||||
not_available: Indisponible
|
||||
availability_check: Vérification de la disponibilité
|
||||
android_warning: "Attention aux utilisateurs Android. Pour synchroniser correctement le noeud intégré, vous devez autoriser l'accès aux notifications et supprimer les restrictions d'utilisation de la batterie pour l'application Grim dans les paramètres système de votre téléphone. Cette opération est nécessaire pour le bon fonctionnement de l'application en arrière-plan."
|
||||
sync_status:
|
||||
node_restarting: Le noeud redémarre
|
||||
node_down: Le noeud est hors ligne
|
||||
initial: Le noeud démarre
|
||||
no_sync: Le noeud fonctionne
|
||||
awaiting_peers: En attente de pairs
|
||||
header_sync: Téléchargement des en-têtes
|
||||
header_sync_percent: 'Téléchargement des en-têtes : %{percent}%'
|
||||
tx_hashset_pibd: "Téléchargement de l'état (PIBD)"
|
||||
tx_hashset_pibd_percent: "Téléchargement de l'état (PIBD) : %{percent}%"
|
||||
tx_hashset_download: "Téléchargement de l'état"
|
||||
tx_hashset_download_percent: "Téléchargement de l'état : %{percent}%"
|
||||
tx_hashset_setup_history: "Préparation de l'état (historique) : %{percent}%"
|
||||
tx_hashset_setup_position: "Préparation de l'état (position) : %{percent}%"
|
||||
tx_hashset_setup: "Préparation de l'état"
|
||||
tx_hashset_range_proofs_validation: "Validation de l'état (preuves de plage) : %{percent}%"
|
||||
tx_hashset_kernels_validation: "Validation de l'état (kernels) : %{percent}%"
|
||||
tx_hashset_save: "Finalisation de l'état de la chaîne"
|
||||
body_sync: Téléchargement des blocs
|
||||
body_sync_percent: 'Téléchargement des blocs : %{percent}%'
|
||||
shutdown: Le noeud se ferme
|
||||
network_node:
|
||||
header: En-tête
|
||||
block: Bloc
|
||||
hash: Hash
|
||||
height: Hauteur
|
||||
difficulty: Difficulté
|
||||
time: Temps
|
||||
main_pool: Pool principal
|
||||
stem_pool: Pool secondaire
|
||||
data: Données
|
||||
size: Taille (GB)
|
||||
peers: Pairs
|
||||
error_clean: Les données du noeud ont été corrompues, une resynchronisation est nécessaire.
|
||||
resync: Resynchronisation
|
||||
error_p2p_api: "Une erreur s'est produite lors de l'initialisation du serveur %{p2p_api}, vérifiez les paramètres %{p2p_api} en sélectionnant %{settings} en bas de l'écran."
|
||||
error_config: "Une erreur s'est produite lors de l'initialisation de la configuration, vérifiez les paramètres en sélectionnant %{settings} en bas de l'écran."
|
||||
error_unknown: "Une erreur s'est produite lors de l'initialisation, vérifiez les paramètres du noeud intégré en sélectionnant %{settings} en bas de l'écran ou resynchronisez."
|
||||
network_metrics:
|
||||
loading: Les métriques seront disponibles après la synchronisation
|
||||
emission: Émission
|
||||
inflation: Inflation
|
||||
supply: Offre
|
||||
block_time: Temps de bloc
|
||||
reward: Récompense
|
||||
difficulty_window: 'Fenêtre de difficulté %{size}'
|
||||
network_mining:
|
||||
loading: Le minage sera disponible après la synchronisation
|
||||
info: "Le serveur de minage est activé, vous pouvez changer ses paramètres en sélectionnant %{settings} en bas de l'écran. Les données sont mises à jour lorsque les appareils sont connectés."
|
||||
restart_server_required: Le redémarrage du serveur est nécessaire pour appliquer les modifications.
|
||||
rewards_wallet: Portefeuille pour les récompenses
|
||||
server: Serveur Stratum
|
||||
address: Adresse
|
||||
miners: Mineurs
|
||||
devices: Appareils
|
||||
blocks_found: Blocs trouvés
|
||||
hashrate: 'Taux de hachage (C%{bits})'
|
||||
connected: Connecté
|
||||
disconnected: Déconnecté
|
||||
network_settings:
|
||||
change_value: Modifier la valeur
|
||||
stratum_ip: 'Adresse IP Stratum :'
|
||||
stratum_port: 'Port Stratum :'
|
||||
port_unavailable: Le port spécifié est indisponible
|
||||
restart_node_required: Le redémarrage du noeud est nécessaire pour appliquer les modifications.
|
||||
choose_wallet: Choisir un portefeuille
|
||||
stratum_wallet_warning: Le portefeuille doit être ouvert pour recevoir des récompenses.
|
||||
enable: Activer
|
||||
disable: Désactiver
|
||||
restart: Redémarrer
|
||||
server: Serveur
|
||||
api_ip: 'Adresse IP API :'
|
||||
api_port: 'Port API :'
|
||||
api_secret: 'Jeton API Rest et API Propriétaire V2 :'
|
||||
foreign_api_secret: 'Jeton API étranger :'
|
||||
disabled: Désactivé
|
||||
enabled: Activé
|
||||
ftl: 'La Limite de Temps Futur (FTL) :'
|
||||
ftl_description: "Limite de combien de temps dans le futur, par rapport à l'heure locale d'un noeud en secondes, l'horodatage sur un nouveau bloc peut être, afin que le bloc soit accepté."
|
||||
not_valid_value: "La valeur entrée n'est pas valide"
|
||||
full_validation: Validation complète
|
||||
full_validation_description: Exécuter une validation complète de la chaîne lors du traitement de chaque bloc (sauf pendant la synchronisation).
|
||||
archive_mode: Mode archive
|
||||
archive_mode_desc: "Exécuter le noeud en mode archive complet (plus d'espace disque et de temps seront nécessaires pour la synchronisation)."
|
||||
attempt_time: 'Temps de tentative de minage (en secondes) :'
|
||||
attempt_time_desc: "Le temps pendant lequel tenter de miner sur un en-tête particulier avant d'arrêter et de récupérer à nouveau les transactions du pool"
|
||||
min_share_diff: 'La difficulté minimale acceptable du partage :'
|
||||
reset_settings_desc: Réinitialiser les paramètres du noeud aux valeurs par défaut
|
||||
reset_settings: Réinitialiser les paramètres
|
||||
reset: Réinitialiser
|
||||
tx_pool: Pool de transactions
|
||||
pool_fee: 'Frais de base acceptés dans le pool :'
|
||||
reorg_period: 'Période de rétention du cache de réorganisation (en minutes) :'
|
||||
max_tx_pool: 'Nombre maximum de transactions dans le pool :'
|
||||
max_tx_stempool: 'Nombre maximum de transactions dans le pool secondaire :'
|
||||
max_tx_weight: 'Poids total maximum des transactions pouvant être sélectionnées pour créer un bloc :'
|
||||
epoch_duration: "Durée de l'époque (en secondes) :"
|
||||
embargo_timer: "Minuterie d'embargo (en secondes) :"
|
||||
aggregation_period: "Période d'agrégation (en secondes) :"
|
||||
stem_probability: 'Probabilité de la phase secondaire :'
|
||||
stem_txs: Transactions secondaires
|
||||
p2p_server: Serveur P2P
|
||||
p2p_port: 'Port P2P :'
|
||||
add_seed: Ajouter une seed DNS
|
||||
seed_address: 'Adresse de la seed DNS :'
|
||||
add_peer: Ajouter un pair
|
||||
peer_address: 'Adresse du pair :'
|
||||
peer_address_error: "Entrez l'adresse IP ou le nom DNS (assurez-vous que l'hôte spécifié est disponible) au format correct, par exemple : 192.168.0.1:1234 ou example.com:5678"
|
||||
default: Par défaut
|
||||
allow_list: Liste autorisée
|
||||
allow_list_desc: Se connecter uniquement aux pairs de cette liste.
|
||||
deny_list: Liste refusée
|
||||
deny_list_desc: Ne jamais se connecter aux pairs de cette liste.
|
||||
favourites: Favoris
|
||||
favourites_desc: Une liste de pairs préférés à connecter.
|
||||
ban_window: 'Combien de temps (en secondes) un pair banni doit rester banni :'
|
||||
ban_window_desc: La décision de bannir est prise par le noeud, en fonction de la validité des données reçues du pair.
|
||||
max_inbound_count: 'Nombre maximum de connexions de pairs entrants :'
|
||||
max_outbound_count: 'Nombre maximum de connexions de pairs sortants :'
|
||||
reset_data_desc: Réinitialisez les données du noeud. Utilisez-le avec prudence uniquement en cas de problème de synchronisation.
|
||||
reset_data: Réinitialisation des données
|
||||
modal:
|
||||
cancel: Annuler
|
||||
save: Sauvegarder
|
||||
add: Ajouter
|
||||
modal_exit:
|
||||
description: "Êtes-vous sûr de vouloir quitter l'application ?"
|
||||
exit: Quitter
|
||||
app_settings:
|
||||
proxy: Proxy
|
||||
proxy_desc: Vaut-il la peine d'utiliser un proxy pour les requêtes réseau de l'application.
|
||||
keyboard:
|
||||
1: 1
|
||||
2: 2
|
||||
3: 3
|
||||
4: 4
|
||||
5: 5
|
||||
6: 6
|
||||
7: 7
|
||||
8: 8
|
||||
9: 9
|
||||
0: 0
|
||||
01: '`'
|
||||
q: a
|
||||
w: z
|
||||
e: e
|
||||
r: r
|
||||
t: t
|
||||
y: y
|
||||
u: u
|
||||
i: i
|
||||
o: o
|
||||
p: p
|
||||
p1: ç
|
||||
a: q
|
||||
s: s
|
||||
d: d
|
||||
f: f
|
||||
g: g
|
||||
h: h
|
||||
j: j
|
||||
k: k
|
||||
l: l
|
||||
l1: m
|
||||
l2: ù
|
||||
z: w
|
||||
x: x
|
||||
c: c
|
||||
v: v
|
||||
b: b
|
||||
n: n
|
||||
m: ','
|
||||
m1: .
|
||||
m2: ':'
|
||||
m3: /
|
||||
goblin:
|
||||
home:
|
||||
anonymous: "Anonyme"
|
||||
connected_nym: "Connecté via Nym"
|
||||
nym_ready: "Nym prêt · relais…"
|
||||
connecting_nym: "Connexion à Nym…"
|
||||
cant_reach_node: "Nœud injoignable"
|
||||
node_synced: "Nœud synchronisé"
|
||||
syncing: "Synchronisation…"
|
||||
block: "Bloc %{height}"
|
||||
waiting_for_chain: "En attente de la chaîne…"
|
||||
nav_wallet: "Portefeuille"
|
||||
nav_pay: "Payer"
|
||||
nav_activity: "Activité"
|
||||
nav_receive: "Recevoir"
|
||||
nav_settings: "Réglages"
|
||||
activity: "Activité"
|
||||
empty_title: "Aucune activité"
|
||||
empty_sub: "Envoyez ou recevez des grin pour commencer."
|
||||
recent: "Récent"
|
||||
scan_to_pay: "Scanner pour payer"
|
||||
type_amount: "Saisir un montant"
|
||||
request: "Demander"
|
||||
pay: "Payer"
|
||||
enter_amount: "Saisissez un montant à payer ou demander"
|
||||
activity:
|
||||
canceled: "annulé"
|
||||
pending: "en attente"
|
||||
earlier: "Plus tôt"
|
||||
today: "Aujourd'hui"
|
||||
yesterday: "Hier"
|
||||
title: "Activité"
|
||||
requests: "Demandes"
|
||||
empty_title: "Aucune activité"
|
||||
empty_sub: "Vos paiements apparaîtront ici."
|
||||
pending_header: "En attente"
|
||||
receipt:
|
||||
title: "Reçu"
|
||||
not_found: "Transaction introuvable"
|
||||
for_note: "Pour %{note}"
|
||||
details: "Détails de la transaction"
|
||||
canceled: "Annulé"
|
||||
expired: "Expiré"
|
||||
funds_returned: "Fonds retournés"
|
||||
complete: "Terminé"
|
||||
payment_received: "Paiement reçu"
|
||||
payment_sent: "Paiement envoyé avec succès"
|
||||
pending: "En attente"
|
||||
confs: "%{c}/%{r} confirmations"
|
||||
waiting_to_confirm: "En attente de confirmation"
|
||||
paying: "Paiement…"
|
||||
you: "Vous"
|
||||
to: "À"
|
||||
from: "De"
|
||||
nostr: "nostr"
|
||||
fee_none: "Aucun"
|
||||
network_fee: "Frais de réseau"
|
||||
privacy: "Confidentialité"
|
||||
privacy_value: "Mimblewimble + Nym"
|
||||
transaction: "Transaction"
|
||||
cancel_request: "Annuler la demande"
|
||||
cancel_send: "Annuler le paiement"
|
||||
cancel_send_confirm: "Appuyez à nouveau pour annuler — il peut encore le recevoir"
|
||||
cancel_send_done: "Paiement annulé — vos fonds sont à nouveau disponibles"
|
||||
cancel_send_too_late: "Ce paiement est déjà passé et ne peut pas être annulé"
|
||||
waiting_to_receive: "En attente de réception par %{name}…"
|
||||
request:
|
||||
title: "%{name} demande"
|
||||
approve: "Approuver"
|
||||
decline: "Refuser"
|
||||
review_title: "Vérifier la demande"
|
||||
hold_to_accept: "Maintenir pour accepter"
|
||||
hold_accept_hint: "Maintenez pour payer cette demande"
|
||||
receive:
|
||||
title: "Recevoir"
|
||||
requesting: "Demande de %{amt}%{tsu} — partagez pour être payé"
|
||||
clear_request: "Effacer la demande"
|
||||
share_handle: "Partagez votre identifiant pour être payé"
|
||||
copied: "Copié"
|
||||
copy_nostr_id: "Copier l'ID nostr"
|
||||
copy_address: "Copier l'adresse"
|
||||
copy_npub: "Copier npub"
|
||||
share_message: "Payez-moi sur Goblin (goblin.st) — %{npub}"
|
||||
privacy_note: "Votre nom d'utilisateur est public. Le contenu des paiements reste chiffré sur le réseau."
|
||||
profile:
|
||||
title: "Profil"
|
||||
activity: "Activité"
|
||||
no_activity: "Aucune activité avec cette personne."
|
||||
unblock: "Débloquer"
|
||||
block: "Bloquer"
|
||||
blocked_blurb: "Bloqué — ses paiements et demandes sont ignorés."
|
||||
block_blurb: "Le blocage ignore ses paiements et demandes entrants."
|
||||
settings:
|
||||
title: "Réglages"
|
||||
connected_nostr: "Connecté à nostr"
|
||||
connecting_relays: "Connexion aux relais…"
|
||||
identity: "Identité"
|
||||
copy_npub: "Copier le npub (public)"
|
||||
rotate_key: "Renouveler la clé nostr"
|
||||
import_identity: "Importer l'identité (.backup / nsec)"
|
||||
backup_note: "Changement d'appareil ? Sauvegardez les DEUX : votre phrase seed (fonds) et votre fichier .backup d'identité (nom + clé)."
|
||||
wallet: "Portefeuille"
|
||||
display_unit: "Unité d'affichage"
|
||||
relays: "Relais"
|
||||
node: "Nœud"
|
||||
slatepacks: "Slatepacks"
|
||||
slatepacks_value: "Transaction manuelle"
|
||||
lock_wallet: "Verrouiller le portefeuille"
|
||||
switch_wallet: "Changer de portefeuille"
|
||||
advanced: "Avancé"
|
||||
privacy: "Confidentialité"
|
||||
mixnet_routing: "Routage par mixnet"
|
||||
messages_lookups: "Messages et recherches"
|
||||
auto_accept: "Acceptation auto"
|
||||
pairing: "Appairage"
|
||||
accept_anyone: "Tout le monde"
|
||||
accept_contacts: "Contacts seulement"
|
||||
accept_ask: "Toujours demander"
|
||||
requests: "Demandes"
|
||||
incoming_requests: "Demandes entrantes"
|
||||
incoming_requests_sub: "Laisser les autres vous demander de l'argent"
|
||||
appearance: "Apparence"
|
||||
theme: "Thème"
|
||||
theme_light: "Clair"
|
||||
theme_dark: "Sombre"
|
||||
theme_yellow: "Jaune"
|
||||
archive: "Archive"
|
||||
export_archive: "Exporter l'archive"
|
||||
wipe_history: "Effacer l'historique des paiements"
|
||||
about: "À propos"
|
||||
goblin: "Goblin"
|
||||
build: "Build %{build}"
|
||||
network: "Réseau"
|
||||
network_value: "MW + mixnet Nym + nostr"
|
||||
third_party: "Tiers"
|
||||
grim: "GRIM (portefeuille amont)"
|
||||
grin_node: "Nœud grin"
|
||||
sp_intro: "Avancé — échangez des slatepacks bruts à la main, comme le fait GRIM. À utiliser seulement si vous ne pouvez pas payer ou être payé via un username."
|
||||
sp_receive_group: "Recevoir ou finaliser"
|
||||
sp_receive_blurb: "Collez un slatepack qu'on vous a donné. Goblin reçoit le paiement, règle la facture, ou le finalise et le publie."
|
||||
sp_process: "Traiter le slatepack"
|
||||
sp_paste_first: "Collez d'abord un slatepack."
|
||||
sp_reply_ready: "Réponse prête — renvoyez-la à l'expéditeur."
|
||||
sp_finalizing: "Finalisation et publication sur la chaîne…"
|
||||
sp_create_group: "Créer un paiement"
|
||||
sp_create_blurb: "Créez un slatepack à remettre à quelqu'un. Il le reçoit, vous renvoie la réponse, et vous le finalisez ci-dessus."
|
||||
sp_amount_hint: "Montant en grin"
|
||||
sp_addr_hint: "Adresse du destinataire (facultatif)"
|
||||
sp_create: "Créer un slatepack"
|
||||
sp_ready: "Slatepack prêt — remettez-le au destinataire."
|
||||
sp_amount_gt_zero: "Saisissez un montant supérieur à zéro."
|
||||
sp_to_send: "Slatepack à envoyer"
|
||||
sp_copy: "Copier le slatepack"
|
||||
rotate_line1: "• Vous obtenez une toute nouvelle clé ALÉATOIRE ; l'ancien npub cesse de recevoir. Il n'y a aucune chaîne de dérivation entre eux."
|
||||
rotate_line2: "• La nouvelle clé n'est PAS récupérable depuis votre phrase de récupération — sauvegardez le nouveau nsec juste après le renouvellement."
|
||||
rotate_line3: "• Votre nom d'utilisateur est LIBÉRÉ — réclamez le même ou un nouveau juste après (une fois libre, n'importe qui peut le prendre)."
|
||||
rotate_line4: "• Les paiements encore en cours vers l'ancienne clé SERONT interrompus — attendez d'abord la fin des paiements en attente."
|
||||
rotate_line5: "• Les contacts qui ont enregistré votre npub directement doivent vous retrouver — partagez votre nouveau npub ou votre username re-réservé."
|
||||
cancel: "Annuler"
|
||||
continue: "Continuer"
|
||||
final_confirmation: "Confirmation finale"
|
||||
rotate_confirm_blurb: "Cette action est irréversible depuis l'app. Tapez RESET et saisissez le mot de passe du portefeuille pour renouveler."
|
||||
type_reset: "Tapez RESET"
|
||||
wallet_password: "Mot de passe du portefeuille"
|
||||
rotate_key_btn: "Renouveler la clé"
|
||||
rotating_key: "Renouvellement de la clé…"
|
||||
key_rotated: "Clé renouvelée"
|
||||
new_npub: "Nouveau npub : %{npub}"
|
||||
backup_new_key: "Sauvegardez la NOUVELLE clé secrète maintenant — votre phrase de récupération ne peut pas la restaurer."
|
||||
copy_new_nsec: "Copier la sauvegarde du nouveau nsec"
|
||||
done: "Terminé"
|
||||
rotation_failed: "Échec du renouvellement"
|
||||
close: "Fermer"
|
||||
import_identity_title: "Importer une identité"
|
||||
import_blurb: "Remplace l'identité nostr de ce portefeuille — choisissez un fichier .backup GOBLIN, ou collez un nsec. Une sauvegarde restaure aussi votre nom d'utilisateur et votre historique. Sauvegardez d'abord la clé actuelle si besoin."
|
||||
import_nsec_hint: "nsec1… ou sauvegarde collée"
|
||||
backup_password_hint: "Mot de passe de sauvegarde (uniquement si exportée ailleurs)"
|
||||
import_btn: "Importer"
|
||||
importing: "Importation…"
|
||||
identity_replaced: "Identité remplacée"
|
||||
now_using: "Utilise maintenant : %{npub}"
|
||||
import_failed: "Échec de l'importation"
|
||||
name_authority: "Autorité de noms"
|
||||
name_authority_title: "Changer l'autorité de noms"
|
||||
name_authority_blurb: "Le serveur qui enregistre et vérifie les noms. Pointez-le vers une autre instance pour utiliser et payer des noms qui y sont hébergés."
|
||||
name_authority_invalid: "Saisissez une URL complète (https://…)."
|
||||
reset: "Réinitialiser"
|
||||
save: "Enregistrer"
|
||||
backup_file: "Sauvegarder dans un fichier"
|
||||
choose_backup_file: "Choisir un fichier .backup"
|
||||
backup_read_failed: "Impossible de lire ce fichier."
|
||||
backup_saved: "Sauvegarde enregistrée"
|
||||
backup_saved_sub: "Conservez le fichier .backup en lieu sûr — quiconque l'a AVEC votre mot de passe peut restaurer votre identité."
|
||||
backup_file_title: "Sauvegarder l'identité"
|
||||
backup_file_blurb: "Crée un fichier .backup chiffré avec votre nom d'utilisateur et votre clé. Saisissez le mot de passe du portefeuille pour le sceller."
|
||||
backup_write_failed: "Impossible d'enregistrer le fichier."
|
||||
create_backup: "Créer la sauvegarde"
|
||||
registered: "%{name} enregistré"
|
||||
released_msg: "Libéré — le nom est disponible"
|
||||
release_confirm: "Libérer %{name} ?"
|
||||
release_blurb: "Dès qu'il est libre, il est disponible — n'importe qui peut le réclamer, y compris votre prochaine clé. Vous ne pourrez pas enregistrer un autre nom d'utilisateur pendant 10 minutes."
|
||||
releasing: "Libération…"
|
||||
keep_it: "Le garder"
|
||||
release_it: "Le libérer"
|
||||
username: "Nom d'utilisateur"
|
||||
username_note: "Affiché comme you. Public sur goblin.st. Les paiements restent chiffrés."
|
||||
release_username: "Libérer le nom d'utilisateur"
|
||||
pick_username: "Choisir un nom d'utilisateur — facultatif"
|
||||
working: "En cours…"
|
||||
claim: "Réserver"
|
||||
err_just_taken: "Ce nom d'utilisateur vient d'être pris"
|
||||
err_cooldown: "Vous avez récemment libéré un nom d'utilisateur — vous pouvez en enregistrer un nouveau dans les 10 minutes."
|
||||
err_unreachable: "Impossible de joindre goblin.st — souci de connexion. Réessayez."
|
||||
err_release: "Impossible de libérer : %{err}"
|
||||
avail_available: "Disponible !"
|
||||
avail_taken: "Pris"
|
||||
avail_reserved: "Réservé"
|
||||
avail_invalid: "Les noms font 3 à 20 caractères : a–z, 0–9, _ ou -"
|
||||
avail_quarantined: "Indisponible"
|
||||
avail_unknown: "Vérification impossible — souci de connexion. Réessayez."
|
||||
advanced:
|
||||
title: "Avancé"
|
||||
intro: "Outils de portefeuille bas niveau de GRIM. Vous n'en aurez normalement pas besoin."
|
||||
own_node_desc: "Synchronisez un nœud Grin complet sur cet appareil au lieu de faire confiance à un nœud public."
|
||||
own_node_active: "Votre propre nœud est actif"
|
||||
repair: "Réparer le portefeuille"
|
||||
repair_desc: "Re-scanner la chaîne et restaurer les sorties manquantes. Cela peut prendre du temps."
|
||||
repair_unavailable: "Nécessite d'abord une connexion à un nœud synchronisé."
|
||||
repairing: "Réparation… %{pct}%"
|
||||
restore: "Restaurer le portefeuille"
|
||||
restore_desc: "Supprimer les données locales et reconstruire depuis votre seed. À utiliser si une réparation n'a pas aidé — vous rouvrirez le portefeuille ensuite."
|
||||
restore_confirm: "Touchez à nouveau pour restaurer"
|
||||
show_phrase: "Phrase de récupération"
|
||||
phrase_desc: "Vos 24 mots de seed grin — le seul moyen de récupérer les fonds. Gardez-les hors ligne et privés."
|
||||
reveal: "Afficher la phrase"
|
||||
hide: "Masquer"
|
||||
password: "Mot de passe du portefeuille"
|
||||
wrong_password: "Mot de passe incorrect."
|
||||
delete: "Supprimer le portefeuille"
|
||||
delete_desc: "Supprimer définitivement ce portefeuille de cet appareil. Sans votre seed, les fonds sont irrécupérables."
|
||||
delete_confirm: "Touchez à nouveau pour supprimer"
|
||||
manage_node: "Gérer la connexion au nœud"
|
||||
repair_confirm: "Oui, réparer maintenant"
|
||||
repair_confirm_note: "La réparation réanalyse la chaîne et peut prendre quelques minutes."
|
||||
restore_confirm_note: "Cela efface les données locales et les reconstruit depuis votre seed — cela peut prendre plusieurs minutes."
|
||||
privacy:
|
||||
title: "Confidentialité réseau"
|
||||
intro: "Goblin envoie son trafic privé via le mixnet Nym — un réseau à cinq sauts qui masque qui parle à qui, afin qu'un relais ne puisse pas relier un paiement à vous."
|
||||
payments: "Paiements"
|
||||
payments_blurb: "Chaque message nostr transportant un slatepack."
|
||||
usernames: "usernames"
|
||||
usernames_blurb: "Recherches de noms NIP-05 vers et depuis goblin.st."
|
||||
price_avatars: "Prix"
|
||||
price_avatars_blurb: "Le taux en temps réel affiché à côté des montants."
|
||||
over_mixnet: "Via le mixnet"
|
||||
direct_connection: "Connexion directe"
|
||||
grin_node: "Nœud grin"
|
||||
grin_node_blurb: "Synchronisation des blocs et diffusion de votre transaction sur le réseau. Ce sont des données de chaîne publiques, identiques pour tous, et non liées à votre identité."
|
||||
pairing:
|
||||
title: "Appairage"
|
||||
intro: "Ce à quoi votre solde et vos montants sont comparés."
|
||||
pair_with: "Apparier avec"
|
||||
rates_note: "Les cours sont récupérés via le mixnet Nym, uniquement tant qu'un appairage est actif — désactivé, aucune requête de cours ne quitte votre appareil."
|
||||
relays:
|
||||
title: "Relais"
|
||||
intro: "Les messages de paiement sont répliqués sur tous les relais ci-dessous ; un seul relais joignable suffit pour recevoir."
|
||||
your_relays: "Vos relais"
|
||||
add_relay: "Ajouter un relais"
|
||||
add_relay_btn: "Ajouter un relais"
|
||||
save_reconnect: "Enregistrer et reconnecter"
|
||||
none: "aucun"
|
||||
count: "%{n} relais"
|
||||
node:
|
||||
title: "Nœud"
|
||||
connection: "Connexion"
|
||||
integrated: "Nœud intégré"
|
||||
applies_after: "S'applique après le verrouillage puis déverrouillage du portefeuille."
|
||||
add_external: "Ajouter un nœud externe"
|
||||
api_secret_hint: "Secret API (facultatif)"
|
||||
add_node: "Ajouter le nœud"
|
||||
integrated_host: "nœud intégré"
|
||||
summary_syncing: "%{conn} · synchronisation"
|
||||
summary_block: "Bloc %{height} · %{conn}"
|
||||
nips:
|
||||
title: "nostr et NIP"
|
||||
intro1: "Goblin parle nostr — un protocole ouvert de messages signés transmis via de simples serveurs relais. Votre portefeuille porte sa propre identité nostr : une clé aléatoire autonome, gardée délibérément indépendante de vos fonds et de votre phrase de récupération. Chaque paiement voyage comme un message direct chiffré de bout en bout entre identités, le slatepack à l'intérieur."
|
||||
intro2: "goblin.st est le service de noms de Goblin : réserver un nom d'utilisateur y publie une correspondance nom → clé (NIP-05), pour qu'on puisse payer you au lieu d'un long npub. Le nom d'utilisateur est public ; le contenu des paiements ne l'est jamais. Les NIP sont les briques du protocole — touchez-en un pour lire la spécification."
|
||||
n05_title: "Noms"
|
||||
n05_blurb: "Associe username@goblin.st à votre clé, pour que les identifiants fonctionnent comme des adresses."
|
||||
n17_title: "Messages privés"
|
||||
n17_blurb: "L'enveloppe de DM chiffré dans laquelle voyage chaque paiement."
|
||||
n44_title: "Chiffrement"
|
||||
n44_blurb: "Le chiffrement authentifié utilisé à l'intérieur de ces messages."
|
||||
n49_title: "Chiffrement de clé"
|
||||
n49_blurb: "Comment la clé secrète est stockée au repos, verrouillée par votre mot de passe."
|
||||
n59_title: "Emballage cadeau"
|
||||
n59_blurb: "Enveloppe les messages pour que les relais ne voient pas qui parle à qui."
|
||||
n98_title: "Auth HTTP"
|
||||
n98_blurb: "Signe la demande d'enregistrement du nom d'utilisateur auprès de goblin.st."
|
||||
onboarding:
|
||||
intro:
|
||||
private_money_head: "Argent privé"
|
||||
private_money_body: "Goblin est un portefeuille pour grin — de l'argent numérique sans montants ni adresses sur sa chaîne."
|
||||
send_like_message_head: "Envoyer comme un message"
|
||||
send_like_message_body: "Payez un username ou un npub et cela arrive comme un message chiffré de bout en bout via nostr et le mixnet Nym — personne entre les deux ne voit le montant ni les personnes impliquées."
|
||||
yours_alone_head: "À vous seul"
|
||||
yours_alone_body: "Clés, noms et historique vivent sur cet appareil. Construit sur le portefeuille GRIM."
|
||||
get_started: "Commencer"
|
||||
footnote: "Environ une minute. Vous pourrez tout changer plus tard."
|
||||
node:
|
||||
kicker: "ÉTAPE 1 SUR 3 · RÉSEAU"
|
||||
title: "Comment Goblin doit-il\nsurveiller la chaîne ?"
|
||||
own_title: "Lancer mon propre nœud"
|
||||
own_badge: "Privé"
|
||||
own_body: "Ne fait confiance à personne — votre portefeuille vérifie la chaîne lui-même. Se synchronise en arrière-plan pendant que vous terminez la configuration."
|
||||
connect_title: "Se connecter à un nœud"
|
||||
connect_badge: "Instantané"
|
||||
connect_body: "Aucune attente de synchronisation. Le nœud que vous choisissez peut voir les requêtes de votre portefeuille."
|
||||
changeable: "Modifiable à tout moment dans Réglages → Nœud."
|
||||
continue: "Continuer"
|
||||
url_invalid: "L'URL du nœud doit commencer par http:// ou https://"
|
||||
wallet:
|
||||
kicker: "ÉTAPE 2 SUR 3 · PORTEFEUILLE"
|
||||
title: "Configurez votre portefeuille"
|
||||
create_new: "Créer un nouveau"
|
||||
restore_from_seed: "Restaurer depuis la phrase"
|
||||
name_hint: "Nom du portefeuille"
|
||||
password_hint: "Mot de passe"
|
||||
repeat_password_hint: "Répéter le mot de passe"
|
||||
restore_hint: "Préparez vos mots de récupération — vous les saisirez ensuite."
|
||||
create_hint: "Vous obtiendrez ensuite 24 mots de récupération à noter. Ce sont l'argent — quiconque les détient détient vos fonds."
|
||||
continue: "Continuer"
|
||||
passwords_no_match: "Les mots de passe ne correspondent pas"
|
||||
words:
|
||||
kicker: "ÉTAPE 2 SUR 3 · PORTEFEUILLE"
|
||||
title_restore: "Saisissez vos mots de récupération"
|
||||
title_create: "Notez ces mots"
|
||||
write_down_hint: "Sur papier, dans l'ordre. Quiconque a ces mots peut prendre vos fonds ; sans eux, un appareil perdu signifie des fonds perdus."
|
||||
paste: "Coller"
|
||||
scan_qr: "Scanner le QR"
|
||||
copy_clipboard: "Copier dans le presse-papiers (à éviter)"
|
||||
restore_wallet: "Restaurer le portefeuille"
|
||||
wrote_them_down: "Je les ai notés"
|
||||
fill_every_word: "Remplissez chaque mot — touchez un mot pour le modifier, ou collez la phrase."
|
||||
confirm:
|
||||
kicker: "ÉTAPE 2 SUR 3 · PORTEFEUILLE"
|
||||
title: "Maintenant prouvez-le"
|
||||
enter_hint: "Saisissez les mots que vous venez de noter. Touchez un mot pour le taper."
|
||||
paste: "Coller"
|
||||
create_wallet: "Créer le portefeuille"
|
||||
keep_going: "Continuez — chaque mot, dans l'ordre."
|
||||
identity:
|
||||
kicker: "ÉTAPE 3 SUR 3 · IDENTITÉ"
|
||||
title: "Votre identité de paiement"
|
||||
key_being_made: "clé en cours de création…"
|
||||
connected_nym: "connecté via Nym"
|
||||
connecting_nym: "connexion via Nym…"
|
||||
fresh_key_blurb: "Une clé de paiement qui ne fait pas partie de votre seed — renouvelable à tout moment, sans toucher à vos fonds."
|
||||
clean_slate_blurb: "Envie de repartir à zéro ? Remplacez par une toute nouvelle clé à tout moment — le nouveau vous n'est pas lié à l'ancien. Même portefeuille, nouveau visage."
|
||||
pick_username: "Choisir un nom d'utilisateur — facultatif"
|
||||
username_blurb: "Vos amis paient votre nom au lieu d'une longue clé. Facultatif — réclamez-en un à tout moment."
|
||||
username_field_hint: "votrenom"
|
||||
working: "En cours…"
|
||||
claim_username: "Réserver le nom d'utilisateur"
|
||||
available_when_connected: "Disponible une fois le mixnet connecté — ou passez et réservez plus tard."
|
||||
youre: "Vous êtes %{name}"
|
||||
claimed_title: "%{name} est à vous"
|
||||
claimed_blurb: "Vos amis peuvent désormais vous payer par votre nom. Tout est prêt — ouvrez votre portefeuille."
|
||||
open_wallet: "Ouvrir mon portefeuille"
|
||||
skip_for_now: "Passer pour l'instant"
|
||||
import_existing: "Vous avez déjà une identité Goblin ? Importez-la"
|
||||
import_title: "Importer votre identité"
|
||||
import_blurb: "Collez votre nsec ou choisissez un fichier .backup pour conserver votre clé et votre nom existants au lieu de ce nouveau."
|
||||
errors:
|
||||
cant_open: "Impossible d'ouvrir le portefeuille : %{err}"
|
||||
cant_create: "Impossible de créer le portefeuille : %{err}"
|
||||
send:
|
||||
scan_to_request: "Scanner pour demander"
|
||||
scan_to_pay: "Scanner pour payer"
|
||||
tab_scan: "Scanner"
|
||||
tab_my_code: "Mon code"
|
||||
request_from: "Demander à"
|
||||
send_to: "Envoyer à"
|
||||
search_hint: "handle, npub ou nom"
|
||||
suggested: "%{icon} Suggéré"
|
||||
no_contacts: "Aucun contact pour l'instant. Trouvez quelqu'un par son handle."
|
||||
no_profile: "pas de profil"
|
||||
tag_contact: "contact"
|
||||
tag_on_nostr: "sur nostr"
|
||||
searching_nostr: "Recherche sur nostr…"
|
||||
unverified_title: "Payer une clé non vérifiée ?"
|
||||
unverified_body: "Aucun profil nostr n'est publié pour cette clé — elle peut être toute neuve, anonyme ou mal saisie. Vérifiez bien qu'il s'agit de la bonne avant d'envoyer."
|
||||
keep_looking: "Continuer à chercher"
|
||||
pay_anyway: "Payer quand même"
|
||||
scan_not_recipient: "Ce QR n'est pas un destinataire goblin — un npub ou handle est attendu"
|
||||
scan_prompt: "Placez un code goblin dans le champ pour activer"
|
||||
scan_to_pay_me: "Scannez pour me payer"
|
||||
share_btn: "%{icon} Partager"
|
||||
share_message: "Payez-moi sur Goblin — %{handle}\n%{link}\nnpub : %{npub}"
|
||||
none_found: "Personne trouvé pour %{label}"
|
||||
enter_recipient: "Saisissez un handle, un npub ou un nom"
|
||||
amount_title: "Montant"
|
||||
to_name: "À %{name}"
|
||||
not_enough: "Vous n'avez pas assez de grin"
|
||||
max: "Max"
|
||||
note_label: "Note"
|
||||
note_hint: "Ajouter une note…"
|
||||
add_note: "Ajouter une note"
|
||||
edit_note: "Modifier la note"
|
||||
note_cancel: "Annuler"
|
||||
note_save: "Enregistrer"
|
||||
review_btn: "Vérifier"
|
||||
confirm_request: "Confirmer la demande"
|
||||
review_title: "Vérification"
|
||||
requesting_from: "Demande à %{name}"
|
||||
youre_sending: "Vous envoyez %{name}"
|
||||
row_from: "De"
|
||||
row_to: "À"
|
||||
row_note: "Note"
|
||||
row_they_pay: "Ils paient"
|
||||
row_they_pay_val: "Seulement s'ils approuvent"
|
||||
row_delivery: "Livraison"
|
||||
row_delivery_val: "Chiffré NIP-44, via Nym"
|
||||
row_network_fee: "Frais de réseau"
|
||||
row_network_fee_val: "Déduit de votre solde"
|
||||
row_privacy: "Confidentialité"
|
||||
row_privacy_val: "Mimblewimble + Nym"
|
||||
send_request_btn: "Envoyer la demande"
|
||||
request_approve_hint: "Ils recevront une demande à approuver"
|
||||
hold_to_send: "Maintenir pour envoyer"
|
||||
lower_amount: "Revenez et baissez le montant"
|
||||
hold_confirm_hint: "Appuyez et maintenez pour confirmer"
|
||||
requesting: "Demande en cours…"
|
||||
sending: "Envoi…"
|
||||
they: "Ils"
|
||||
request_blocked: "%{who} n'accepte pas les demandes. Demandez-lui de vous envoyer des grin à la place."
|
||||
failed_request_title: "Échec de la demande"
|
||||
failed_send_title: "Échec de l'envoi"
|
||||
failed_request_body: "Impossible de livrer la demande. Demandez-lui de vous envoyer des grin à la place."
|
||||
failed_send_body: "Le paiement n'a pas été livré. Vos grin sont en sécurité — réessayez."
|
||||
try_again_btn: "Réessayer"
|
||||
close_btn: "Fermer"
|
||||
success:
|
||||
requested: "Demandé"
|
||||
sent: "Envoyé"
|
||||
from: "de"
|
||||
to: "à"
|
||||
subtitle: "%{dir} %{who} · à l'instant"
|
||||
done_btn: "Terminé"
|
||||
receipt_btn: "Reçu"
|
||||
@@ -0,0 +1,808 @@
|
||||
lang_name: Русский
|
||||
copy: Копировать
|
||||
paste: Вставить
|
||||
continue: Продолжить
|
||||
complete: Завершить
|
||||
error: Ошибка
|
||||
retry: Повторить
|
||||
close: Закрыть
|
||||
change: Изменить
|
||||
show: Показать
|
||||
delete: Удалить
|
||||
clear: Очистить
|
||||
create: Создать
|
||||
id: Идентификатор
|
||||
kernel: Ядро
|
||||
settings: Настройки
|
||||
language: Язык
|
||||
scan: Сканировать
|
||||
qr_code: QR-код
|
||||
scan_qr: Сканирование QR-кода
|
||||
repeat: Повторить
|
||||
scan_result: Результат сканирования
|
||||
back: Назад
|
||||
share: Поделиться
|
||||
theme: 'Тема:'
|
||||
dark: Тёмная
|
||||
light: Светлая
|
||||
file: Файл
|
||||
choose_file: Выбрать файл
|
||||
choose_folder: Выбрать папку
|
||||
crash_report: Отчёт о сбое
|
||||
crash_report_warning: В прошлый раз приложение неожиданно закрылось, вы можете поделиться отчетом о сбое с разработчиками.
|
||||
confirmation: Подтверждение
|
||||
enter_url: Введите URL-адрес
|
||||
max_short: МАКС
|
||||
files_location: Расположение файлов
|
||||
moving_files: Перемещение файлов
|
||||
wrong_path_error: Указан неправильный путь
|
||||
check_updates: Проверять обновления при запуске
|
||||
update_available: Доступно обновление!
|
||||
changelog: 'Журнал изменений:'
|
||||
wallets:
|
||||
await_conf_amount: Ожидает подтверждения
|
||||
await_fin_amount: Ожидает завершения
|
||||
locked_amount: Заблокировано
|
||||
txs_empty: 'Для получения средств вручную или через транспорт используйте кнопки %{message} или %{transport} внизу экрана, для изменения настроек кошелька нажмите кнопку %{settings}.'
|
||||
title: Goblin
|
||||
create_desc: Создайте или импортируйте существующий кошелёк из сохранённой фразы восстановления.
|
||||
add: Добавить кошелёк
|
||||
name: 'Название:'
|
||||
pass: 'Пароль:'
|
||||
pass_empty: Введите пароль от кошелька
|
||||
current_pass: 'Текущий пароль:'
|
||||
new_pass: 'Новый пароль:'
|
||||
min_tx_conf_count: 'Минимальное количество подтверждений для транзакций:'
|
||||
recover: Восстановить
|
||||
recovery_phrase: Фраза восстановления
|
||||
words_count: 'Количество слов:'
|
||||
enter_word: 'Введите слово #%{number}:'
|
||||
not_valid_word: Введено недопустимое слово
|
||||
not_valid_phrase: Введена недопустимая фраза восстановления
|
||||
create_phrase_desc: Безопасно запишите и сохраните вашу фразу восстановления.
|
||||
restore_phrase_desc: Введите слова из вашей сохранённой фразы восстановления.
|
||||
setup_conn_desc: Выберите способ подключения вашего кошелька к сети.
|
||||
conn_method: Способ подключения
|
||||
ext_conn: 'Внешние подключения:'
|
||||
add_node: Добавить узел
|
||||
node_url: 'URL узла:'
|
||||
node_secret: 'API токен (необязательно):'
|
||||
invalid_url: Введённый URL-адрес недействителен
|
||||
open: Открыть кошелёк
|
||||
wrong_pass: Введён неправильный пароль
|
||||
locked: Заблокирован
|
||||
unlocked: Разблокирован
|
||||
enable_node: 'Чтобы использовать кошелёк, включите встроенный узел или измените настройки подключения, выбрав %{settings} внизу экрана.'
|
||||
node_loading: 'Кошелёк будет загружен после синхронизации встроенного узла, вы можете изменить подключение, выбрав %{settings} внизу экрана.'
|
||||
loading: Загружается
|
||||
closing: Закрывается
|
||||
checking: Проверяется
|
||||
default_wallet: Стандартный кошелёк
|
||||
new_account_desc: 'Введите название нового аккаунта:'
|
||||
wallet_loading: Загрузка кошелька
|
||||
wallet_closing: Закрытие кошелька
|
||||
wallet_checking: Проверка кошелька
|
||||
tx_loading: Загрузка транзакций
|
||||
default_account: Стандартный аккаунт
|
||||
accounts: Аккаунты
|
||||
tx_sent: Отправлено
|
||||
tx_received: Получено
|
||||
tx_sending: Отправка
|
||||
tx_receiving: Получение
|
||||
tx_confirming: Ожидает подтверждения
|
||||
tx_canceled: Отменено
|
||||
tx_cancelling: Отмена
|
||||
tx_finalizing: Завершение
|
||||
tx_posting: Публикация
|
||||
tx_confirmed: Подтверждено
|
||||
txs: Транзакции
|
||||
tx: Транзакция
|
||||
messages: Сообщения
|
||||
transport: Транспорт
|
||||
input_slatepack_desc: 'Введите сообщение для создания ответа или завершения запроса:'
|
||||
parse_slatepack_err: 'Во время чтения сообщения произошла ошибка, проверьте входные данные:'
|
||||
pay_balance_error: 'Средств на аккаунте недостаточно для оплаты %{amount} ツ и комиссии сети.'
|
||||
parse_i1_slatepack_desc: 'Для оплаты %{amount} ツ отправьте это сообщение получателю:'
|
||||
parse_i2_slatepack_desc: 'Завершите транзакцию для получения %{amount} ツ:'
|
||||
parse_i3_slatepack_desc: 'Опубликуйте транзакцию для завершения получения %{amount} ツ:'
|
||||
parse_s1_slatepack_desc: 'Для получения %{amount} ツ отправьте это сообщение отправителю:'
|
||||
parse_s2_slatepack_desc: 'Завершите транзакцию для отправки %{amount} ツ:'
|
||||
parse_s3_slatepack_desc: 'Опубликуйте транзакцию для завершения отправки %{amount} ツ:'
|
||||
resp_slatepack_err: 'Во время создания ответа произошла ошибка, проверьте входные данные или повторите попытку:'
|
||||
resp_exists_err: Такая транзакция уже существует.
|
||||
resp_canceled_err: Такая транзакция уже была отменена.
|
||||
create_request_desc: 'Запрос на отправку или получение средств:'
|
||||
send_request_desc: 'Вы создали запрос на отправку %{amount} ツ. Отправьте это сообщение получателю:'
|
||||
send_slatepack_err: Во время создания запроса на отправку средств произошла ошибка, проверьте входные данные или повторите попытку.
|
||||
invoice_desc: 'Вы создали запрос на получение %{amount} ツ. Отправьте это сообщение отправителю:'
|
||||
invoice_slatepack_err: Во время выставления счёта произошла ошибка, проверьте входные данные или повторите попытку.
|
||||
finalize_slatepack_err: 'Во время завершения произошла ошибка, проверьте входные данные или повторите попытку:'
|
||||
finalize: Завершить
|
||||
use_dandelion: Использовать Dandelion
|
||||
enter_amount_send: 'У вас есть %{amount} ツ. Введите количество для отправки:'
|
||||
enter_amount_receive: 'Введите количество для получения:'
|
||||
recovery: Восстановление
|
||||
repair_wallet: Исправить кошелёк
|
||||
repair_desc: Проверить кошелёк, исправляя и восстанавливая недостающие выходы, если это необходимо. Эта операция займёт время.
|
||||
repair_unavailable: Необходимо активное подключение к узлу и завершённая синхронизация кошелька.
|
||||
delete: Удалить кошелёк
|
||||
delete_conf: Вы уверены, что хотите удалить кошелек?
|
||||
delete_desc: Убедитесь, что вы сохранили вашу фразу восстановления, чтобы получить доступ к средствам.
|
||||
wallet_loading_err: 'Во время синхронизации кошелька произошла ошибка, вы можете повторить попытку или изменить настройки подключения, выбрав %{settings} внизу экрана.'
|
||||
wallet: Кошелёк
|
||||
send: Отправить
|
||||
receive: Получить
|
||||
settings: Настройки кошелька
|
||||
tx_send_cancel_conf: 'Вы действительно хотите отменить отправку %{amount} ツ?'
|
||||
tx_receive_cancel_conf: 'Вы действительно хотите отменить получение %{amount} ツ?'
|
||||
rec_phrase_not_found: Фраза восстановления не найдена.
|
||||
restore_wallet_desc: Восстановить кошелёк, удалив все файлы, если обычное исправление не помогло. Необходимо переоткрыть кошелёк.
|
||||
fee_base_desc: 'Комиссия (базовое значение%{value}):'
|
||||
payment_proof: Подтверждение оплаты
|
||||
payment_proof_desc: 'Введите полученное подтверждение оплаты для проверки транзакции:'
|
||||
payment_proof_valid: 'Введённое подтверждение оплаты действительно:'
|
||||
payment_proof_error: 'Введённое подтверждение оплаты недействительно:'
|
||||
tx_delete_confirmation: Вы уверены, что хотите удалить транзакцию из истории?
|
||||
transport:
|
||||
desc: 'Используйте транспорт для синхронных получения или отправки сообщений:'
|
||||
tor_network: Сеть Tor
|
||||
connected: Подключено
|
||||
connecting: Подключение
|
||||
disconnecting: Отключение
|
||||
conn_error: Ошибка подключения
|
||||
disconnected: Отключено
|
||||
receiver_address: 'Адрес получателя:'
|
||||
incorrect_addr_err: 'Введённый адрес неверен:'
|
||||
tor_send_error: Во время отправки через Tor произошла ошибка, убедитесь, что получатель находится онлайн, транзакция была отменена.
|
||||
tor_autorun_desc: Запускать ли Tor сервис при открытии кошелька для синхронного получения транзакций.
|
||||
tor_sending: Отправка через Tor
|
||||
tor_settings: Настройки Tor
|
||||
bridges: Мосты
|
||||
bridges_desc: Настройте мосты для обхода цензуры сети Tor, если обычное соединение не работает.
|
||||
bin_file: 'Исполняемый файл:'
|
||||
conn_line: 'Строка подключения:'
|
||||
bridges_disabled: Мосты отключены
|
||||
bridge_name: 'Мост %{b}'
|
||||
network:
|
||||
self: Сеть
|
||||
type: 'Тип сети:'
|
||||
mainnet: Основная
|
||||
testnet: Тестовая
|
||||
connections: Подключения
|
||||
node: Встроенный узел
|
||||
metrics: Показатели
|
||||
mining: Майнинг
|
||||
settings: Настройки узла
|
||||
enable_node: Включить узел
|
||||
autorun: Автозапуск
|
||||
disabled_server: 'Включите встроенный узел или добавьте другой способ подключения, нажав %{dots} в левом-верхнем углу экрана.'
|
||||
no_ips: В вашей системе отсутствуют доступные IP адреса, запуск сервера невозможен, проверьте ваше подключение к сети.
|
||||
available: Доступно
|
||||
not_available: Недоступно
|
||||
availability_check: Проверка доступности
|
||||
android_warning: Вниманию пользователей Android. Для успешной синхронизации встроенного узла необходимо разрешить доступ к уведомлениям и снять ограничения на использование батареи для приложения Grim в настройках телефона. Это необходимая операция для корректной работы приложения в фоне.
|
||||
sync_status:
|
||||
node_restarting: Узел перезапускается
|
||||
node_down: Узел выключен
|
||||
initial: Запуск узла
|
||||
no_sync: Узел запущен
|
||||
awaiting_peers: Ожидание пиров
|
||||
header_sync: Загрузка заголовков
|
||||
header_sync_percent: 'Загрузка заголовков: %{percent}%'
|
||||
tx_hashset_pibd: Загрузка состояния (PIBD)
|
||||
tx_hashset_pibd_percent: 'Загрузка состояния (PIBD): %{percent}%'
|
||||
tx_hashset_download: Загрузка состояния
|
||||
tx_hashset_download_percent: 'Загрузка состояния: %{percent}%'
|
||||
tx_hashset_setup_history: 'Подготовка состояния (история): %{percent}%'
|
||||
tx_hashset_setup_position: 'Подготовка состояния (позиция): %{percent}%'
|
||||
tx_hashset_setup: Подготовка состояния
|
||||
tx_hashset_range_proofs_validation: 'Проверка доказательств: %{percent}%'
|
||||
tx_hashset_kernels_validation: 'Проверка ядер: %{percent}%'
|
||||
tx_hashset_save: Сохранение состояния цепи
|
||||
body_sync: Загрузка блоков
|
||||
body_sync_percent: 'Загрузка блоков: %{percent}%'
|
||||
shutdown: Выключение узла
|
||||
network_node:
|
||||
header: Заголовок
|
||||
block: Блок
|
||||
hash: Хэш
|
||||
height: Высота
|
||||
difficulty: Сложность
|
||||
time: Время
|
||||
main_pool: Основной пул
|
||||
stem_pool: Stem пул
|
||||
data: Данные
|
||||
size: Размер (ГБ)
|
||||
peers: Пиры
|
||||
error_clean: Данные узла повреждены, необходима повторная синхронизация.
|
||||
resync: Cинхронизация
|
||||
error_p2p_api: 'Во время инициализации %{p2p_api} сервера произошла ошибка, проверьте настройки %{p2p_api}, выбрав %{settings} внизу экрана.'
|
||||
error_config: 'Во время инициализации конфигурации произошла ошибка, проверьте настройки встроенного узла, выбрав %{settings} внизу экрана.'
|
||||
error_unknown: 'Во время инициализации произошла ошибка, проверьте настройки встроенного узла, выбрав %{settings} внизу экрана или очистите данные.'
|
||||
network_metrics:
|
||||
loading: Показатели будут доступны после синхронизации
|
||||
emission: Эмиссия
|
||||
inflation: Инфляция
|
||||
supply: Предложение
|
||||
block_time: Время блока
|
||||
reward: Награда
|
||||
difficulty_window: 'Окно сложности %{size}'
|
||||
network_mining:
|
||||
loading: Майнинг будет доступен после синхронизации
|
||||
info: 'Сервер майнинга запущен, вы можете изменить его настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.'
|
||||
restart_server_required: Для применения изменений потребуется перезапуск Stratum сервера.
|
||||
rewards_wallet: Кошелёк для наград
|
||||
server: Stratum сервер
|
||||
address: Адрес
|
||||
miners: Майнеры
|
||||
devices: Устройства
|
||||
blocks_found: Найдено блоков
|
||||
hashrate: 'Хешрэйт (C%{bits})'
|
||||
connected: Подключен
|
||||
disconnected: Отключен
|
||||
network_settings:
|
||||
change_value: Изменить значение
|
||||
stratum_ip: 'Stratum IP адрес:'
|
||||
stratum_port: 'Порт Stratum:'
|
||||
port_unavailable: Указанный порт недоступен
|
||||
restart_node_required: Для применения изменений требуется перезапуск узла.
|
||||
choose_wallet: Выбрать кошелёк
|
||||
stratum_wallet_warning: Кошелёк должен быть открыт для получения наград.
|
||||
enable: Включить
|
||||
disable: Выключить
|
||||
restart: Перезапуск
|
||||
server: Сервер
|
||||
api_ip: 'API IP адрес:'
|
||||
api_port: 'API порт:'
|
||||
api_secret: 'Rest и V2 Owner API токен:'
|
||||
foreign_api_secret: 'Foreign API токен:'
|
||||
disabled: Отключен
|
||||
enabled: Включен
|
||||
ftl: 'Предел Будущего Времени (FTL):'
|
||||
ftl_description: Насколько далеко в будущем, относительно локального времени узла в секундах, может находиться временная метка на новом блоке для его принятия.
|
||||
not_valid_value: Введено недопустимое значение
|
||||
full_validation: Полная валидация
|
||||
full_validation_description: Запускать ли полную проверку цепи при обработке каждого блока (за исключением синхронизации).
|
||||
archive_mode: Архивный режим
|
||||
archive_mode_desc: Запустить узел в режиме полного архива (потребуется больше места и времени для синхронизации).
|
||||
attempt_time: 'Время попытки майнинга (в секундах):'
|
||||
attempt_time_desc: Количество времени для попытки майнинга на определённом заголовке перед остановкой и повторным сбором транзакций из пула
|
||||
min_share_diff: 'Минимальная допустимая сложность шары:'
|
||||
reset_settings_desc: Сбросить настройки узла до стандартных значений
|
||||
reset_settings: Сброс настроек
|
||||
reset: Сбросить
|
||||
tx_pool: Пул транзакций
|
||||
pool_fee: 'Базовая комиссия, принимаемая в пул:'
|
||||
reorg_period: 'Срок хранения кэша реорганизации (в минутах):'
|
||||
max_tx_pool: 'Максимальное количество транзакций в пуле:'
|
||||
max_tx_stempool: 'Максимальное количество транзакций в stem-пуле:'
|
||||
max_tx_weight: 'Максимальный общий вес транзакций, которые могут быть выбраны для построения блока:'
|
||||
epoch_duration: 'Длительность эпохи (в секундах):'
|
||||
embargo_timer: 'Таймер эмбарго (в секундах):'
|
||||
aggregation_period: 'Период агрегации (в секундах):'
|
||||
stem_probability: 'Вероятность фазы Stem:'
|
||||
stem_txs: Stem транзакций
|
||||
p2p_server: P2P сервер
|
||||
p2p_port: 'P2P порт:'
|
||||
add_seed: Добавить DNS Seed
|
||||
seed_address: 'Адрес DNS Seed:'
|
||||
add_peer: Добавить пир
|
||||
peer_address: 'Адрес пира:'
|
||||
peer_address_error: 'Введите IP адрес или DNS имя (убедитесь, что указанный хост доступен) в правильном формате, например: 192.168.0.1:1234 или example.com:5678'
|
||||
default: По умолчанию
|
||||
allow_list: Белый список
|
||||
allow_list_desc: Подключаться только к пирам в данном списке.
|
||||
deny_list: Чёрный список
|
||||
deny_list_desc: Никогда не подключаться к пирам в данном списке.
|
||||
favourites: Избранное
|
||||
favourites_desc: Список предпочтительных пиров для подключения.
|
||||
ban_window: 'Сколько времени (в секундах) забаненый пир должен оставаться забаненым:'
|
||||
ban_window_desc: Решение о запрете принимается узлом, основываясь на корректности данных полученных от пира.
|
||||
max_inbound_count: 'Максимальное количество входящих подключений пиров:'
|
||||
max_outbound_count: 'Максимальное количество исходящих подключений к пирам:'
|
||||
reset_data_desc: Сбросить данные узла. Используйте с осторожностью, только при наличии проблем с синхронизацией.
|
||||
reset_data: Сброс данных
|
||||
modal:
|
||||
cancel: Отмена
|
||||
save: Сохранить
|
||||
add: Добавить
|
||||
modal_exit:
|
||||
description: Вы уверены, что хотите выйти из приложения?
|
||||
exit: Выход
|
||||
app_settings:
|
||||
proxy: Прокси
|
||||
proxy_desc: Стоит ли использовать прокси для сетевых запросов из приложения.
|
||||
keyboard:
|
||||
1: 1
|
||||
2: 2
|
||||
3: 3
|
||||
4: 4
|
||||
5: 5
|
||||
6: 6
|
||||
7: 7
|
||||
8: 8
|
||||
9: 9
|
||||
0: 0
|
||||
01: ъ
|
||||
q: й
|
||||
w: ц
|
||||
e: у
|
||||
r: к
|
||||
t: е
|
||||
y: н
|
||||
u: г
|
||||
i: ш
|
||||
o: щ
|
||||
p: з
|
||||
p1: х
|
||||
a: ф
|
||||
s: ы
|
||||
d: в
|
||||
f: а
|
||||
g: п
|
||||
h: р
|
||||
j: о
|
||||
k: л
|
||||
l: д
|
||||
l1: ж
|
||||
l2: э
|
||||
z: я
|
||||
x: ч
|
||||
c: с
|
||||
v: м
|
||||
b: и
|
||||
n: т
|
||||
m: ь
|
||||
m1: б
|
||||
m2: ю
|
||||
m3: ё
|
||||
goblin:
|
||||
home:
|
||||
anonymous: "Аноним"
|
||||
connected_nym: "Подключено через Nym"
|
||||
nym_ready: "Nym готов · реле…"
|
||||
connecting_nym: "Подключение к Nym…"
|
||||
cant_reach_node: "Нет связи с узлом"
|
||||
node_synced: "Узел синхронизирован"
|
||||
syncing: "Синхронизация…"
|
||||
block: "Блок %{height}"
|
||||
waiting_for_chain: "Ожидание цепочки…"
|
||||
nav_wallet: "Кошелёк"
|
||||
nav_pay: "Оплатить"
|
||||
nav_activity: "Действия"
|
||||
nav_receive: "Получить"
|
||||
nav_settings: "Настройки"
|
||||
activity: "Действия"
|
||||
empty_title: "Пока нет действий"
|
||||
empty_sub: "Отправьте или получите grin, чтобы начать."
|
||||
recent: "Недавние"
|
||||
scan_to_pay: "Сканируйте для оплаты"
|
||||
type_amount: "Введите сумму"
|
||||
request: "Запросить"
|
||||
pay: "Оплатить"
|
||||
enter_amount: "Введите сумму для оплаты или запроса"
|
||||
activity:
|
||||
canceled: "отменено"
|
||||
pending: "в ожидании"
|
||||
earlier: "Ранее"
|
||||
today: "Сегодня"
|
||||
yesterday: "Вчера"
|
||||
title: "Действия"
|
||||
requests: "Запросы"
|
||||
empty_title: "Пока нет действий"
|
||||
empty_sub: "Здесь появятся ваши платежи."
|
||||
pending_header: "В ожидании"
|
||||
receipt:
|
||||
title: "Квитанция"
|
||||
not_found: "Транзакция не найдена"
|
||||
for_note: "За %{note}"
|
||||
details: "Детали транзакции"
|
||||
canceled: "Отменено"
|
||||
expired: "Истекло"
|
||||
funds_returned: "Средства возвращены"
|
||||
complete: "Завершено"
|
||||
payment_received: "Платёж получен"
|
||||
payment_sent: "Платёж успешно отправлен"
|
||||
pending: "В ожидании"
|
||||
confs: "%{c}/%{r} подтверждений"
|
||||
waiting_to_confirm: "Ожидание подтверждения"
|
||||
paying: "Оплата…"
|
||||
you: "Вы"
|
||||
to: "Кому"
|
||||
from: "От"
|
||||
nostr: "nostr"
|
||||
fee_none: "Нет"
|
||||
network_fee: "Сетевая комиссия"
|
||||
privacy: "Приватность"
|
||||
privacy_value: "Mimblewimble + Nym"
|
||||
transaction: "Транзакция"
|
||||
cancel_request: "Отменить запрос"
|
||||
cancel_send: "Отменить платёж"
|
||||
cancel_send_confirm: "Нажмите ещё раз для отмены — он ещё может его получить"
|
||||
cancel_send_done: "Платёж отменён — ваши средства снова доступны"
|
||||
cancel_send_too_late: "Этот платёж уже прошёл и не может быть отменён"
|
||||
waiting_to_receive: "Ожидание, пока %{name} получит…"
|
||||
request:
|
||||
title: "%{name} запрашивает"
|
||||
approve: "Принять"
|
||||
decline: "Отклонить"
|
||||
review_title: "Проверить запрос"
|
||||
hold_to_accept: "Удерживайте, чтобы принять"
|
||||
hold_accept_hint: "Нажмите и удерживайте, чтобы оплатить запрос"
|
||||
receive:
|
||||
title: "Получить"
|
||||
requesting: "Запрос %{amt}%{tsu} — поделитесь, чтобы получить оплату"
|
||||
clear_request: "Очистить запрос"
|
||||
share_handle: "Поделитесь именем, чтобы получить оплату"
|
||||
copied: "Скопировано"
|
||||
copy_nostr_id: "Копировать nostr ID"
|
||||
copy_address: "Копировать адрес"
|
||||
copy_npub: "Копировать npub"
|
||||
share_message: "Заплатите мне в Goblin (goblin.st) — %{npub}"
|
||||
privacy_note: "Ваше имя публично. Содержимое платежей остаётся зашифрованным в сети."
|
||||
profile:
|
||||
title: "Профиль"
|
||||
activity: "Действия"
|
||||
no_activity: "Пока нет действий с ними."
|
||||
unblock: "Разблокировать"
|
||||
block: "Заблокировать"
|
||||
blocked_blurb: "Заблокирован — их платежи и запросы отклоняются."
|
||||
block_blurb: "Блокировка отклоняет их входящие платежи и запросы."
|
||||
settings:
|
||||
title: "Настройки"
|
||||
connected_nostr: "Подключено к nostr"
|
||||
connecting_relays: "Подключение к реле…"
|
||||
identity: "Личность"
|
||||
copy_npub: "Копировать npub (публичный)"
|
||||
rotate_key: "Сменить ключ nostr"
|
||||
import_identity: "Импорт личности (.backup / nsec)"
|
||||
backup_note: "Меняете устройство? Сохраните ОБА: seed-фразу (средства) и файл .backup личности (имя + ключ)."
|
||||
wallet: "Кошелёк"
|
||||
display_unit: "Единица отображения"
|
||||
relays: "Реле"
|
||||
node: "Узел"
|
||||
slatepacks: "Slatepacks"
|
||||
slatepacks_value: "Ручная транзакция"
|
||||
lock_wallet: "Заблокировать кошелёк"
|
||||
switch_wallet: "Сменить кошелёк"
|
||||
advanced: "Дополнительно"
|
||||
privacy: "Приватность"
|
||||
mixnet_routing: "Маршрутизация через mixnet"
|
||||
messages_lookups: "Сообщения и поиск"
|
||||
auto_accept: "Автоприём"
|
||||
pairing: "Привязка"
|
||||
accept_anyone: "Любой"
|
||||
accept_contacts: "Только контакты"
|
||||
accept_ask: "Всегда спрашивать"
|
||||
requests: "Запросы"
|
||||
incoming_requests: "Входящие запросы"
|
||||
incoming_requests_sub: "Разрешить другим запрашивать у вас деньги"
|
||||
appearance: "Внешний вид"
|
||||
theme: "Тема"
|
||||
theme_light: "Светлая"
|
||||
theme_dark: "Тёмная"
|
||||
theme_yellow: "Жёлтая"
|
||||
archive: "Архив"
|
||||
export_archive: "Экспорт архива"
|
||||
wipe_history: "Стереть историю платежей"
|
||||
about: "О приложении"
|
||||
goblin: "Goblin"
|
||||
build: "Сборка %{build}"
|
||||
network: "Сеть"
|
||||
network_value: "MW + mixnet Nym + nostr"
|
||||
third_party: "Сторонние"
|
||||
grim: "GRIM (исходный кошелёк)"
|
||||
grin_node: "Узел Grin"
|
||||
sp_intro: "Для опытных — обмен сырыми slatepacks вручную, как в GRIM. Используйте только если не можете платить или получать через username."
|
||||
sp_receive_group: "Получить или завершить"
|
||||
sp_receive_blurb: "Вставьте slatepack, который вам дали. Goblin получит платёж, оплатит счёт или завершит и опубликует его."
|
||||
sp_process: "Обработать slatepack"
|
||||
sp_paste_first: "Сначала вставьте slatepack."
|
||||
sp_reply_ready: "Ответ готов — отправьте его обратно отправителю."
|
||||
sp_finalizing: "Завершение и публикация в цепочку…"
|
||||
sp_create_group: "Создать платёж"
|
||||
sp_create_blurb: "Создайте slatepack для передачи кому-то. Они получат его, отправят ответ, а вы завершите его выше."
|
||||
sp_amount_hint: "Сумма в grin"
|
||||
sp_addr_hint: "Адрес получателя (необязательно)"
|
||||
sp_create: "Создать slatepack"
|
||||
sp_ready: "Slatepack готов — передайте его получателю."
|
||||
sp_amount_gt_zero: "Введите сумму больше нуля."
|
||||
sp_to_send: "Slatepack для отправки"
|
||||
sp_copy: "Копировать slatepack"
|
||||
rotate_line1: "• Вы получите совершенно новый СЛУЧАЙНЫЙ ключ; старый npub перестанет принимать. Между ними нет цепочки вывода."
|
||||
rotate_line2: "• Новый ключ НЕЛЬЗЯ восстановить из seed — сохраните новый nsec сразу после смены."
|
||||
rotate_line3: "• Ваше имя пользователя ОСВОБОЖДАЕТСЯ — сразу после заявите то же или новое имя (как только свободно, его может занять кто угодно)."
|
||||
rotate_line4: "• Платежи, всё ещё идущие к старому ключу, БУДУТ нарушены — сначала дождитесь завершения ожидающих платежей."
|
||||
rotate_line5: "• Контакты, сохранившие ваш npub напрямую, должны найти вас заново — поделитесь новым npub или заново занятым username."
|
||||
cancel: "Отмена"
|
||||
continue: "Продолжить"
|
||||
final_confirmation: "Финальное подтверждение"
|
||||
rotate_confirm_blurb: "Это нельзя отменить из приложения. Введите RESET и пароль кошелька, чтобы сменить."
|
||||
type_reset: "Введите RESET"
|
||||
wallet_password: "Пароль кошелька"
|
||||
rotate_key_btn: "Сменить ключ"
|
||||
rotating_key: "Смена ключа…"
|
||||
key_rotated: "Ключ сменён"
|
||||
new_npub: "Новый npub: %{npub}"
|
||||
backup_new_key: "Сохраните НОВЫЙ секретный ключ сейчас — seed не сможет его восстановить."
|
||||
copy_new_nsec: "Копировать резерв нового nsec"
|
||||
done: "Готово"
|
||||
rotation_failed: "Смена не удалась"
|
||||
close: "Закрыть"
|
||||
import_identity_title: "Импорт личности"
|
||||
import_blurb: "Заменяет nostr-личность этого кошелька — выберите файл GOBLIN .backup или вставьте nsec. Резервная копия также восстанавливает имя и историю. Сначала сохраните текущий ключ, если он ещё нужен."
|
||||
import_nsec_hint: "nsec1… или вставленная копия"
|
||||
backup_password_hint: "Пароль резерва (только если экспортирован в другом месте)"
|
||||
import_btn: "Импорт"
|
||||
importing: "Импорт…"
|
||||
identity_replaced: "Личность заменена"
|
||||
now_using: "Сейчас используется: %{npub}"
|
||||
import_failed: "Импорт не удался"
|
||||
name_authority: "Сервер имён"
|
||||
name_authority_title: "Сменить сервер имён"
|
||||
name_authority_blurb: "Сервер, который регистрирует и проверяет имена. Укажите другой инстанс, чтобы использовать и оплачивать имена оттуда."
|
||||
name_authority_invalid: "Введите полный URL (https://…)."
|
||||
reset: "Сброс"
|
||||
save: "Сохранить"
|
||||
backup_file: "Сохранить в файл"
|
||||
choose_backup_file: "Выбрать файл .backup"
|
||||
backup_read_failed: "Не удалось прочитать файл."
|
||||
backup_saved: "Резервная копия сохранена"
|
||||
backup_saved_sub: "Храните файл .backup в безопасности — любой, у кого есть он И ваш пароль, может восстановить вашу личность."
|
||||
backup_file_title: "Резервная копия личности"
|
||||
backup_file_blurb: "Создаёт один зашифрованный файл .backup с именем и ключом. Введите пароль кошелька, чтобы запечатать его."
|
||||
backup_write_failed: "Не удалось сохранить файл."
|
||||
create_backup: "Создать копию"
|
||||
registered: "Зарегистрировано %{name}"
|
||||
released_msg: "Освобождено — имя свободно для занятия"
|
||||
release_confirm: "Освободить %{name}?"
|
||||
release_blurb: "Как только оно свободно, его можно занять — кто угодно, включая ваш следующий ключ. Вы не сможете зарегистрировать другое имя в течение 10 минут."
|
||||
releasing: "Освобождение…"
|
||||
keep_it: "Оставить"
|
||||
release_it: "Освободить"
|
||||
username: "Имя пользователя"
|
||||
username_note: "Показывается как you. Публично на goblin.st. Платежи остаются зашифрованными."
|
||||
release_username: "Освободить имя"
|
||||
pick_username: "Выберите имя — необязательно"
|
||||
working: "Обработка…"
|
||||
claim: "Занять"
|
||||
err_just_taken: "Это имя только что заняли"
|
||||
err_cooldown: "Вы недавно освободили имя — можно зарегистрировать новое в течение 10 минут."
|
||||
err_unreachable: "Не удалось связаться с goblin.st — сбой соединения. Попробуйте снова."
|
||||
err_release: "Не удалось освободить: %{err}"
|
||||
avail_available: "Доступно!"
|
||||
avail_taken: "Занято"
|
||||
avail_reserved: "Зарезервировано"
|
||||
avail_invalid: "Имена 3–20 символов: a–z, 0–9, _ или -"
|
||||
avail_quarantined: "Недоступно"
|
||||
avail_unknown: "Не удалось проверить — сбой соединения. Попробуйте снова."
|
||||
advanced:
|
||||
title: "Дополнительно"
|
||||
intro: "Низкоуровневые инструменты кошелька из GRIM. Обычно они вам не нужны."
|
||||
own_node_desc: "Синхронизируйте полный узел Grin на этом устройстве вместо доверия публичному."
|
||||
own_node_active: "Ваш узел запущен"
|
||||
repair: "Починить кошелёк"
|
||||
repair_desc: "Повторно просканировать цепочку и восстановить недостающие выходы. Это может занять время."
|
||||
repair_unavailable: "Сначала нужно синхронизированное подключение к узлу."
|
||||
repairing: "Починка… %{pct}%"
|
||||
restore: "Восстановить кошелёк"
|
||||
restore_desc: "Удалить локальные данные и пересоздать из seed-фразы. Используйте, если починка не помогла — после этого откройте кошелёк заново."
|
||||
restore_confirm: "Нажмите ещё раз для восстановления"
|
||||
show_phrase: "Фраза восстановления"
|
||||
phrase_desc: "Ваши 24 seed-слова grin — единственный способ восстановить средства. Храните их офлайн и в тайне."
|
||||
reveal: "Показать фразу"
|
||||
hide: "Скрыть"
|
||||
password: "Пароль кошелька"
|
||||
wrong_password: "Неверный пароль."
|
||||
delete: "Удалить кошелёк"
|
||||
delete_desc: "Безвозвратно удалить этот кошелёк с этого устройства. Без seed-фразы средства не восстановить."
|
||||
delete_confirm: "Нажмите ещё раз для удаления"
|
||||
manage_node: "Управление подключением к узлу"
|
||||
repair_confirm: "Да, восстановить сейчас"
|
||||
repair_confirm_note: "Восстановление повторно сканирует цепочку и может занять несколько минут."
|
||||
restore_confirm_note: "Это стирает локальные данные и восстанавливает их из seed-фразы — может занять несколько минут."
|
||||
privacy:
|
||||
title: "Сетевая приватность"
|
||||
intro: "Goblin отправляет приватный трафик через mixnet Nym — сеть из пяти переходов, скрывающую, кто с кем общается, чтобы реле не могло связать платёж с вами."
|
||||
payments: "Платежи"
|
||||
payments_blurb: "Каждое nostr-сообщение, несущее slatepack."
|
||||
usernames: "usernames"
|
||||
usernames_blurb: "Поиск имён NIP-05 к и от goblin.st."
|
||||
price_avatars: "Цена"
|
||||
price_avatars_blurb: "Текущий курс рядом с суммами."
|
||||
over_mixnet: "Через mixnet"
|
||||
direct_connection: "Прямое соединение"
|
||||
grin_node: "Узел Grin"
|
||||
grin_node_blurb: "Синхронизация блоков и трансляция транзакции в сеть. Это публичные данные цепочки, одинаковые для всех, и они не связаны с вашей личностью."
|
||||
pairing:
|
||||
title: "Привязка"
|
||||
intro: "К чему привязаны отображаемые баланс и суммы."
|
||||
pair_with: "Привязать к"
|
||||
rates_note: "Курсы загружаются через mixnet Nym только при включённой привязке — выключено означает, что запрос курса не покидает устройство."
|
||||
relays:
|
||||
title: "Реле"
|
||||
intro: "Сообщения о платежах дублируются на каждое реле ниже; для получения достаточно одного доступного реле."
|
||||
your_relays: "Ваши реле"
|
||||
add_relay: "Добавить реле"
|
||||
add_relay_btn: "Добавить реле"
|
||||
save_reconnect: "Сохранить и переподключить"
|
||||
none: "нет"
|
||||
count: "%{n} реле"
|
||||
node:
|
||||
title: "Узел"
|
||||
connection: "Соединение"
|
||||
integrated: "Встроенный узел"
|
||||
applies_after: "Применяется после блокировки и повторной разблокировки кошелька."
|
||||
add_external: "Добавить внешний узел"
|
||||
api_secret_hint: "API-секрет (необязательно)"
|
||||
add_node: "Добавить узел"
|
||||
integrated_host: "встроенный узел"
|
||||
summary_syncing: "%{conn} · синхронизация"
|
||||
summary_block: "Блок %{height} · %{conn}"
|
||||
nips:
|
||||
title: "nostr и NIPs"
|
||||
intro1: "Goblin говорит на nostr — открытом протоколе подписанных сообщений, передаваемых через простые реле-серверы. Ваш кошелёк несёт собственную nostr-личность: отдельный случайный ключ, намеренно независимый от ваших средств и seed. Каждый платёж идёт как сквозно зашифрованное личное сообщение между личностями, со slatepack внутри."
|
||||
intro2: "goblin.st — это служба имён Goblin: занятие имени публикует там сопоставление имя → ключ (NIP-05), чтобы вам платили на you вместо длинного npub. Имя публично; содержимое платежей — никогда. NIPs — это строительные блоки протокола; коснитесь одного, чтобы прочитать спецификацию."
|
||||
n05_title: "Имена"
|
||||
n05_blurb: "Сопоставляет username@goblin.st с вашим ключом, чтобы имена работали как адреса."
|
||||
n17_title: "Личные сообщения"
|
||||
n17_blurb: "Зашифрованный конверт DM, в котором идёт каждый платёж."
|
||||
n44_title: "Шифрование"
|
||||
n44_blurb: "Аутентифицированный шифр, используемый внутри этих сообщений."
|
||||
n49_title: "Шифрование ключа"
|
||||
n49_blurb: "Как секретный ключ хранится в покое, защищённый вашим паролем."
|
||||
n59_title: "Gift wrap"
|
||||
n59_blurb: "Оборачивает сообщения, чтобы реле не видели, кто с кем общается."
|
||||
n98_title: "HTTP-авторизация"
|
||||
n98_blurb: "Подписывает запрос регистрации имени на goblin.st."
|
||||
onboarding:
|
||||
intro:
|
||||
private_money_head: "Приватные деньги"
|
||||
private_money_body: "Goblin — кошелёк для grin: цифровая наличность без сумм и адресов в её цепочке."
|
||||
send_like_message_head: "Отправляйте как сообщение"
|
||||
send_like_message_body: "Заплатите на username или npub, и платёж придёт как сквозно зашифрованное сообщение через nostr и mixnet Nym — никто посередине не увидит сумму или участников."
|
||||
yours_alone_head: "Только ваше"
|
||||
yours_alone_body: "Ключи, имена и история живут на этом устройстве. На базе кошелька GRIM."
|
||||
get_started: "Начать"
|
||||
footnote: "Займёт около минуты. Всё можно изменить позже."
|
||||
node:
|
||||
kicker: "ШАГ 1 ИЗ 3 · СЕТЬ"
|
||||
title: "Как Goblin должен\nследить за цепочкой?"
|
||||
own_title: "Запустить свой узел"
|
||||
own_badge: "Приватно"
|
||||
own_body: "Никому не доверяет — ваш кошелёк проверяет цепочку сам. Синхронизируется в фоне, пока вы завершаете настройку."
|
||||
connect_title: "Подключиться к узлу"
|
||||
connect_badge: "Мгновенно"
|
||||
connect_body: "Без ожидания синхронизации. Выбранный узел может видеть запросы вашего кошелька."
|
||||
changeable: "Меняется в любой момент в Настройки → Узел."
|
||||
continue: "Продолжить"
|
||||
url_invalid: "URL узла должен начинаться с http:// или https://"
|
||||
wallet:
|
||||
kicker: "ШАГ 2 ИЗ 3 · КОШЕЛЁК"
|
||||
title: "Настройте кошелёк"
|
||||
create_new: "Создать новый"
|
||||
restore_from_seed: "Восстановить из seed"
|
||||
name_hint: "Имя кошелька"
|
||||
password_hint: "Пароль"
|
||||
repeat_password_hint: "Повторите пароль"
|
||||
restore_hint: "Подготовьте seed-слова — вы введёте их далее."
|
||||
create_hint: "Далее вы получите 24 seed-слова для записи. Они — это деньги: кто владеет ими, владеет вашими средствами."
|
||||
continue: "Продолжить"
|
||||
passwords_no_match: "Пароли не совпадают"
|
||||
words:
|
||||
kicker: "ШАГ 2 ИЗ 3 · КОШЕЛЁК"
|
||||
title_restore: "Введите seed-слова"
|
||||
title_create: "Запишите эти слова"
|
||||
write_down_hint: "На бумаге, по порядку. Любой с этими словами может забрать ваши средства; без них потеря устройства означает потерю средств."
|
||||
paste: "Вставить"
|
||||
scan_qr: "Сканировать QR"
|
||||
copy_clipboard: "Копировать в буфер (избегайте этого)"
|
||||
restore_wallet: "Восстановить кошелёк"
|
||||
wrote_them_down: "Я записал их"
|
||||
fill_every_word: "Заполните каждое слово — коснитесь слова для редактирования или вставьте фразу."
|
||||
confirm:
|
||||
kicker: "ШАГ 2 ИЗ 3 · КОШЕЛЁК"
|
||||
title: "Теперь подтвердите"
|
||||
enter_hint: "Введите слова, которые только что записали. Коснитесь слова, чтобы ввести его."
|
||||
paste: "Вставить"
|
||||
create_wallet: "Создать кошелёк"
|
||||
keep_going: "Продолжайте — каждое слово, по порядку."
|
||||
identity:
|
||||
kicker: "ШАГ 3 ИЗ 3 · ЛИЧНОСТЬ"
|
||||
title: "Ваша платёжная личность"
|
||||
key_being_made: "ключ создаётся…"
|
||||
connected_nym: "подключено через Nym"
|
||||
connecting_nym: "подключение через Nym…"
|
||||
fresh_key_blurb: "Платёжный ключ, не связанный с seed-фразой — меняйте его в любой момент, не трогая средства."
|
||||
clean_slate_blurb: "Хотите начать с чистого листа? Подставьте совершенно новый ключ в любой момент — новый вы не связан со старым. Тот же кошелёк, новое лицо."
|
||||
pick_username: "Выберите имя — необязательно"
|
||||
username_blurb: "Друзья платят на ваше имя, а не на длинный ключ. Необязательно — можно занять в любой момент."
|
||||
username_field_hint: "yourname"
|
||||
working: "Обработка…"
|
||||
claim_username: "Занять имя"
|
||||
available_when_connected: "Доступно после подключения mixnet — или пропустите и займите позже."
|
||||
youre: "Вы %{name}"
|
||||
claimed_title: "%{name} теперь ваше"
|
||||
claimed_blurb: "Друзья теперь могут платить вам по имени. Всё готово — откройте кошелёк."
|
||||
open_wallet: "Открыть кошелёк"
|
||||
skip_for_now: "Пропустить пока"
|
||||
import_existing: "Уже есть личность Goblin? Импортируйте её"
|
||||
import_title: "Импорт личности"
|
||||
import_blurb: "Вставьте свой nsec или выберите файл .backup, чтобы сохранить существующий ключ и имя вместо нового."
|
||||
errors:
|
||||
cant_open: "Не удалось открыть кошелёк: %{err}"
|
||||
cant_create: "Не удалось создать кошелёк: %{err}"
|
||||
send:
|
||||
scan_to_request: "Сканируйте для запроса"
|
||||
scan_to_pay: "Сканируйте для оплаты"
|
||||
tab_scan: "Сканировать"
|
||||
tab_my_code: "Мой код"
|
||||
request_from: "Запросить у"
|
||||
send_to: "Отправить"
|
||||
search_hint: "handle, npub или имя"
|
||||
suggested: "%{icon} Рекомендуемые"
|
||||
no_contacts: "Пока нет контактов. Найдите кого-то по их handle."
|
||||
no_profile: "нет профиля"
|
||||
tag_contact: "контакт"
|
||||
tag_on_nostr: "в nostr"
|
||||
searching_nostr: "Поиск в nostr…"
|
||||
unverified_title: "Заплатить непроверенному ключу?"
|
||||
unverified_body: "Для этого ключа не опубликован nostr-профиль — он может быть совсем новым, анонимным или с опечаткой. Дважды проверьте перед отправкой."
|
||||
keep_looking: "Продолжить поиск"
|
||||
pay_anyway: "Всё равно оплатить"
|
||||
scan_not_recipient: "Этот QR — не получатель goblin; ожидался npub или handle"
|
||||
scan_prompt: "Наведите на код goblin, чтобы активировать"
|
||||
scan_to_pay_me: "Сканируйте, чтобы заплатить мне"
|
||||
share_btn: "%{icon} Поделиться"
|
||||
share_message: "Заплатите мне в Goblin — %{handle}\n%{link}\nnpub: %{npub}"
|
||||
none_found: "Никого не найдено по %{label}"
|
||||
enter_recipient: "Введите handle, npub или имя"
|
||||
amount_title: "Сумма"
|
||||
to_name: "Кому %{name}"
|
||||
not_enough: "Недостаточно grin"
|
||||
max: "Макс"
|
||||
note_label: "Заметка"
|
||||
note_hint: "Добавить заметку…"
|
||||
add_note: "Добавить заметку"
|
||||
edit_note: "Изменить заметку"
|
||||
note_cancel: "Отмена"
|
||||
note_save: "Сохранить"
|
||||
review_btn: "Проверить"
|
||||
confirm_request: "Подтвердить запрос"
|
||||
review_title: "Проверка"
|
||||
requesting_from: "Запрос у %{name}"
|
||||
youre_sending: "Вы отправляете %{name}"
|
||||
row_from: "От"
|
||||
row_to: "Кому"
|
||||
row_note: "Заметка"
|
||||
row_they_pay: "Они платят"
|
||||
row_they_pay_val: "Только если они одобрят"
|
||||
row_delivery: "Доставка"
|
||||
row_delivery_val: "Зашифровано NIP-44, через Nym"
|
||||
row_network_fee: "Сетевая комиссия"
|
||||
row_network_fee_val: "Списывается с вашего баланса"
|
||||
row_privacy: "Приватность"
|
||||
row_privacy_val: "Mimblewimble + Nym"
|
||||
send_request_btn: "Отправить запрос"
|
||||
request_approve_hint: "Они получат запрос на одобрение"
|
||||
hold_to_send: "Удерживайте для отправки"
|
||||
lower_amount: "Вернуться и уменьшить сумму"
|
||||
hold_confirm_hint: "Нажмите и удерживайте для подтверждения"
|
||||
requesting: "Запрос…"
|
||||
sending: "Отправка…"
|
||||
they: "Они"
|
||||
request_blocked: "%{who} не принимает запросы. Попросите их отправить вам grin вместо этого."
|
||||
failed_request_title: "Не удалось запросить"
|
||||
failed_send_title: "Не удалось отправить"
|
||||
failed_request_body: "Не удалось доставить запрос. Попросите их отправить вам grin вместо этого."
|
||||
failed_send_body: "Платёж не доставлен. Ваш grin в безопасности — попробуйте снова."
|
||||
try_again_btn: "Попробовать снова"
|
||||
close_btn: "Закрыть"
|
||||
success:
|
||||
requested: "Запрошено"
|
||||
sent: "Отправлено"
|
||||
from: "от"
|
||||
to: "кому"
|
||||
subtitle: "%{dir} %{who} · только что"
|
||||
done_btn: "Готово"
|
||||
receipt_btn: "Квитанция"
|
||||
@@ -0,0 +1,808 @@
|
||||
lang_name: Türkçe
|
||||
copy: Kopyala
|
||||
paste: Yapistir
|
||||
continue: Devam
|
||||
complete: Tamamla
|
||||
error: Hata
|
||||
retry: Tekrar Dene
|
||||
close: Kapa
|
||||
change: Degistir
|
||||
show: Goster
|
||||
delete: Sil
|
||||
clear: Temizle
|
||||
create: Olustur
|
||||
id: Id
|
||||
kernel: Kernel
|
||||
settings: Ayarlar
|
||||
language: Dil
|
||||
scan: Scan
|
||||
qr_code: QR kod
|
||||
scan_qr: QR kod tara
|
||||
repeat: Tekrar
|
||||
scan_result: Tarama sonucu
|
||||
back: Geri
|
||||
share: Paylasmak
|
||||
theme: 'Tema:'
|
||||
dark: Karanlik
|
||||
light: Isik
|
||||
file: Dosya
|
||||
choose_file: Dosya seçin
|
||||
choose_folder: Klasör seç
|
||||
crash_report: Ariza Raporu
|
||||
crash_report_warning: Uygulama beklenmedik bir sekilde kapandi son kez, kilitlenme raporunu gelistiricilerle paylasabilirsiniz.
|
||||
confirmation: Onay
|
||||
enter_url: URL'yi girin
|
||||
max_short: MAKS
|
||||
files_location: Dosya konumu
|
||||
moving_files: Dosyalari Tasima
|
||||
wrong_path_error: Yanlis yol belirtildi
|
||||
check_updates: Başlangiçta güncellemeleri kontrol edin
|
||||
update_available: Güncelleme mevcut!
|
||||
changelog: 'Değişiklik Günlüğü:'
|
||||
wallets:
|
||||
await_conf_amount: Onay bekleniyor
|
||||
await_fin_amount: Tamamlanma bekleniyor
|
||||
locked_amount: Kilitli
|
||||
txs_empty: 'Koinleri al/gonder icin ekranin altinda bulunan %{receive} / %{send} sekmeleri, cuzdan ayarlar icin %{settings} sekmesini kullanin.'
|
||||
title: Goblin
|
||||
create_desc: Yeni cuzdan olustur veya var olan bakiyeli cuzdani kurtarma kelimelerinizle canlandirin.
|
||||
add: Cuzdan ekle
|
||||
name: 'Ad:'
|
||||
pass: 'Sifre:'
|
||||
pass_empty: Cuzdan Sifresini girin
|
||||
current_pass: Su anki sifre:'
|
||||
new_pass: 'Yeni sifre:'
|
||||
min_tx_conf_count: 'Tx islem için Minimum onay:'
|
||||
recover: Restore et
|
||||
recovery_phrase: Kurtarma kelimeleri
|
||||
words_count: 'Kelime sayisi:'
|
||||
enter_word: 'Kelimeyi gir #%{sira}:'
|
||||
not_valid_word: Girilen kelime yanlis
|
||||
not_valid_phrase: Girilen kurtarma kelimeleri gecerli degil
|
||||
create_phrase_desc: Kurtarma kelimelerini yazın ve mutlaka saklayin!
|
||||
restore_phrase_desc: Kaydettiginiz kurtarma kelimelerini girin.
|
||||
setup_conn_desc: Cuzdan baglanma metodu Sec.
|
||||
conn_method: Baglanti metodu
|
||||
ext_conn: 'Harici baglantilar:'
|
||||
add_node: Node ekle
|
||||
node_url: 'Node URL:'
|
||||
node_secret: 'API Secret (optional):'
|
||||
invalid_url: Girilen URL gecersiz
|
||||
open: Cuzdani Ac
|
||||
wrong_pass: Girilen sifre yanlis
|
||||
locked: Kilitli
|
||||
unlocked: Kilitsiz
|
||||
enable_node: 'Cuzdani kullanmak için Tumlesik node etkinlestirin veya ekranin altindaki %{settings} ogesini secerek baska baglanti metodu secin.'
|
||||
node_loading: 'Cuzdan tumlesik node senkronize olunca yuklenecektir, ekranin altindan baglanma metod %{settings} degistirebilirsiniz.'
|
||||
loading: Yukleniyor
|
||||
closing: Kapaniyor
|
||||
checking: Denetleniyor
|
||||
default_wallet: Varsayilan cuzdan
|
||||
new_account_desc: 'Yemi hesap ad girin:'
|
||||
wallet_loading: Cuzdan yukleniyor
|
||||
wallet_closing: Cuzdan kapaniyor
|
||||
wallet_checking: Cuzdan denetleniyor
|
||||
tx_loading: Islemler yukleniyor
|
||||
default_account: Varsayilan hesap
|
||||
accounts: Hesaplar
|
||||
tx_sent: Gonderildi
|
||||
tx_received: Alindi
|
||||
tx_sending: Gonderiliyor
|
||||
tx_receiving: Aliniyor
|
||||
tx_confirming: Onaylaniyor
|
||||
tx_canceled: Iptal edildi
|
||||
tx_cancelling: Iptal ediliyor
|
||||
tx_finalizing: Islem tamamlaniyor
|
||||
tx_posting: Islem kaydetme
|
||||
tx_confirmed: Onaylandi
|
||||
txs: Islemler
|
||||
tx: Islem
|
||||
messages: Mesajlar
|
||||
transport: Transferler
|
||||
input_slatepack_desc: 'Islemi Tamamlamak veya cevap Slatepack olusturmak için mesaji girin:'
|
||||
parse_slatepack_err: 'Girilen mesaji okurken hata olustu,girilien mesaji tekrar kontrol et:'
|
||||
pay_balance_error: 'Hesap bakiyesi girilen %{amount} ツ ve ağ ücretini ödemek için yetersiz.'
|
||||
parse_i1_slatepack_desc: '%{amount} ツ ödemek için bu mesaji aliciya gönderin:'
|
||||
parse_i2_slatepack_desc: '%{amount} ツ Almak için bu islemi tamamlayin:'
|
||||
parse_i3_slatepack_desc: '%{amount} almak için mesaji tamamlama islemi postalayin:'
|
||||
parse_s1_slatepack_desc: '%{amount} ツ almak için mesaji ödeyecek kisiye gönderin:'
|
||||
parse_s2_slatepack_desc: 'Göndereciğiniz %{amount} ツ islemini tamamlayin:'
|
||||
parse_s3_slatepack_desc: '%{amount} ツ gönderim tamamlamak için islemi postalayin:'
|
||||
resp_slatepack_err: 'Cevap slateapack olusturulurken bir hata olustu, girisi kontrol edin:'
|
||||
resp_exists_err: Bu islem zaten mevcut.
|
||||
resp_canceled_err: Bu islem zaten iptal edildi.
|
||||
create_request_desc: 'Para Almak veya göndermek için talep olustur:'
|
||||
send_request_desc: '%{amount} ツ göndermek için bir istek olusturdunuz. Bu mesaji aliciya gönder:'
|
||||
send_slatepack_err: Para gönderme isteği olusturulurken bir hata olustu, girisi kontrol edin.
|
||||
invoice_desc: 'Almak istediginiz tutar %{amount} ツ talebiniz. Slatepack mesajini gondericiye ilet:'
|
||||
invoice_slatepack_err: Fatura duzenlenirken bir hata olustu, girilen bilgiyi kontrol edin.
|
||||
finalize_slatepack_err: 'TX islemi tamamlanirken hata olustu, girilen bilgiyi kontrol edin:'
|
||||
finalize: Tamamla
|
||||
use_dandelion: Dandelion kullan
|
||||
enter_amount_send: '%{amount} ツ var. GONDERIM miktari gir:'
|
||||
enter_amount_receive: 'ALIM miktari gir:'
|
||||
recovery: Kurtarma
|
||||
repair_wallet: Cuzdani Onar
|
||||
repair_desc: Cuzdani check et,yapilmis, gorunmeyen islemler için resynch biraz zaman alir.
|
||||
repair_unavailable: Cuzdani yeniden tam senkronize etmek için Node baglantisi aktif olmali.
|
||||
delete: Cuzdani Sil
|
||||
delete_conf: Cuzdan silinecektir, emin misiniz?
|
||||
delete_desc: Gelecekte, bakiyeli cuzdaninizi restore etmek için kurtarma kelimelerinizi mutlaka saklayin.
|
||||
wallet_loading_err: 'Cuzdan senkronize edilirken hata olustu, tekrar deneyin veya ekranin altinda bulunan ayarlar %{settings} ogesinden baglanti metodunu degistirin.'
|
||||
wallet: Cuzdan
|
||||
send: Gonder
|
||||
receive: Al
|
||||
settings: Cuzdan ayarlar
|
||||
tx_send_cancel_conf: Gonderim tx iptal
|
||||
tx_receive_cancel_conf: Gelen tx iptal
|
||||
rec_phrase_not_found: Sifre kelime bulunmuyor
|
||||
restore_wallet_desc: Cuzdani restore et
|
||||
fee_base_desc: 'Ücret (taban değeri%{value}):'
|
||||
payment_proof: Ödeme kaniti
|
||||
payment_proof_desc: 'Islemi doğrulamak için alinan ödeme kanitini girin:'
|
||||
payment_proof_valid: 'Girilen ödeme kaniti geçerlidir:'
|
||||
payment_proof_error: 'Girilen ödeme kaniti geçerli değildir:'
|
||||
tx_delete_confirmation: Islemi geçmişten silmek istediğinizden emin misiniz?
|
||||
transport:
|
||||
desc: 'Adresten senkronize GONDER veya AL:'
|
||||
tor_network: Tor network
|
||||
connected: Baglandi
|
||||
connecting: Baglaniyor
|
||||
disconnecting: Baglanti kesiliyor
|
||||
conn_error: Bagalanti hatasi
|
||||
disconnected: Baglanti yok
|
||||
receiver_address: 'Alicinin adresi:'
|
||||
incorrect_addr_err: 'Girilen adres hatali:'
|
||||
tor_send_error: Tor adresi uzerinden gonderimde aksaklik olustu, alici online olmasi gerek, islem iptal edildi.
|
||||
tor_autorun_desc: Islemleri Tor adresi olarak AL,bunun için cuzdan acilisinda Tor hizmetinin baslatilip baslatilmayacagi.
|
||||
tor_sending: Tor adrese gonderiliyor
|
||||
tor_settings: Tor Ayarlar
|
||||
bridges: Bridges
|
||||
bridges_desc: Setup bridges to bypass Tor network censorship if usual connection is not working.
|
||||
bin_file: 'Binary file:'
|
||||
conn_line: 'Baglanti line:'
|
||||
bridges_disabled: Bridges etkin degil
|
||||
bridge_name: 'Bridge %{b}'
|
||||
network:
|
||||
self: Network
|
||||
type: 'Network tipi:'
|
||||
mainnet: Mainnet
|
||||
testnet: Test agi
|
||||
connections: Baglantilar
|
||||
node: Tumlesik node
|
||||
metrics: Metrikler
|
||||
mining: Madencilik
|
||||
settings: Node ayarlar
|
||||
enable_node: Nodu BASLAT
|
||||
autorun: Autorun
|
||||
disabled_server: 'Tumlesik Nodu Baslat veya ust sol kosede %{dots} basarak baska bir baglanti metodu ekleyin.'
|
||||
no_ips: Sisteminizde hic mevcut IP adresleri yok, server baslatilamadi, network baglantisini kontrol edin.
|
||||
available: Mevcut
|
||||
not_available: Mevcut degil
|
||||
availability_check: Mevcut kontrol
|
||||
android_warning: Android kullanicilarinin dikkatine. Tümlesik NODE basarili bir sekilde senkronize etmek için telefonunuzun sistem ayarlarinda Grim uygulamasi için bildirimlere erisime izin vermeniz ve pil kullanim kisitlamalarini kaldirmaniz gerekir. Bu, arka planda uygulamanin doğru çalismasi için gerekli bir islemdir.
|
||||
sync_status:
|
||||
node_restarting: Node yeniden baslatiliyor
|
||||
node_down: Node calismiyor
|
||||
initial: Node bagliyor
|
||||
no_sync: Node calisiyor.
|
||||
awaiting_peers: Peers bekleniyor
|
||||
header_sync: Downloading headers
|
||||
header_sync_percent: 'Downloading headers: %{percent}%'
|
||||
tx_hashset_pibd: Indiriyor state (PIBD)
|
||||
tx_hashset_pibd_percent: 'İndirme durummu (PIBD): %{percent}%'
|
||||
tx_hashset_download: Downloading state
|
||||
tx_hashset_download_percent: 'Indiriyor state: %{percent}%'
|
||||
tx_hashset_setup_history: 'Durum hazirlaniyor (gecmis): %{percent}%'
|
||||
tx_hashset_setup_position: 'Durum hazirlaniyor (posizyon): %{percent}%'
|
||||
tx_hashset_setup: Durum hazirlama
|
||||
tx_hashset_range_proofs_validation: 'Onaylaniyor durum (range proofs): %{percent}%'
|
||||
tx_hashset_kernels_validation: 'Onaylaniyor durum (kernels): %{percent}%'
|
||||
tx_hashset_save: Zincir durumu Tamamlaniyor
|
||||
body_sync: Bloklar Yukleniyor
|
||||
body_sync_percent: 'Bloklar yukleniyor: %{percent}%'
|
||||
shutdown: Node kapaniyor
|
||||
network_node:
|
||||
header: Header
|
||||
block: Block
|
||||
hash: Hash
|
||||
height: Height
|
||||
difficulty: Difficulty
|
||||
time: Time
|
||||
main_pool: Main pool
|
||||
stem_pool: Stem pool
|
||||
data: Data
|
||||
size: Size (GB)
|
||||
peers: Peers
|
||||
error_clean: Node verileri bozuldu, Resync yapmaniz gerekli.
|
||||
resync: Resync
|
||||
error_p2p_api: '%{p2p_api} sunucusu baslatilirken bir hata olustu, ekranin altindaki %{settings} ögesini secerek %{p2p_api} ayarlarini kontrol edin.'
|
||||
error_config: 'Yapilandirmann baslatilmasi sirasinda bir hata olustu; ekranin alt kismindaki %{settings} öğesini seçerek ayarlari kontrol edin.'
|
||||
error_unknown: 'Baslatma sirasinda bir hata olustu. Ekranin altindaki %{settings} öğesini seçerek Tümlesik NODE ayarlariNi kontrol edin veya yeniden Resync edin.'
|
||||
network_metrics:
|
||||
loading: Metrikler senkronizasyondan sonra mevcut olur.
|
||||
emission: Emission
|
||||
inflation: Enflasyon
|
||||
supply: Arz
|
||||
block_time: Blok zaman
|
||||
reward: Odul
|
||||
difficulty_window: 'Difficulty penceresi %{size}'
|
||||
network_mining:
|
||||
loading: Madencilik senkronizasyondan sonra mevcut olacak.
|
||||
info: 'Madencilik server etkinlesti, ayarlar %{settings} ekranin alt koseden degistirilir. Cihaz bagliyken veriler guncelleniyor.'
|
||||
restart_server_required: Degisiklikleri uygulamak için Server yeniden BASLAT.
|
||||
rewards_wallet: Odul Cuzdani
|
||||
server: Stratum server
|
||||
address: Addres
|
||||
miners: Madenciler
|
||||
devices: Cihazlar
|
||||
blocks_found: Blok bulunan
|
||||
hashrate: 'Hashrate (C%{bits})'
|
||||
connected: Baglandi
|
||||
disconnected: Bagli degil
|
||||
network_settings:
|
||||
change_value: Change value
|
||||
stratum_ip: 'Stratum IP address:'
|
||||
stratum_port: 'Stratum port:'
|
||||
port_unavailable: Belirlenen port mevcut degil
|
||||
restart_node_required: Degisiklikler için yeniden Node BASLAT
|
||||
choose_wallet: Cüzdan seç
|
||||
stratum_wallet_warning: Odul almak için cüzdan açilmalidir.
|
||||
enable: Etkinlestir
|
||||
disable: Devredisi birak
|
||||
restart: Restart
|
||||
server: Server
|
||||
api_ip: 'API IP address:'
|
||||
api_port: 'API port:'
|
||||
api_secret: 'Rest API and V2 Owner API token:'
|
||||
foreign_api_secret: 'Foreign API token:'
|
||||
disabled: Mevcut degil
|
||||
enabled: Mevcut
|
||||
ftl: 'The Future Time Limit (FTL):'
|
||||
ftl_description: Blok kabul edilebilmesi için yeni bir bloktaki zaman damgasinin, NODE (saniye cinsinden) yerel saatine gore gelecege ne kadar uzak olabilecegine iliskin sinirlama.
|
||||
not_valid_value: Girilen deger gecersiz
|
||||
full_validation: Tam gecerli
|
||||
full_validation_description: Her blogu islerken tam zincir dogrulamasinin calistirilip calistirilmayacagi (senkronizasyon haric).
|
||||
archive_mode: Arsiv mode
|
||||
archive_mode_desc: Tam arsiv NODE calistir (daha fazla disk yeri ve senkronizasyon için zaman gerektirir).
|
||||
attempt_time: 'Mining attempt time (in seconds):'
|
||||
attempt_time_desc: The amount of time to attempt to mine on a particular header before stopping and re-collecting transactions from the pool
|
||||
min_share_diff: 'The minimum acceptable share difficulty:'
|
||||
reset_settings_desc: Node varsayilan degerlere Resetle
|
||||
reset_settings: Reset ayarlar
|
||||
reset: Reset
|
||||
tx_pool: Transaction pool
|
||||
pool_fee: 'Poolakabul edilen taban ücret:'
|
||||
reorg_period: 'Reorg cache retention period (in minutes):'
|
||||
max_tx_pool: 'Pool icindeki maximum islem sayisi:'
|
||||
max_tx_stempool: 'Maksimum islem sayisi stem-pool icindeki:'
|
||||
max_tx_weight: 'Maximum total weight of transactions that can get selected to build a block:'
|
||||
epoch_duration: 'Epoch duration (in seconds):'
|
||||
embargo_timer: 'Embargo timer (in seconds):'
|
||||
aggregation_period: 'Aggregation period (in seconds):'
|
||||
stem_probability: 'Stem phase probability:'
|
||||
stem_txs: Stem islemler
|
||||
p2p_server: P2P server
|
||||
p2p_port: 'P2P port:'
|
||||
add_seed: DNS Seed Ekle
|
||||
seed_address: 'DNS Seed adresi:'
|
||||
add_peer: Peer ekle
|
||||
peer_address: 'Peer adresi:'
|
||||
peer_address_error: 'IP addres veya DNS gir (make sure specified host is available) dogru format, ornek.: 192.168.0.1:1234 or example.com:5678'
|
||||
default: Varsayilan
|
||||
allow_list: Izin listesi
|
||||
allow_list_desc: Sadece bu listedeki Peere baglan.
|
||||
deny_list: Red listesi
|
||||
deny_list_desc: Bu listedeki Peer asla baglanma.
|
||||
favourites: Favoriler
|
||||
favourites_desc: Baglanti için terchi edilen Peer listesi.
|
||||
ban_window: 'Banlanan bir Peer (saniye cinsinden) yasakli kalma suresi:'
|
||||
ban_window_desc: Banlama karari, peerden alinan verilerin dogruluguna bagli olarak Node tarafindan verilir.
|
||||
max_inbound_count: 'Maksimum gelen Peer baglanti sayisi:'
|
||||
max_outbound_count: 'Maksimum giden Peer baglanti sayisi:'
|
||||
reset_data_desc: Node verisini sifirlama. Sadece senkronizasyonda sorun varsa dikkatli kullanin.
|
||||
reset_data: Verileri sifirlama
|
||||
modal:
|
||||
cancel: Iptal
|
||||
save: Kaydet
|
||||
add: Ekle
|
||||
modal_exit:
|
||||
description: Uygulamadan cikmak için exit, emin misiniz?
|
||||
exit: Exit
|
||||
app_settings:
|
||||
proxy: Proxy
|
||||
proxy_desc: Uygulamadan gelen ağ istekleri için bir proxy kullanmaya değer mi.
|
||||
keyboard:
|
||||
1: 1
|
||||
2: 2
|
||||
3: 3
|
||||
4: 4
|
||||
5: 5
|
||||
6: 6
|
||||
7: 7
|
||||
8: 8
|
||||
9: 9
|
||||
0: 0
|
||||
01: '-'
|
||||
q: q
|
||||
w: w
|
||||
e: e
|
||||
r: r
|
||||
t: t
|
||||
y: y
|
||||
u: u
|
||||
i: i
|
||||
o: o
|
||||
p: p
|
||||
p1: ü
|
||||
a: a
|
||||
s: s
|
||||
d: d
|
||||
f: f
|
||||
g: g
|
||||
h: h
|
||||
j: j
|
||||
k: k
|
||||
l: l
|
||||
l1: ö
|
||||
l2: ':'
|
||||
z: z
|
||||
x: x
|
||||
c: c
|
||||
v: v
|
||||
b: b
|
||||
n: n
|
||||
m: m
|
||||
m1: ','
|
||||
m2: .
|
||||
m3: /
|
||||
goblin:
|
||||
home:
|
||||
anonymous: "Anonim"
|
||||
connected_nym: "Nym üzerinden bağlı"
|
||||
nym_ready: "Nym hazır · relaylar…"
|
||||
connecting_nym: "Nym'e bağlanılıyor…"
|
||||
cant_reach_node: "Düğüme ulaşılamıyor"
|
||||
node_synced: "Düğüm eşitlendi"
|
||||
syncing: "Eşitleniyor…"
|
||||
block: "Blok %{height}"
|
||||
waiting_for_chain: "Zincir bekleniyor…"
|
||||
nav_wallet: "Cüzdan"
|
||||
nav_pay: "Öde"
|
||||
nav_activity: "Etkinlik"
|
||||
nav_receive: "Al"
|
||||
nav_settings: "Ayarlar"
|
||||
activity: "Etkinlik"
|
||||
empty_title: "Henüz etkinlik yok"
|
||||
empty_sub: "Başlamak için grin gönder ya da al."
|
||||
recent: "Son işlemler"
|
||||
scan_to_pay: "Ödemek için tara"
|
||||
type_amount: "Bir tutar gir"
|
||||
request: "İste"
|
||||
pay: "Öde"
|
||||
enter_amount: "Ödemek ya da istemek için bir tutar gir"
|
||||
activity:
|
||||
canceled: "iptal edildi"
|
||||
pending: "beklemede"
|
||||
earlier: "Daha önce"
|
||||
today: "Bugün"
|
||||
yesterday: "Dün"
|
||||
title: "Etkinlik"
|
||||
requests: "İstekler"
|
||||
empty_title: "Henüz etkinlik yok"
|
||||
empty_sub: "Ödemelerin burada görünecek."
|
||||
pending_header: "Beklemede"
|
||||
receipt:
|
||||
title: "Makbuz"
|
||||
not_found: "İşlem bulunamadı"
|
||||
for_note: "%{note} için"
|
||||
details: "İşlem ayrıntıları"
|
||||
canceled: "İptal edildi"
|
||||
expired: "Süresi doldu"
|
||||
funds_returned: "Para iade edildi"
|
||||
complete: "Tamamlandı"
|
||||
payment_received: "Ödeme alındı"
|
||||
payment_sent: "Ödeme başarıyla gönderildi"
|
||||
pending: "Beklemede"
|
||||
confs: "%{c}/%{r} onay"
|
||||
waiting_to_confirm: "Onay bekleniyor"
|
||||
paying: "Ödeniyor…"
|
||||
you: "Sen"
|
||||
to: "Alıcı"
|
||||
from: "Gönderen"
|
||||
nostr: "nostr"
|
||||
fee_none: "Yok"
|
||||
network_fee: "Ağ ücreti"
|
||||
privacy: "Gizlilik"
|
||||
privacy_value: "Mimblewimble + Nym"
|
||||
transaction: "İşlem"
|
||||
cancel_request: "İsteği iptal et"
|
||||
cancel_send: "Ödemeyi iptal et"
|
||||
cancel_send_confirm: "İptal için tekrar dokun — hâlâ alabilir"
|
||||
cancel_send_done: "Ödeme iptal edildi — paranız yeniden kullanılabilir"
|
||||
cancel_send_too_late: "Bu ödeme zaten geçti ve iptal edilemez"
|
||||
waiting_to_receive: "%{name} alana kadar bekleniyor…"
|
||||
request:
|
||||
title: "%{name} istiyor"
|
||||
approve: "Onayla"
|
||||
decline: "Reddet"
|
||||
review_title: "İsteği incele"
|
||||
hold_to_accept: "Kabul için basılı tut"
|
||||
hold_accept_hint: "Bu isteği ödemek için basılı tutun"
|
||||
receive:
|
||||
title: "Al"
|
||||
requesting: "%{amt}%{tsu} isteniyor — ödeme almak için paylaş"
|
||||
clear_request: "İsteği temizle"
|
||||
share_handle: "Ödeme almak için kullanıcı adını paylaş"
|
||||
copied: "Kopyalandı"
|
||||
copy_nostr_id: "nostr kimliğini kopyala"
|
||||
copy_address: "Adresi kopyala"
|
||||
copy_npub: "npub kopyala"
|
||||
share_message: "Goblin'de bana öde (goblin.st) — %{npub}"
|
||||
privacy_note: "Kullanıcı adın herkese açıktır. Ödeme içeriği ağ üzerinde şifreli kalır."
|
||||
profile:
|
||||
title: "Profil"
|
||||
activity: "Etkinlik"
|
||||
no_activity: "Henüz onlarla etkinlik yok."
|
||||
unblock: "Engeli kaldır"
|
||||
block: "Engelle"
|
||||
blocked_blurb: "Engellendi — ödemeleri ve istekleri reddediliyor."
|
||||
block_blurb: "Engellemek, gelen ödeme ve isteklerini düşürür."
|
||||
settings:
|
||||
title: "Ayarlar"
|
||||
connected_nostr: "nostr'a bağlı"
|
||||
connecting_relays: "Relaylara bağlanılıyor…"
|
||||
identity: "Kimlik"
|
||||
copy_npub: "npub kopyala (genel)"
|
||||
rotate_key: "nostr anahtarını değiştir"
|
||||
import_identity: "Kimlik içe aktar (.backup / nsec)"
|
||||
backup_note: "Cihaz mı değiştiriyorsun? İKİSİNİ de yedekle: seed ifaden (bakiye) ve kimlik .backup dosyan (ad + anahtar)."
|
||||
wallet: "Cüzdan"
|
||||
display_unit: "Görüntüleme birimi"
|
||||
relays: "Relaylar"
|
||||
node: "Düğüm"
|
||||
slatepacks: "Slatepackler"
|
||||
slatepacks_value: "Manuel işlem"
|
||||
lock_wallet: "Cüzdanı kilitle"
|
||||
switch_wallet: "Cüzdan değiştir"
|
||||
advanced: "Gelişmiş"
|
||||
privacy: "Gizlilik"
|
||||
mixnet_routing: "Mixnet yönlendirme"
|
||||
messages_lookups: "Mesajlar ve aramalar"
|
||||
auto_accept: "Otomatik kabul"
|
||||
pairing: "Eşleştirme"
|
||||
accept_anyone: "Herkes"
|
||||
accept_contacts: "Yalnızca kişiler"
|
||||
accept_ask: "Her zaman sor"
|
||||
requests: "İstekler"
|
||||
incoming_requests: "Gelen istekler"
|
||||
incoming_requests_sub: "Başkalarının senden para istemesine izin ver"
|
||||
appearance: "Görünüm"
|
||||
theme: "Tema"
|
||||
theme_light: "Açık"
|
||||
theme_dark: "Koyu"
|
||||
theme_yellow: "Sarı"
|
||||
archive: "Arşiv"
|
||||
export_archive: "Arşivi dışa aktar"
|
||||
wipe_history: "Ödeme geçmişini sil"
|
||||
about: "Hakkında"
|
||||
goblin: "Goblin"
|
||||
build: "Sürüm %{build}"
|
||||
network: "Ağ"
|
||||
network_value: "MW + Nym mixnet + nostr"
|
||||
third_party: "Üçüncü taraf"
|
||||
grim: "GRIM (üst kaynak cüzdan)"
|
||||
grin_node: "Grin düğümü"
|
||||
sp_intro: "Gelişmiş — GRIM'in yaptığı gibi ham slatepackleri elle değiş tokuş et. Bunu yalnızca bir username üzerinden ödeme yapamadığında ya da alamadığında kullan."
|
||||
sp_receive_group: "Al ya da tamamla"
|
||||
sp_receive_blurb: "Birinin sana verdiği bir slatepack'i yapıştır. Goblin ödemeyi alır, faturayı öder ya da tamamlayıp zincire gönderir."
|
||||
sp_process: "Slatepack işle"
|
||||
sp_paste_first: "Önce bir slatepack yapıştır."
|
||||
sp_reply_ready: "Yanıt hazır — gönderene geri yolla."
|
||||
sp_finalizing: "Tamamlanıp zincire gönderiliyor…"
|
||||
sp_create_group: "Ödeme oluştur"
|
||||
sp_create_blurb: "Birine vermek için bir slatepack oluştur. Onlar alır, yanıtı geri gönderir, sen de yukarıda tamamlarsın."
|
||||
sp_amount_hint: "Grin cinsinden tutar"
|
||||
sp_addr_hint: "Alıcı adresi (isteğe bağlı)"
|
||||
sp_create: "Slatepack oluştur"
|
||||
sp_ready: "Slatepack hazır — alıcıya ver."
|
||||
sp_amount_gt_zero: "Sıfırdan büyük bir tutar gir."
|
||||
sp_to_send: "Gönderilecek slatepack"
|
||||
sp_copy: "Slatepack kopyala"
|
||||
rotate_line1: "• Tamamen yeni RASTGELE bir anahtar alırsın; eski npub artık almaz. Aralarında türetme zinciri yoktur."
|
||||
rotate_line2: "• Yeni anahtar tohumundan kurtarılamaz — anahtarı değiştirdikten hemen sonra yeni nsec'i yedekle."
|
||||
rotate_line3: "• Kullanıcı adın SERBEST BIRAKILIR — hemen ardından aynı adı ya da yeni bir ad al (serbest kaldığında başkası da kapabilir)."
|
||||
rotate_line4: "• Eski anahtara hâlâ yoldaki ödemeler KESİNTİYE uğrar — önce bekleyen ödemelerin bitmesini bekle."
|
||||
rotate_line5: "• npub'unu doğrudan kaydeden kişiler seni yeniden bulmalı — yeni npub'unu ya da yeniden aldığın username'i paylaş."
|
||||
cancel: "İptal"
|
||||
continue: "Devam"
|
||||
final_confirmation: "Son onay"
|
||||
rotate_confirm_blurb: "Bu işlem uygulamadan geri alınamaz. Değiştirmek için RESET yaz ve cüzdan parolanı gir."
|
||||
type_reset: "RESET yaz"
|
||||
wallet_password: "Cüzdan parolası"
|
||||
rotate_key_btn: "Anahtarı değiştir"
|
||||
rotating_key: "Anahtar değiştiriliyor…"
|
||||
key_rotated: "Anahtar değiştirildi"
|
||||
new_npub: "Yeni npub: %{npub}"
|
||||
backup_new_key: "YENİ gizli anahtarı şimdi yedekle — tohumun onu kurtaramaz."
|
||||
copy_new_nsec: "Yeni nsec yedeğini kopyala"
|
||||
done: "Bitti"
|
||||
rotation_failed: "Değiştirme başarısız"
|
||||
close: "Kapat"
|
||||
import_identity_title: "Kimlik içe aktar"
|
||||
import_blurb: "Bu cüzdanın nostr kimliğini değiştirir — bir GOBLIN .backup dosyası seç ya da nsec yapıştır. Yedek ayrıca kullanıcı adını ve geçmişini geri yükler. Hâlâ gerekiyorsa önce mevcut anahtarı yedekle."
|
||||
import_nsec_hint: "nsec1… veya yapıştırılan yedek"
|
||||
backup_password_hint: "Yedek parolası (yalnızca başka yerde dışa aktarıldıysa)"
|
||||
import_btn: "İçe aktar"
|
||||
importing: "İçe aktarılıyor…"
|
||||
identity_replaced: "Kimlik değiştirildi"
|
||||
now_using: "Şu an kullanılan: %{npub}"
|
||||
import_failed: "İçe aktarma başarısız"
|
||||
name_authority: "İsim otoritesi"
|
||||
name_authority_title: "İsim otoritesini değiştir"
|
||||
name_authority_blurb: "Adları kaydeden ve doğrulayan sunucu. Başka bir örneğe yönlendirerek oradaki adları kullan ve öde."
|
||||
name_authority_invalid: "Tam bir URL gir (https://…)."
|
||||
reset: "Sıfırla"
|
||||
save: "Kaydet"
|
||||
backup_file: "Dosyaya yedekle"
|
||||
choose_backup_file: "Bir .backup dosyası seç"
|
||||
backup_read_failed: "Dosya okunamadı."
|
||||
backup_saved: "Yedek kaydedildi"
|
||||
backup_saved_sub: ".backup dosyasını güvende tut — hem ona hem de parolana sahip olan kimliğini geri yükleyebilir."
|
||||
backup_file_title: "Kimliği yedekle"
|
||||
backup_file_blurb: "Kullanıcı adın ve anahtarınla tek bir şifreli .backup dosyası oluşturur. Mühürlemek için cüzdan parolanı gir."
|
||||
backup_write_failed: "Dosya kaydedilemedi."
|
||||
create_backup: "Yedek oluştur"
|
||||
registered: "%{name} kaydedildi"
|
||||
released_msg: "Bırakıldı — ad artık alınabilir"
|
||||
release_confirm: "%{name} bırakılsın mı?"
|
||||
release_blurb: "Serbest kalır kalmaz herkes alabilir — döndüğün bir sonraki anahtar dahil. 10 dakika boyunca başka bir kullanıcı adı kaydedemezsin."
|
||||
releasing: "Bırakılıyor…"
|
||||
keep_it: "Vazgeç"
|
||||
release_it: "Bırak"
|
||||
username: "Kullanıcı adı"
|
||||
username_note: "you olarak gösterilir. goblin.st'de herkese açık. Ödemeler şifreli kalır."
|
||||
release_username: "Kullanıcı adını bırak"
|
||||
pick_username: "Bir kullanıcı adı seç — isteğe bağlı"
|
||||
working: "Çalışıyor…"
|
||||
claim: "Al"
|
||||
err_just_taken: "O kullanıcı adı az önce alındı"
|
||||
err_cooldown: "Yakın zamanda bir kullanıcı adı bıraktın — 10 dakika içinde yenisini kaydedebilirsin."
|
||||
err_unreachable: "goblin.st'ye ulaşılamadı — bağlantı sorunu. Tekrar dene."
|
||||
err_release: "Bırakılamadı: %{err}"
|
||||
avail_available: "Müsait!"
|
||||
avail_taken: "Alınmış"
|
||||
avail_reserved: "Ayrılmış"
|
||||
avail_invalid: "Adlar 3–20 karakter: a–z, 0–9, _ ya da -"
|
||||
avail_quarantined: "Müsait değil"
|
||||
avail_unknown: "Kontrol edilemedi — bağlantı sorunu. Tekrar dene."
|
||||
advanced:
|
||||
title: "Gelişmiş"
|
||||
intro: "GRIM'den düşük seviyeli cüzdan araçları. Bunlara normalde ihtiyacın olmaz."
|
||||
own_node_desc: "Herkese açık bir düğüme güvenmek yerine bu cihazda tam bir Grin düğümü senkronize edin."
|
||||
own_node_active: "Kendi düğümünüz çalışıyor"
|
||||
repair: "Cüzdanı onar"
|
||||
repair_desc: "Zinciri yeniden tara ve eksik çıktıları geri yükle. Bu biraz zaman alabilir."
|
||||
repair_unavailable: "Önce senkronize bir düğüm bağlantısı gerekir."
|
||||
repairing: "Onarılıyor… %{pct}%"
|
||||
restore: "Cüzdanı geri yükle"
|
||||
restore_desc: "Yerel verileri sil ve tohumundan yeniden oluştur. Onarım işe yaramadıysa bunu kullan — sonra cüzdanı yeniden açarsın."
|
||||
restore_confirm: "Geri yüklemek için tekrar dokun"
|
||||
show_phrase: "Kurtarma ifadesi"
|
||||
phrase_desc: "24 grin tohum kelimen — fonları kurtarmanın tek yolu. Onları çevrimdışı ve gizli tut."
|
||||
reveal: "İfadeyi göster"
|
||||
hide: "Gizle"
|
||||
password: "Cüzdan parolası"
|
||||
wrong_password: "Yanlış parola."
|
||||
delete: "Cüzdanı sil"
|
||||
delete_desc: "Bu cüzdanı bu cihazdan kalıcı olarak kaldır. Tohumun olmadan fonlar kurtarılamaz."
|
||||
delete_confirm: "Silmek için tekrar dokun"
|
||||
manage_node: "Düğüm bağlantısını yönet"
|
||||
repair_confirm: "Evet, şimdi onar"
|
||||
repair_confirm_note: "Onarım zinciri yeniden tarar ve birkaç dakika sürebilir."
|
||||
restore_confirm_note: "Bu, yerel verileri siler ve seed'inizden yeniden oluşturur — birkaç dakika sürebilir."
|
||||
privacy:
|
||||
title: "Ağ gizliliği"
|
||||
intro: "Goblin özel trafiğini Nym mixnet üzerinden gönderir — kimin kiminle konuştuğunu gizleyen beş atlamalı bir ağ, böylece bir relay bir ödemeyi sana bağlayamaz."
|
||||
payments: "Ödemeler"
|
||||
payments_blurb: "Slatepack taşıyan her nostr mesajı."
|
||||
usernames: "usernamelar"
|
||||
usernames_blurb: "goblin.st'ye ve oradan NIP-05 ad aramaları."
|
||||
price_avatars: "Fiyat"
|
||||
price_avatars_blurb: "Tutarların yanında gösterilen anlık kur."
|
||||
over_mixnet: "Mixnet üzerinden"
|
||||
direct_connection: "Doğrudan bağlantı"
|
||||
grin_node: "Grin düğümü"
|
||||
grin_node_blurb: "Blok eşitleme ve işlemini ağa yayma. Bu, herkes için aynı olan genel zincir verisidir ve kimliğinle ilişkilendirilmez."
|
||||
pairing:
|
||||
title: "Eşleştirme"
|
||||
intro: "Bakiyenin ve tutarların neye göre gösterildiği."
|
||||
pair_with: "Eşleştir"
|
||||
rates_note: "Kurlar yalnızca bir eşleştirme açıkken Nym mixnet üzerinden alınır — kapalıysa cihazından hiçbir kur isteği çıkmaz."
|
||||
relays:
|
||||
title: "Relaylar"
|
||||
intro: "Ödeme mesajları aşağıdaki her relay'e yansıtılır; almak için ulaşılabilir tek bir relay yeterlidir."
|
||||
your_relays: "Relaylarn"
|
||||
add_relay: "Relay ekle"
|
||||
add_relay_btn: "Relay ekle"
|
||||
save_reconnect: "Kaydet ve yeniden bağlan"
|
||||
none: "yok"
|
||||
count: "%{n} relay"
|
||||
node:
|
||||
title: "Düğüm"
|
||||
connection: "Bağlantı"
|
||||
integrated: "Tümleşik düğüm"
|
||||
applies_after: "Cüzdan kilitlenip yeniden açıldıktan sonra geçerli olur."
|
||||
add_external: "Harici düğüm ekle"
|
||||
api_secret_hint: "API gizli anahtarı (isteğe bağlı)"
|
||||
add_node: "Düğüm ekle"
|
||||
integrated_host: "tümleşik düğüm"
|
||||
summary_syncing: "%{conn} · eşitleniyor"
|
||||
summary_block: "Blok %{height} · %{conn}"
|
||||
nips:
|
||||
title: "nostr ve NIPler"
|
||||
intro1: "Goblin nostr konuşur — basit relay sunucuları üzerinden geçen imzalı mesajların açık bir protokolü. Cüzdanın kendi nostr kimliğini taşır: bağımsız rastgele bir anahtar, paran ve tohumundan kasıtlı olarak ayrı tutulur. Her ödeme, slatepack içinde olacak şekilde, kimlikler arasında uçtan uca şifreli bir doğrudan mesaj olarak gider."
|
||||
intro2: "goblin.st, Goblin'in ad servisidir: bir kullanıcı adı almak orada bir ad → anahtar eşlemesi yayımlar (NIP-05), böylece insanlar uzun bir npub yerine you'ya ödeme yapabilir. Kullanıcı adı herkese açıktır; ödeme içeriği asla değil. NIPler protokolün yapı taşlarıdır — özelliği okumak için birine dokun."
|
||||
n05_title: "Adlar"
|
||||
n05_blurb: "username@goblin.st'yi anahtarına eşler, böylece kullanıcı adları adres gibi çalışır."
|
||||
n17_title: "Özel mesajlar"
|
||||
n17_blurb: "Her ödemenin içinde gittiği şifreli DM zarfı."
|
||||
n44_title: "Şifreleme"
|
||||
n44_blurb: "Bu mesajların içinde kullanılan kimlik doğrulamalı şifreleme."
|
||||
n49_title: "Anahtar şifreleme"
|
||||
n49_blurb: "Gizli anahtarın parolanla kilitli olarak nasıl depolandığı."
|
||||
n59_title: "Hediye paketi"
|
||||
n59_blurb: "Mesajları sarar, böylece relaylar kimin kiminle konuştuğunu göremez."
|
||||
n98_title: "HTTP kimlik doğrulama"
|
||||
n98_blurb: "goblin.st'ye gönderilen kullanıcı adı kayıt isteğini imzalar."
|
||||
onboarding:
|
||||
intro:
|
||||
private_money_head: "Özel para"
|
||||
private_money_body: "Goblin, grin için bir cüzdan — zincirinde tutar ya da adres bulunmayan dijital nakit."
|
||||
send_like_message_head: "Mesaj gibi gönder"
|
||||
send_like_message_body: "Bir username ya da npub'a öde, nostr ve Nym mixnet üzerinden uçtan uca şifreli bir mesaj olarak ulaşır — aradaki hiç kimse tutarı ya da kimlerin dahil olduğunu göremez."
|
||||
yours_alone_head: "Yalnızca senin"
|
||||
yours_alone_body: "Anahtarlar, adlar ve geçmiş bu cihazda yaşar. GRIM cüzdanı üzerine kuruludur."
|
||||
get_started: "Başla"
|
||||
footnote: "Yaklaşık bir dakika sürer. Her şeyi sonradan değiştirebilirsin."
|
||||
node:
|
||||
kicker: "ADIM 1 / 3 · AĞ"
|
||||
title: "Goblin zinciri nasıl\nizlesin?"
|
||||
own_title: "Kendi düğümümü çalıştır"
|
||||
own_badge: "Özel"
|
||||
own_body: "Kimseye güvenmez — cüzdanın zinciri kendisi kontrol eder. Sen kurulumu bitirirken arka planda eşitlenir."
|
||||
connect_title: "Bir düğüme bağlan"
|
||||
connect_badge: "Anında"
|
||||
connect_body: "Eşitleme beklemesi yok. Seçtiğin düğüm cüzdanının sorgularını görebilir."
|
||||
changeable: "Ayarlar → Düğüm'den istediğin zaman değiştirilebilir."
|
||||
continue: "Devam"
|
||||
url_invalid: "Düğüm URL'si http:// ya da https:// ile başlamalı"
|
||||
wallet:
|
||||
kicker: "ADIM 2 / 3 · CÜZDAN"
|
||||
title: "Cüzdanını kur"
|
||||
create_new: "Yeni oluştur"
|
||||
restore_from_seed: "Tohumdan geri yükle"
|
||||
name_hint: "Cüzdan adı"
|
||||
password_hint: "Parola"
|
||||
repeat_password_hint: "Parolayı tekrarla"
|
||||
restore_hint: "Tohum kelimelerini hazır tut — onları sonra gireceksin."
|
||||
create_hint: "Sırada yazman için 24 tohum kelimesi var. Onlar paradır — onları elinde tutan paranı elinde tutar."
|
||||
continue: "Devam"
|
||||
passwords_no_match: "Parolalar eşleşmiyor"
|
||||
words:
|
||||
kicker: "ADIM 2 / 3 · CÜZDAN"
|
||||
title_restore: "Tohum kelimelerini gir"
|
||||
title_create: "Bu kelimeleri yaz"
|
||||
write_down_hint: "Kâğıda, sırayla. Bu kelimelere sahip olan paranı alabilir; onlar olmadan kaybolan bir cihaz kaybolan para demektir."
|
||||
paste: "Yapıştır"
|
||||
scan_qr: "QR tara"
|
||||
copy_clipboard: "Panoya kopyala (bundan kaçın)"
|
||||
restore_wallet: "Cüzdanı geri yükle"
|
||||
wrote_them_down: "Onları yazdım"
|
||||
fill_every_word: "Her kelimeyi doldur — düzenlemek için bir kelimeye dokun ya da ifadeyi yapıştır."
|
||||
confirm:
|
||||
kicker: "ADIM 2 / 3 · CÜZDAN"
|
||||
title: "Şimdi kanıtla"
|
||||
enter_hint: "Az önce yazdığın kelimeleri gir. Yazmak için bir kelimeye dokun."
|
||||
paste: "Yapıştır"
|
||||
create_wallet: "Cüzdan oluştur"
|
||||
keep_going: "Devam et — her kelime, sırayla."
|
||||
identity:
|
||||
kicker: "ADIM 3 / 3 · KİMLİK"
|
||||
title: "Ödeme kimliğin"
|
||||
key_being_made: "anahtar oluşturuluyor…"
|
||||
connected_nym: "Nym üzerinden bağlı"
|
||||
connecting_nym: "Nym üzerinden bağlanılıyor…"
|
||||
fresh_key_blurb: "Seed'inin parçası olmayan bir ödeme anahtarı — paranı hiç ellemeden istediğin an döndür."
|
||||
clean_slate_blurb: "Temiz bir sayfa mı istiyorsun? İstediğin zaman yepyeni bir anahtar tak — yeni sen eskisine bağlı değil. Aynı cüzdan, yeni yüz."
|
||||
pick_username: "Bir kullanıcı adı seç — isteğe bağlı"
|
||||
username_blurb: "Arkadaşların uzun bir anahtar yerine adına öder. İsteğe bağlı — istediğin an al."
|
||||
username_field_hint: "adınız"
|
||||
working: "Çalışıyor…"
|
||||
claim_username: "Kullanıcı adı al"
|
||||
available_when_connected: "Mixnet bağlandığında müsait — ya da atla ve sonra al."
|
||||
youre: "Sen %{name}'sin"
|
||||
claimed_title: "%{name} artık senin"
|
||||
claimed_blurb: "Arkadaşların artık sana adınla ödeme yapabilir. Her şey hazır — cüzdanını aç."
|
||||
open_wallet: "Cüzdanımı aç"
|
||||
skip_for_now: "Şimdilik atla"
|
||||
import_existing: "Zaten bir Goblin kimliğin var mı? İçe aktar"
|
||||
import_title: "Kimliğini içe aktar"
|
||||
import_blurb: "Bu yeni anahtar yerine mevcut anahtarını ve kullanıcı adını korumak için nsec'ini yapıştır veya bir .backup dosyası seç."
|
||||
errors:
|
||||
cant_open: "Cüzdan açılamadı: %{err}"
|
||||
cant_create: "Cüzdan oluşturulamadı: %{err}"
|
||||
send:
|
||||
scan_to_request: "İstemek için tara"
|
||||
scan_to_pay: "Ödemek için tara"
|
||||
tab_scan: "Tara"
|
||||
tab_my_code: "Kodum"
|
||||
request_from: "Şundan iste"
|
||||
send_to: "Şuna gönder"
|
||||
search_hint: "handle, npub ya da ad"
|
||||
suggested: "%{icon} Önerilen"
|
||||
no_contacts: "Henüz kişi yok. Birini handle ile bul."
|
||||
no_profile: "profil yok"
|
||||
tag_contact: "kişi"
|
||||
tag_on_nostr: "nostr'da"
|
||||
searching_nostr: "nostr aranıyor…"
|
||||
unverified_title: "Doğrulanmamış bir anahtara ödeme yapılsın mı?"
|
||||
unverified_body: "Bu anahtar için yayımlanmış bir nostr profili yok — yepyeni, anonim ya da yanlış yazılmış olabilir. Göndermeden önce doğru olduğunu iki kez kontrol et."
|
||||
keep_looking: "Aramaya devam et"
|
||||
pay_anyway: "Yine de öde"
|
||||
scan_not_recipient: "O QR bir goblin alıcısı değil — bir npub ya da handle bekleniyordu"
|
||||
scan_prompt: "Etkinleştirmek için bir goblin kodunu görüntüye getir"
|
||||
scan_to_pay_me: "Bana ödemek için tara"
|
||||
share_btn: "%{icon} Paylaş"
|
||||
share_message: "Goblin'de bana öde — %{handle}\n%{link}\nnpub: %{npub}"
|
||||
none_found: "%{label} için kimse bulunamadı"
|
||||
enter_recipient: "Bir handle, npub ya da ad gir"
|
||||
amount_title: "Tutar"
|
||||
to_name: "%{name} için"
|
||||
not_enough: "Yeterli grin yok"
|
||||
max: "Maks"
|
||||
note_label: "Not"
|
||||
note_hint: "Bir not ekle…"
|
||||
add_note: "Not ekle"
|
||||
edit_note: "Notu düzenle"
|
||||
note_cancel: "İptal"
|
||||
note_save: "Kaydet"
|
||||
review_btn: "İncele"
|
||||
confirm_request: "İsteği onayla"
|
||||
review_title: "İncele"
|
||||
requesting_from: "%{name} kişisinden isteniyor"
|
||||
youre_sending: "%{name} kişisine gönderiyorsun"
|
||||
row_from: "Gönderen"
|
||||
row_to: "Alıcı"
|
||||
row_note: "Not"
|
||||
row_they_pay: "Onlar öder"
|
||||
row_they_pay_val: "Yalnızca onaylarlarsa"
|
||||
row_delivery: "Teslimat"
|
||||
row_delivery_val: "NIP-44 şifreli, Nym üzerinden"
|
||||
row_network_fee: "Ağ ücreti"
|
||||
row_network_fee_val: "Bakiyenden düşülür"
|
||||
row_privacy: "Gizlilik"
|
||||
row_privacy_val: "Mimblewimble + Nym"
|
||||
send_request_btn: "İstek gönder"
|
||||
request_approve_hint: "Onaylamaları için bir istek alacaklar"
|
||||
hold_to_send: "Göndermek için basılı tut"
|
||||
lower_amount: "Geri dön ve tutarı düşür"
|
||||
hold_confirm_hint: "Onaylamak için basılı tut"
|
||||
requesting: "İsteniyor…"
|
||||
sending: "Gönderiliyor…"
|
||||
they: "Onlar"
|
||||
request_blocked: "%{who} istek kabul etmiyor. Bunun yerine sana grin göndermesini iste."
|
||||
failed_request_title: "İstenemedi"
|
||||
failed_send_title: "Gönderilemedi"
|
||||
failed_request_body: "İsteği teslim edemedik. Bunun yerine sana grin göndermesini iste."
|
||||
failed_send_body: "Ödeme teslim edilemedi. Grin'in güvende — tekrar dene."
|
||||
try_again_btn: "Tekrar dene"
|
||||
close_btn: "Kapat"
|
||||
success:
|
||||
requested: "İstendi"
|
||||
sent: "Gönderildi"
|
||||
from: "şuradan"
|
||||
to: "şuraya"
|
||||
subtitle: "%{dir} %{who} · az önce"
|
||||
done_btn: "Bitti"
|
||||
receipt_btn: "Makbuz"
|
||||
@@ -0,0 +1,808 @@
|
||||
lang_name: 英语
|
||||
copy: 复制
|
||||
paste: 粘贴
|
||||
continue: 继续
|
||||
complete: 完成
|
||||
error: 错误
|
||||
retry: 重试
|
||||
close: 关闭
|
||||
change: 更改
|
||||
show: 显示
|
||||
delete: 删除
|
||||
clear: 清楚
|
||||
create: 创建
|
||||
id: 标识
|
||||
kernel: 核心
|
||||
settings: 设置
|
||||
language: 语言
|
||||
scan: 扫描
|
||||
qr_code: 二维码
|
||||
scan_qr: 扫描二维码
|
||||
repeat: 重复
|
||||
scan_result: 扫描结果
|
||||
back: 返回
|
||||
share: 分享
|
||||
theme: '主题:'
|
||||
dark: 深色
|
||||
light: 淡色
|
||||
file: 文件
|
||||
choose_file: 选择文件
|
||||
choose_folder: 选择文件夹
|
||||
crash_report: 崩溃报告
|
||||
crash_report_warning: 上次应用程序意外关闭,您可以报告开发人员崩溃事件.
|
||||
confirmation: 确认
|
||||
enter_url: 输入 URL
|
||||
max_short: 最大數量
|
||||
files_location: 檔案位置
|
||||
moving_files: 檔案移動
|
||||
wrong_path_error: 指定錯誤路徑
|
||||
check_updates: 啟動時請查看更新
|
||||
update_available: 最新消息已发布!
|
||||
changelog: '更新日誌:'
|
||||
wallets:
|
||||
await_conf_amount: 等待确认中
|
||||
await_fin_amount: 等待确定中
|
||||
locked_amount: 锁定帐户
|
||||
txs_empty: '手动接收资金或通过传输接收资金 %{message} or %{transport} 更改钱包设置, 请按屏幕底部的按钮 %{settings} 按钮.'
|
||||
title: Goblin
|
||||
create_desc: 创建或种子单词导入已有钱包.
|
||||
add: 添加钱包
|
||||
name: '用户名:'
|
||||
pass: '密码:'
|
||||
pass_empty: 输入钱包的密码
|
||||
current_pass: '目前密码:'
|
||||
new_pass: '新密码:'
|
||||
min_tx_conf_count: '确认交易的最低数量:'
|
||||
recover: 恢复
|
||||
recovery_phrase: 助记词
|
||||
words_count: '字数:'
|
||||
enter_word: '输入单词 #%{number}:'
|
||||
not_valid_word: 输入的单词无效
|
||||
not_valid_phrase: 输入的助记词无效
|
||||
create_phrase_desc: 已安全地写下并保存助记词.
|
||||
restore_phrase_desc: 从已保存的助记词中输入.
|
||||
setup_conn_desc: 选择钱包连接到网络的方式.
|
||||
conn_method: 连接方式
|
||||
ext_conn: '外部连接:'
|
||||
add_node: 添加节点
|
||||
node_url: '节点网址:'
|
||||
node_secret: 'API 密钥 (可选):'
|
||||
invalid_url: 输入的网址无效
|
||||
open: 打开钱包
|
||||
wrong_pass: 输入的密码错误
|
||||
locked: 已锁定
|
||||
unlocked: 解锁
|
||||
enable_node: '通过选择屏幕底部的按钮 %{settings} 启用集成节点以使用钱包或更改连接设置.'
|
||||
node_loading: '集成节点同步后钱包会加载,你可选择屏幕底部的按钮 %{settings} 更改连接.'
|
||||
loading: 正在加载
|
||||
closing: 正在关闭
|
||||
checking: 检查中
|
||||
default_wallet: 默认钱包
|
||||
new_account_desc: '输入新帐户的名称:'
|
||||
wallet_loading: 加载钱包
|
||||
wallet_closing: 关闭钱包
|
||||
wallet_checking: 检查钱包
|
||||
tx_loading: 加载事务
|
||||
default_account: 默认账户
|
||||
accounts: 账户
|
||||
tx_sent: 已发送
|
||||
tx_received: 已接收
|
||||
tx_sending: 发送中
|
||||
tx_receiving: 接收中
|
||||
tx_confirming: 等待确认
|
||||
tx_canceled: 已取消
|
||||
tx_cancelling: 取消
|
||||
tx_finalizing: 完成
|
||||
tx_posting: 过账交易
|
||||
tx_confirmed: 已确认
|
||||
txs: 所有交易
|
||||
tx: 交易
|
||||
messages: 消息
|
||||
transport: 传输
|
||||
input_slatepack_desc: '输入收到的 Slatepack 消息创建响应或完成的请求:'
|
||||
parse_slatepack_err: '读取消息时出错,请检查输入:'
|
||||
pay_balance_error: '账户余额不足以支付 %{amount} ツ 和网络费用.'
|
||||
parse_i1_slatepack_desc: '要支付 %{amount} ツ 请将此消息发送给接收者:'
|
||||
parse_i2_slatepack_desc: '完成交易以接收 %{amount} ツ:'
|
||||
parse_i3_slatepack_desc: '发布交易以完成 %{amount} ツ的接收 ツ:'
|
||||
parse_s1_slatepack_desc: '要接收 %{amount} ツ 请将此消息发送给发件人:'
|
||||
parse_s2_slatepack_desc: '完成交易以发送 %{amount} ツ:'
|
||||
parse_s3_slatepack_desc: '发布交易以完成 %{amount} ツ的发送:'
|
||||
resp_slatepack_err: '创建响应时出错,请检查输入数据或重试:'
|
||||
resp_exists_err: 此交易已存在.
|
||||
resp_canceled_err: 此交易已被取消.
|
||||
create_request_desc: '创建发送或接收资金的请求:'
|
||||
send_request_desc: '您已创建发送请求 %{amount} ツ. 将此消息发送给接收者:'
|
||||
send_slatepack_err: 创建发送资金请求时出错,请检查输入数据或重试.
|
||||
invoice_desc: '您已创建接收请求 %{amount} ツ. 将此消息发送给发送者:'
|
||||
invoice_slatepack_err: 发票开具时出错,请检查输入数据或重试.
|
||||
finalize_slatepack_err: '完结时出错,请检查输入数据或重试:'
|
||||
finalize: 完成
|
||||
use_dandelion: 使用蒲公英
|
||||
enter_amount_send: '你有 %{amount} ツ. 输入要发送的金额:'
|
||||
enter_amount_receive: '输入要接收的金额:'
|
||||
recovery: 恢复
|
||||
repair_wallet: 修复钱包
|
||||
repair_desc: 检查钱包,必要时修复和恢复丢失的输出. 此操作需要时间.
|
||||
repair_unavailable: 您需要与节点建立有效连接并完成钱包同步.
|
||||
delete: 删除钱包
|
||||
delete_conf: 您确定要删除钱包吗?
|
||||
delete_desc: 确保您已保存恢复助记语,以便日后使用资金。.
|
||||
wallet_loading_err: '同步钱包时出错,你可以通过选择屏幕底部的按钮 %{settings} 来重试或更改连接设置.'
|
||||
wallet: 钱包
|
||||
send: 发送
|
||||
receive: 接收
|
||||
settings: 钱包设置
|
||||
tx_send_cancel_conf: '您确定要取消 %{amount} ツ的发送吗?'
|
||||
tx_receive_cancel_conf: '您确定要取消 %{amount} ツ的接收吗?'
|
||||
rec_phrase_not_found: 找不到恢复助记词.
|
||||
restore_wallet_desc: 如果常规修复没有帮助,通过删除所有文件来恢复钱包.您将需要重新打开您的钱包.
|
||||
fee_base_desc: '费用 (基值%{value}):'
|
||||
payment_proof: 付款證明
|
||||
payment_proof_desc: '輸入已收款證明以驗證交易:'
|
||||
payment_proof_valid: '輸入的付款證明有效:'
|
||||
payment_proof_error: '輸入的付款證明無效:'
|
||||
tx_delete_confirmation: 你確定要從歷史紀錄中刪除這筆交易嗎?
|
||||
transport:
|
||||
desc: '使用传输同步接收或发送消息:'
|
||||
tor_network: Tor 网络
|
||||
connected: 已连接
|
||||
connecting: 正在连接
|
||||
disconnecting: 断开连接
|
||||
conn_error: 连接错误
|
||||
disconnected: 已断开连接
|
||||
receiver_address: '接收者的地址:'
|
||||
incorrect_addr_err: '输入的地址不正确:'
|
||||
tor_send_error: 通过 Tor 发送时出错,请确保接收方在线, 交易已取消.
|
||||
tor_autorun_desc: 是否在开钱包时启动 Tor 服务以同步接收交易.
|
||||
tor_sending: 通过 Tor 发送
|
||||
tor_settings: Tor 设置
|
||||
bridges: 桥梁
|
||||
bridges_desc: 如果常规连接不正常,设置网桥,可以绕过 Tor 网络审查.
|
||||
bin_file: '二进制文件:'
|
||||
conn_line: '连接线:'
|
||||
bridges_disabled: 网桥已禁用
|
||||
bridge_name: '网桥%{b}'
|
||||
network:
|
||||
self: 网络
|
||||
type: '网络类型:'
|
||||
mainnet: 主网
|
||||
testnet: 测试网
|
||||
connections: 连接
|
||||
node: 集成节点
|
||||
metrics: 指标
|
||||
mining: 挖矿
|
||||
settings: 节点设置
|
||||
enable_node: 启用节点
|
||||
autorun: 自动运行
|
||||
disabled_server: '按屏幕左上角的按钮 %{dots}启用集成节点或添加其他连接方法.'
|
||||
no_ips: T您的系统上没有可用的 IP 地址,服务器无法启动,请检查您的网络连接.
|
||||
available: 可用
|
||||
not_available: 不可用
|
||||
availability_check: 检查是否可用
|
||||
android_warning: Android 用户注意 .要成功同步集成节点,您必须在手机的系统设置中允许访问通知并取消 Grim 应用程序的电池使用限制.这是在后台正确运行应用程序的必要操作.
|
||||
sync_status:
|
||||
node_restarting: 节点正在重新启动
|
||||
node_down: 节点已关闭
|
||||
initial: 节点正在启动
|
||||
no_sync: 节点正在运行
|
||||
awaiting_peers: 等待网络对点
|
||||
header_sync: 正下载标题
|
||||
header_sync_percent: '正在下载标题: %{percent}%'
|
||||
tx_hashset_pibd: 下载状态 (PIBD)
|
||||
tx_hashset_pibd_percent: '下载状态 (PIBD): %{percent}%'
|
||||
tx_hashset_download: 正在下载状态
|
||||
tx_hashset_download_percent: '下载状态: %{percent}%'
|
||||
tx_hashset_setup_history: '正在准备状态(历史记录): %{percent}%'
|
||||
tx_hashset_setup_position: '正在准备状态(位置): %{percent}%'
|
||||
tx_hashset_setup: 正在准备状态
|
||||
tx_hashset_range_proofs_validation: '验证状态(范围证明): %{percent}%'
|
||||
tx_hashset_kernels_validation: '正在验证状态(核心): %{percent}%'
|
||||
tx_hashset_save: 最终确定链状态
|
||||
body_sync: 下载区块
|
||||
body_sync_percent: '下载区块中: %{percent}%'
|
||||
shutdown: 节点正在关闭
|
||||
network_node:
|
||||
header: 标题
|
||||
block: 区块
|
||||
hash: 哈希值
|
||||
height: 高度
|
||||
difficulty: 难度
|
||||
time: 时间
|
||||
main_pool: 主池
|
||||
stem_pool: stem池
|
||||
data: 数据
|
||||
size: 大小 (GB)
|
||||
peers: 网络对点
|
||||
error_clean: 点数据已损坏,需要重新同步.
|
||||
resync: 重新同步
|
||||
error_p2p_api: '%{p2p_api} 服务器初始化时出错,请选择屏幕底部的按钮 %{p2p_api} 来检查 %{settings}设置.'
|
||||
error_config: '配置初始化时出错,请选择屏幕底部的按钮 %{settings} 检查设置.'
|
||||
error_unknown: '初始化时出错,请选择屏幕底部的按钮 %{settings} 来检查集成节点设置,或者重新同步.'
|
||||
network_metrics:
|
||||
loading: 指标在同步后将可用
|
||||
emission: 发射
|
||||
inflation: 通货膨胀
|
||||
supply: 供应
|
||||
block_time: Block time
|
||||
reward: 奖励
|
||||
difficulty_window: '难度窗口 %{size}'
|
||||
network_mining:
|
||||
loading: 同步后即可挖矿
|
||||
info: '挖矿服务器已启用,您可以通过选择屏幕底部的按钮 %{settings} 来更改其设置。连接设备后,数据会更新.'
|
||||
restart_server_required: 需要重启服务器才能应用更改.
|
||||
rewards_wallet: 奖励钱包
|
||||
server: 阶层服务器
|
||||
address: 地址
|
||||
miners: 矿工
|
||||
devices: 设备
|
||||
blocks_found: 找到的区块
|
||||
hashrate: '哈希率 (C%{bits})'
|
||||
connected: 已连接
|
||||
disconnected: 已断开连接
|
||||
network_settings:
|
||||
change_value: 更改值
|
||||
stratum_ip: '层 IP 地址:'
|
||||
stratum_port: '层端口:'
|
||||
port_unavailable: 指定的端口不可用
|
||||
restart_node_required: 需要重启节点才能应用更改.
|
||||
choose_wallet: 选择钱包
|
||||
stratum_wallet_warning: 必须打开钱包才能获得奖励.
|
||||
enable: 启用
|
||||
disable: 禁用
|
||||
restart: 重新启动
|
||||
server: 服务器
|
||||
api_ip: 'API IP 地址:'
|
||||
api_port: 'API 端口:'
|
||||
api_secret: '其它API 和 V2 所有者 API 令牌:'
|
||||
foreign_api_secret: '外部 API 令牌:'
|
||||
disabled: 已禁用
|
||||
enabled: 已启用
|
||||
ftl: '未来时间限制 (FTL):'
|
||||
ftl_description: 限制未来多长时间, 相对于节点的本地时间,以秒为单位, 新区块的时间戳可以被接受.
|
||||
not_valid_value: 输入的值无效
|
||||
full_validation: 完全验证
|
||||
full_validation_description: 在处理每个区块时是否运行全链验证(同步期间除外).
|
||||
archive_mode: 存档模式
|
||||
archive_mode_desc: 以全部存档模式运行全节点(同步需要更多的磁盘空间和时间).
|
||||
attempt_time: '尝试挖矿时间 (秒):'
|
||||
attempt_time_desc: 在停止并从池中重新收集交易之前尝试对特定标题进行挖矿的时间
|
||||
min_share_diff: '可接受的最低份额难度:'
|
||||
reset_settings_desc: 将节点设置重置为默认值
|
||||
reset_settings: 重置设置
|
||||
reset: 重置
|
||||
tx_pool: 交易池
|
||||
pool_fee: '接受到矿池的基本费用:'
|
||||
reorg_period: '重组缓存保留期(以分钟为单位):'
|
||||
max_tx_pool: '池中的最大交易数:'
|
||||
max_tx_stempool: 'stem池中的最大交易数:'
|
||||
max_tx_weight: '可以选择构建区块交易的最大总权重:'
|
||||
epoch_duration: '纪元持续时间(以秒为单位):'
|
||||
embargo_timer: '禁止计时器(以秒为单位):'
|
||||
aggregation_period: '聚合周期(以秒为单位):'
|
||||
stem_probability: 'stem助记词概率:'
|
||||
stem_txs: stem交易
|
||||
p2p_server: P2P 服务器
|
||||
p2p_port: 'P2P 端口:'
|
||||
add_seed: 添加 DNS 种子
|
||||
seed_address: 'DNS 种子地址:'
|
||||
add_peer: 添加网络对点
|
||||
peer_address: '网络对点地址:'
|
||||
peer_address_error: '以正确的格式输入 IP 地址或 DNS 名称(确保指定的主机可用),例如:192.168.0.1:1234 或 example.com:5678'
|
||||
default: 默认
|
||||
allow_list: 允许列表
|
||||
allow_list_desc: 仅连接到此列表中的网络对点.
|
||||
deny_list: 拒绝列表
|
||||
deny_list_desc: 切勿连接到此列表中的网络对点.
|
||||
favourites: 收藏夹
|
||||
favourites_desc: 要连接的首选网络对点列表.
|
||||
ban_window: '被封禁的网络对点应该保持被封禁多长时间(以秒为单位):'
|
||||
ban_window_desc: 禁止的决定是由节点 根据从网络对点收到的数据的正确性做出的.
|
||||
max_inbound_count: '入站网络对点连接的最大数量:'
|
||||
max_outbound_count: '最大出站网络对点连接数:'
|
||||
reset_data_desc: 重置节点数据。只有在出现同步问题时才需谨慎使用.
|
||||
reset_data: 重置数据
|
||||
modal:
|
||||
cancel: 取消
|
||||
save: 保存
|
||||
add: 添加
|
||||
modal_exit:
|
||||
description: 您确定要退出应用程序吗?
|
||||
exit: 退出手
|
||||
app_settings:
|
||||
proxy: 代理
|
||||
proxy_desc: 是否值得对来自应用程序的网络请求使用代理.
|
||||
keyboard:
|
||||
1: 1
|
||||
2: 2
|
||||
3: 3
|
||||
4: 4
|
||||
5: 5
|
||||
6: 6
|
||||
7: 7
|
||||
8: 8
|
||||
9: 9
|
||||
0: 0
|
||||
01: '-'
|
||||
q: 手
|
||||
w: 田
|
||||
e: 水
|
||||
r: 口
|
||||
t: 廿
|
||||
y: 卜
|
||||
u: 山
|
||||
i: 戈
|
||||
o: 人
|
||||
p: 心
|
||||
p1: '"'
|
||||
a: 日
|
||||
s: 尸
|
||||
d: 木
|
||||
f: 火
|
||||
g: 土
|
||||
h: 竹
|
||||
j: 十
|
||||
k: 大
|
||||
l: 中
|
||||
l1: \
|
||||
l2: ':'
|
||||
z: 重
|
||||
x: 難
|
||||
c: 金
|
||||
v: 女
|
||||
b: 月
|
||||
n: 弓
|
||||
m: 一
|
||||
m1: ','
|
||||
m2: .
|
||||
m3: /
|
||||
goblin:
|
||||
home:
|
||||
anonymous: "匿名"
|
||||
connected_nym: "已通过 Nym 连接"
|
||||
nym_ready: "Nym 就绪 · 连接中继…"
|
||||
connecting_nym: "正在连接 Nym…"
|
||||
cant_reach_node: "无法连接节点"
|
||||
node_synced: "节点已同步"
|
||||
syncing: "同步中…"
|
||||
block: "区块 %{height}"
|
||||
waiting_for_chain: "等待链数据…"
|
||||
nav_wallet: "钱包"
|
||||
nav_pay: "支付"
|
||||
nav_activity: "动态"
|
||||
nav_receive: "收款"
|
||||
nav_settings: "设置"
|
||||
activity: "动态"
|
||||
empty_title: "暂无动态"
|
||||
empty_sub: "收发 grin 即可开始。"
|
||||
recent: "最近"
|
||||
scan_to_pay: "扫码支付"
|
||||
type_amount: "输入金额"
|
||||
request: "请求"
|
||||
pay: "支付"
|
||||
enter_amount: "输入要支付或请求的金额"
|
||||
activity:
|
||||
canceled: "已取消"
|
||||
pending: "待处理"
|
||||
earlier: "更早"
|
||||
today: "今天"
|
||||
yesterday: "昨天"
|
||||
title: "动态"
|
||||
requests: "请求"
|
||||
empty_title: "暂无动态"
|
||||
empty_sub: "你的付款将显示在这里。"
|
||||
pending_header: "待处理"
|
||||
receipt:
|
||||
title: "收据"
|
||||
not_found: "未找到交易"
|
||||
for_note: "用于 %{note}"
|
||||
details: "交易详情"
|
||||
canceled: "已取消"
|
||||
expired: "已过期"
|
||||
funds_returned: "资金已退回"
|
||||
complete: "已完成"
|
||||
payment_received: "已收到付款"
|
||||
payment_sent: "付款发送成功"
|
||||
pending: "待处理"
|
||||
confs: "%{c}/%{r} 次确认"
|
||||
waiting_to_confirm: "等待确认"
|
||||
paying: "支付中…"
|
||||
you: "你"
|
||||
to: "收款方"
|
||||
from: "付款方"
|
||||
nostr: "nostr"
|
||||
fee_none: "无"
|
||||
network_fee: "网络费用"
|
||||
privacy: "隐私"
|
||||
privacy_value: "Mimblewimble + Nym"
|
||||
transaction: "交易"
|
||||
cancel_request: "取消请求"
|
||||
cancel_send: "取消付款"
|
||||
cancel_send_confirm: "再次点按以取消 — 对方可能仍会收到"
|
||||
cancel_send_done: "付款已取消 — 你的资金已重新可用"
|
||||
cancel_send_too_late: "这笔付款已经完成,无法取消"
|
||||
waiting_to_receive: "等待 %{name} 接收…"
|
||||
request:
|
||||
title: "%{name} 发起请求"
|
||||
approve: "同意"
|
||||
decline: "拒绝"
|
||||
review_title: "审核请求"
|
||||
hold_to_accept: "按住以接受"
|
||||
hold_accept_hint: "按住以支付此请求"
|
||||
receive:
|
||||
title: "收款"
|
||||
requesting: "正在请求 %{amt}%{tsu} — 分享以收款"
|
||||
clear_request: "清除请求"
|
||||
share_handle: "分享你的用户名以收款"
|
||||
copied: "已复制"
|
||||
copy_nostr_id: "复制 nostr ID"
|
||||
copy_address: "复制地址"
|
||||
copy_npub: "复制 npub"
|
||||
share_message: "在 Goblin 上向我付款 (goblin.st) — %{npub}"
|
||||
privacy_note: "你的用户名是公开的。付款内容在网络中保持加密。"
|
||||
profile:
|
||||
title: "资料"
|
||||
activity: "动态"
|
||||
no_activity: "尚无往来记录。"
|
||||
unblock: "取消屏蔽"
|
||||
block: "屏蔽"
|
||||
blocked_blurb: "已屏蔽 — 其付款和请求会被丢弃。"
|
||||
block_blurb: "屏蔽后将丢弃对方发来的付款和请求。"
|
||||
settings:
|
||||
title: "设置"
|
||||
connected_nostr: "已连接 nostr"
|
||||
connecting_relays: "正在连接中继…"
|
||||
identity: "身份"
|
||||
copy_npub: "复制 npub(公开)"
|
||||
rotate_key: "轮换 nostr 密钥"
|
||||
import_identity: "导入身份(.backup / nsec)"
|
||||
backup_note: "更换设备?两者都要备份:你的助记词(资金)和身份 .backup 文件(名称 + 密钥)。"
|
||||
wallet: "钱包"
|
||||
display_unit: "显示单位"
|
||||
relays: "中继"
|
||||
node: "节点"
|
||||
slatepacks: "Slatepack"
|
||||
slatepacks_value: "手动交易"
|
||||
lock_wallet: "锁定钱包"
|
||||
switch_wallet: "切换钱包"
|
||||
advanced: "高级"
|
||||
privacy: "隐私"
|
||||
mixnet_routing: "mixnet 路由"
|
||||
messages_lookups: "消息和查询"
|
||||
auto_accept: "自动接受"
|
||||
pairing: "配对"
|
||||
accept_anyone: "任何人"
|
||||
accept_contacts: "仅联系人"
|
||||
accept_ask: "每次询问"
|
||||
requests: "请求"
|
||||
incoming_requests: "收到的请求"
|
||||
incoming_requests_sub: "允许他人向你请求付款"
|
||||
appearance: "外观"
|
||||
theme: "主题"
|
||||
theme_light: "浅色"
|
||||
theme_dark: "深色"
|
||||
theme_yellow: "黄色"
|
||||
archive: "存档"
|
||||
export_archive: "导出存档"
|
||||
wipe_history: "清除付款记录"
|
||||
about: "关于"
|
||||
goblin: "Goblin"
|
||||
build: "构建 %{build}"
|
||||
network: "网络"
|
||||
network_value: "MW + Nym mixnet + nostr"
|
||||
third_party: "第三方"
|
||||
grim: "GRIM(上游钱包)"
|
||||
grin_node: "Grin 节点"
|
||||
sp_intro: "高级功能 — 像 GRIM 那样手动交换原始 slatepack。仅在无法通过 username 收付款时使用。"
|
||||
sp_receive_group: "接收或确认"
|
||||
sp_receive_blurb: "粘贴别人给你的 slatepack。Goblin 会接收付款、支付账单,或确认并广播到链上。"
|
||||
sp_process: "处理 slatepack"
|
||||
sp_paste_first: "请先粘贴 slatepack。"
|
||||
sp_reply_ready: "回复已就绪 — 发回给发送方。"
|
||||
sp_finalizing: "正在确认并广播到链上…"
|
||||
sp_create_group: "创建付款"
|
||||
sp_create_blurb: "生成一个 slatepack 交给他人。对方接收后将回复发回,你在上方确认即可。"
|
||||
sp_amount_hint: "金额(grin)"
|
||||
sp_addr_hint: "收款地址(可选)"
|
||||
sp_create: "创建 slatepack"
|
||||
sp_ready: "slatepack 已就绪 — 交给收款方。"
|
||||
sp_amount_gt_zero: "请输入大于零的金额。"
|
||||
sp_to_send: "待发送的 slatepack"
|
||||
sp_copy: "复制 slatepack"
|
||||
rotate_line1: "• 你会得到一个全新的随机密钥;旧 npub 将停止接收。两者之间没有任何派生关系。"
|
||||
rotate_line2: "• 新密钥无法从助记词恢复 — 轮换后请立即备份新的 nsec。"
|
||||
rotate_line3: "• 你的用户名将被释放——请立即认领相同或新的名称(一旦释放,他人也可抢注)。"
|
||||
rotate_line4: "• 正在发往旧密钥的付款将受影响 — 请先等待待处理付款完成。"
|
||||
rotate_line5: "• 直接保存了你 npub 的联系人需要重新查找你 — 分享你的新 npub 或重新注册的 username。"
|
||||
cancel: "取消"
|
||||
continue: "继续"
|
||||
final_confirmation: "最终确认"
|
||||
rotate_confirm_blurb: "此操作在应用内无法撤销。输入 RESET 并输入钱包密码以进行轮换。"
|
||||
type_reset: "输入 RESET"
|
||||
wallet_password: "钱包密码"
|
||||
rotate_key_btn: "轮换密钥"
|
||||
rotating_key: "正在轮换密钥…"
|
||||
key_rotated: "密钥已轮换"
|
||||
new_npub: "新 npub:%{npub}"
|
||||
backup_new_key: "立即备份新私钥 — 助记词无法恢复它。"
|
||||
copy_new_nsec: "复制新 nsec 备份"
|
||||
done: "完成"
|
||||
rotation_failed: "轮换失败"
|
||||
close: "关闭"
|
||||
import_identity_title: "导入身份"
|
||||
import_blurb: "替换此钱包的 nostr 身份——选择一个 GOBLIN .backup 文件,或粘贴 nsec。备份也会恢复你的用户名和历史。如果仍需要当前密钥,请先备份。"
|
||||
import_nsec_hint: "nsec1… 或粘贴的备份"
|
||||
backup_password_hint: "备份密码(仅当在他处导出时需要)"
|
||||
import_btn: "导入"
|
||||
importing: "正在导入…"
|
||||
identity_replaced: "身份已替换"
|
||||
now_using: "当前使用:%{npub}"
|
||||
import_failed: "导入失败"
|
||||
name_authority: "名称授权方"
|
||||
name_authority_title: "更改名称授权方"
|
||||
name_authority_blurb: "注册和验证名称的服务器。指向另一个实例即可使用并支付那里托管的名称。"
|
||||
name_authority_invalid: "请输入完整网址(https://…)。"
|
||||
reset: "重置"
|
||||
save: "保存"
|
||||
backup_file: "备份到文件"
|
||||
choose_backup_file: "选择 .backup 文件"
|
||||
backup_read_failed: "无法读取该文件。"
|
||||
backup_saved: "备份已保存"
|
||||
backup_saved_sub: "妥善保管 .backup 文件——同时拥有它和你密码的人都能恢复你的身份。"
|
||||
backup_file_title: "备份身份"
|
||||
backup_file_blurb: "创建一个包含你的用户名和密钥的加密 .backup 文件。输入钱包密码以封存它。"
|
||||
backup_write_failed: "无法保存文件。"
|
||||
create_backup: "创建备份"
|
||||
registered: "已注册 %{name}"
|
||||
released_msg: "已释放 — 用户名可被抢注"
|
||||
release_confirm: "释放 %{name}?"
|
||||
release_blurb: "一旦释放即可被认领——任何人都可以,包括你接下来轮换到的密钥。10 分钟内你无法注册另一个用户名。"
|
||||
releasing: "正在释放…"
|
||||
keep_it: "保留"
|
||||
release_it: "释放"
|
||||
username: "用户名"
|
||||
username_note: "显示为 you。在 goblin.st 上公开。付款保持加密。"
|
||||
release_username: "释放用户名"
|
||||
pick_username: "选择用户名 — 可选"
|
||||
working: "处理中…"
|
||||
claim: "注册"
|
||||
err_just_taken: "该用户名刚被占用"
|
||||
err_cooldown: "你刚释放了一个用户名 — 10 分钟内无法注册新用户名。"
|
||||
err_unreachable: "无法连接 goblin.st — 连接中断。请重试。"
|
||||
err_release: "无法释放:%{err}"
|
||||
avail_available: "可用!"
|
||||
avail_taken: "已被占用"
|
||||
avail_reserved: "已保留"
|
||||
avail_invalid: "用户名为 3–20 个字符:a–z、0–9、_ 或 -"
|
||||
avail_quarantined: "不可用"
|
||||
avail_unknown: "无法检查 — 连接中断。请重试。"
|
||||
advanced:
|
||||
title: "高级"
|
||||
intro: "来自 GRIM 的底层钱包工具。通常你用不到这些。"
|
||||
own_node_desc: "在本设备上同步完整的 Grin 节点,而不是信任公共节点。"
|
||||
own_node_active: "正在运行你自己的节点"
|
||||
repair: "修复钱包"
|
||||
repair_desc: "重新扫描链并恢复任何缺失的输出。这可能需要一些时间。"
|
||||
repair_unavailable: "需要先有已同步的节点连接。"
|
||||
repairing: "修复中… %{pct}%"
|
||||
restore: "恢复钱包"
|
||||
restore_desc: "删除本地数据并从助记词重建。如果修复无效,请使用此功能 — 之后需重新打开钱包。"
|
||||
restore_confirm: "再次点击以恢复"
|
||||
show_phrase: "恢复助记词"
|
||||
phrase_desc: "你的 24 个 grin 助记词 — 恢复资金的唯一方式。请离线且私密保存。"
|
||||
reveal: "显示助记词"
|
||||
hide: "隐藏"
|
||||
password: "钱包密码"
|
||||
wrong_password: "密码错误。"
|
||||
delete: "删除钱包"
|
||||
delete_desc: "从此设备永久移除该钱包。没有助记词,资金将无法找回。"
|
||||
delete_confirm: "再次点击以删除"
|
||||
manage_node: "管理节点连接"
|
||||
repair_confirm: "是的,立即修复"
|
||||
repair_confirm_note: "修复会重新扫描链,可能需要几分钟。"
|
||||
restore_confirm_note: "这会清除本地数据并从助记词重建——可能需要几分钟。"
|
||||
privacy:
|
||||
title: "网络隐私"
|
||||
intro: "Goblin 通过 Nym mixnet 发送其私密流量 — 这是一个五跳网络,可隐藏通信双方的身份,使中继无法将付款关联到你。"
|
||||
payments: "付款"
|
||||
payments_blurb: "每条携带 slatepack 的 nostr 消息。"
|
||||
usernames: "用户名"
|
||||
usernames_blurb: "往返 goblin.st 的 NIP-05 名称查询。"
|
||||
price_avatars: "价格"
|
||||
price_avatars_blurb: "金额旁显示的实时法币汇率。"
|
||||
over_mixnet: "经由 mixnet"
|
||||
direct_connection: "直接连接"
|
||||
grin_node: "Grin 节点"
|
||||
grin_node_blurb: "区块同步及向网络广播你的交易。这是公开的链上数据,对所有人都一样,且不与你的身份关联。"
|
||||
pairing:
|
||||
title: "配对"
|
||||
intro: "你的余额和金额以何种货币显示。"
|
||||
pair_with: "配对货币"
|
||||
rates_note: "汇率仅在开启配对时通过 Nym mixnet 获取 — 关闭后不会有任何汇率请求离开你的设备。"
|
||||
relays:
|
||||
title: "中继"
|
||||
intro: "付款消息会镜像到下方每个中继;只要有一个可达的中继即可收款。"
|
||||
your_relays: "你的中继"
|
||||
add_relay: "添加中继"
|
||||
add_relay_btn: "添加中继"
|
||||
save_reconnect: "保存并重新连接"
|
||||
none: "无"
|
||||
count: "%{n} 个中继"
|
||||
node:
|
||||
title: "节点"
|
||||
connection: "连接"
|
||||
integrated: "集成节点"
|
||||
applies_after: "在钱包锁定并再次解锁后生效。"
|
||||
add_external: "添加外部节点"
|
||||
api_secret_hint: "API 密钥(可选)"
|
||||
add_node: "添加节点"
|
||||
integrated_host: "集成节点"
|
||||
summary_syncing: "%{conn} · 同步中"
|
||||
summary_block: "区块 %{height} · %{conn}"
|
||||
nips:
|
||||
title: "nostr 与 NIPs"
|
||||
intro1: "Goblin 使用 nostr — 一种通过简单中继服务器传递签名消息的开放协议。你的钱包拥有自己的 nostr 身份:一个独立的随机密钥,刻意与你的资金和助记词保持独立。每笔付款都作为身份之间的端到端加密私信传输,slatepack 就包含在其中。"
|
||||
intro2: "goblin.st 是 Goblin 的名称服务:注册用户名会在此发布名称 → 密钥的映射(NIP-05),让人们可以付款给 you 而不必使用冗长的 npub。用户名是公开的;付款内容则永不公开。NIPs 是该协议的构建模块 — 点击任意一项可阅读规范。"
|
||||
n05_title: "名称"
|
||||
n05_blurb: "将 username@goblin.st 映射到你的密钥,让用户名像地址一样使用。"
|
||||
n17_title: "私密消息"
|
||||
n17_blurb: "每笔付款传输所用的加密私信信封。"
|
||||
n44_title: "加密"
|
||||
n44_blurb: "这些消息内部使用的认证加密算法。"
|
||||
n49_title: "密钥加密"
|
||||
n49_blurb: "私钥静态存储的方式,由你的密码锁定。"
|
||||
n59_title: "礼物包装"
|
||||
n59_blurb: "包装消息,使中继无法看到通信双方是谁。"
|
||||
n98_title: "HTTP 认证"
|
||||
n98_blurb: "为向 goblin.st 注册用户名的请求签名。"
|
||||
onboarding:
|
||||
intro:
|
||||
private_money_head: "私密货币"
|
||||
private_money_body: "Goblin 是一个 grin 钱包 — 链上无金额、无地址的数字现金。"
|
||||
send_like_message_head: "像发消息一样付款"
|
||||
send_like_message_body: "向 username 或 npub 付款,款项会作为端到端加密消息通过 nostr 和 Nym mixnet 送达 — 中间任何人都看不到金额或参与者。"
|
||||
yours_alone_head: "完全属于你"
|
||||
yours_alone_body: "密钥、用户名和历史记录都存于本设备。基于 GRIM 钱包构建。"
|
||||
get_started: "开始使用"
|
||||
footnote: "约需一分钟。之后一切均可更改。"
|
||||
node:
|
||||
kicker: "步骤 1 / 3 · 网络"
|
||||
title: "Goblin 该如何\n监视链?"
|
||||
own_title: "运行我自己的节点"
|
||||
own_badge: "私密"
|
||||
own_body: "无需信任任何人 — 钱包自行验证链。完成设置时在后台同步。"
|
||||
connect_title: "连接到节点"
|
||||
connect_badge: "即时"
|
||||
connect_body: "无需等待同步。你选择的节点可看到钱包的查询。"
|
||||
changeable: "随时可在 设置 → 节点 中更改。"
|
||||
continue: "继续"
|
||||
url_invalid: "节点 URL 必须以 http:// 或 https:// 开头"
|
||||
wallet:
|
||||
kicker: "步骤 2 / 3 · 钱包"
|
||||
title: "设置你的钱包"
|
||||
create_new: "新建"
|
||||
restore_from_seed: "从助记词恢复"
|
||||
name_hint: "钱包名称"
|
||||
password_hint: "密码"
|
||||
repeat_password_hint: "重复密码"
|
||||
restore_hint: "准备好你的助记词 — 下一步将输入。"
|
||||
create_hint: "接下来你会得到 24 个助记词以供抄写。它们就是钱 — 谁持有它们,谁就掌握你的资金。"
|
||||
continue: "继续"
|
||||
passwords_no_match: "密码不一致"
|
||||
words:
|
||||
kicker: "步骤 2 / 3 · 钱包"
|
||||
title_restore: "输入你的助记词"
|
||||
title_create: "抄下这些词"
|
||||
write_down_hint: "按顺序写在纸上。任何持有这些词的人都能取走你的资金;丢失这些词又丢失设备,资金将无法找回。"
|
||||
paste: "粘贴"
|
||||
scan_qr: "扫描二维码"
|
||||
copy_clipboard: "复制到剪贴板(不建议)"
|
||||
restore_wallet: "恢复钱包"
|
||||
wrote_them_down: "我已抄好"
|
||||
fill_every_word: "填写每个词 — 点击某个词进行编辑,或粘贴整个短语。"
|
||||
confirm:
|
||||
kicker: "步骤 2 / 3 · 钱包"
|
||||
title: "现在来验证"
|
||||
enter_hint: "输入你刚抄下的词。点击某个词进行输入。"
|
||||
paste: "粘贴"
|
||||
create_wallet: "创建钱包"
|
||||
keep_going: "继续 — 每个词,按顺序。"
|
||||
identity:
|
||||
kicker: "步骤 3 / 3 · 身份"
|
||||
title: "你的付款身份"
|
||||
key_being_made: "正在生成密钥…"
|
||||
connected_nym: "已通过 Nym 连接"
|
||||
connecting_nym: "正在通过 Nym 连接…"
|
||||
fresh_key_blurb: "一个不属于助记词的支付密钥——可随时轮换以保护隐私,且不影响你的资金。"
|
||||
clean_slate_blurb: "想要全新开始?随时换上一个全新密钥 — 新的你与旧的毫无关联。同一个钱包,焕然一新。"
|
||||
pick_username: "选择用户名 — 可选"
|
||||
username_blurb: "朋友支付给你的名称,而不是一长串密钥。可选——随时认领。"
|
||||
username_field_hint: "你的用户名"
|
||||
working: "处理中…"
|
||||
claim_username: "注册用户名"
|
||||
available_when_connected: "mixnet 连接后可用 — 或跳过,稍后注册。"
|
||||
youre: "你是 %{name}"
|
||||
claimed_title: "%{name} 已归你所有"
|
||||
claimed_blurb: "朋友现在可以用你的用户名向你付款。一切就绪——打开钱包吧。"
|
||||
open_wallet: "打开我的钱包"
|
||||
skip_for_now: "暂时跳过"
|
||||
import_existing: "已有 Goblin 身份?导入它"
|
||||
import_title: "导入你的身份"
|
||||
import_blurb: "粘贴你的 nsec 或选择一个 .backup 文件,保留你现有的密钥和用户名,而不是这个新的。"
|
||||
errors:
|
||||
cant_open: "无法打开钱包:%{err}"
|
||||
cant_create: "无法创建钱包:%{err}"
|
||||
send:
|
||||
scan_to_request: "扫码请求"
|
||||
scan_to_pay: "扫码支付"
|
||||
tab_scan: "扫描"
|
||||
tab_my_code: "我的二维码"
|
||||
request_from: "向谁请求"
|
||||
send_to: "发送给"
|
||||
search_hint: "handle、npub 或名称"
|
||||
suggested: "%{icon} 建议"
|
||||
no_contacts: "暂无联系人。通过 handle 查找某人。"
|
||||
no_profile: "无资料"
|
||||
tag_contact: "联系人"
|
||||
tag_on_nostr: "在 nostr 上"
|
||||
searching_nostr: "正在搜索 nostr…"
|
||||
unverified_title: "向未验证的密钥付款?"
|
||||
unverified_body: "此密钥未发布 nostr 资料 — 它可能是全新的、匿名的或输错的。发送前请仔细核对是否正确。"
|
||||
keep_looking: "继续查找"
|
||||
pay_anyway: "仍然付款"
|
||||
scan_not_recipient: "该二维码不是 goblin 收款方 — 应为 npub 或 handle"
|
||||
scan_prompt: "将 goblin 二维码对准取景框以激活"
|
||||
scan_to_pay_me: "扫码向我付款"
|
||||
share_btn: "%{icon} 分享"
|
||||
share_message: "在 Goblin 上向我付款 — %{handle}\n%{link}\nnpub:%{npub}"
|
||||
none_found: "未找到与 %{label} 匹配的人"
|
||||
enter_recipient: "输入 handle、npub 或名称"
|
||||
amount_title: "金额"
|
||||
to_name: "发送给 %{name}"
|
||||
not_enough: "你的 grin 余额不足"
|
||||
max: "最大"
|
||||
note_label: "备注"
|
||||
note_hint: "添加备注…"
|
||||
add_note: "添加备注"
|
||||
edit_note: "编辑备注"
|
||||
note_cancel: "取消"
|
||||
note_save: "保存"
|
||||
review_btn: "查看"
|
||||
confirm_request: "确认请求"
|
||||
review_title: "查看"
|
||||
requesting_from: "向 %{name} 请求"
|
||||
youre_sending: "你正在发送给 %{name}"
|
||||
row_from: "付款方"
|
||||
row_to: "收款方"
|
||||
row_note: "备注"
|
||||
row_they_pay: "对方支付"
|
||||
row_they_pay_val: "仅当对方同意时"
|
||||
row_delivery: "传输"
|
||||
row_delivery_val: "NIP-44 加密,经由 Nym"
|
||||
row_network_fee: "网络费用"
|
||||
row_network_fee_val: "从你的余额中扣除"
|
||||
row_privacy: "隐私"
|
||||
row_privacy_val: "Mimblewimble + Nym"
|
||||
send_request_btn: "发送请求"
|
||||
request_approve_hint: "对方将收到一条待同意的请求"
|
||||
hold_to_send: "长按发送"
|
||||
lower_amount: "返回并降低金额"
|
||||
hold_confirm_hint: "按住以确认"
|
||||
requesting: "正在请求…"
|
||||
sending: "正在发送…"
|
||||
they: "对方"
|
||||
request_blocked: "%{who} 不接受请求。请对方改为向你发送 grin。"
|
||||
failed_request_title: "请求失败"
|
||||
failed_send_title: "发送失败"
|
||||
failed_request_body: "我们无法送达请求。请对方改为向你发送 grin。"
|
||||
failed_send_body: "付款未送达。你的 grin 是安全的 — 请重试。"
|
||||
try_again_btn: "重试"
|
||||
close_btn: "关闭"
|
||||
success:
|
||||
requested: "已请求"
|
||||
sent: "已发送"
|
||||
from: "来自"
|
||||
to: "发往"
|
||||
subtitle: "%{dir} %{who} · 刚刚"
|
||||
done_btn: "完成"
|
||||
receipt_btn: "收据"
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Goblin</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>goblin</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>mw.gri.macos</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Goblin</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.3.6</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Goblin needs an access to your camera to scan QR code.</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Apple SimpleText document</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.apple.traditional-mac-plain-text</string>
|
||||
</array>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>Document</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Unknown document</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>NSDocumentClass</key>
|
||||
<string>Document</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.finance</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>2024</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,2 @@
|
||||
!.gitignore
|
||||
grim
|
||||