feat(wallet): new/import wallets + multi-wallet i18n (P3)

Completes the wallet-files dialog and localizes the feature.

- Create a new wallet: a name is normalized to wallet-<name>.dat and opened
  via switchToWallet; the daemon mints it (seed-backed, -usemnemonic=1) on
  first load. Rejects blank names + name collisions.
- Import: out-of-datadir wallets get an Import action that copies the file
  into the datadir (never overwriting) under a wallet-*.dat name, then opens
  it — the daemon only loads plain filenames from the datadir.
- i18n: the 32 new multi-wallet / per-wallet-contacts / mining-payout strings
  translated into all 8 languages (de/es/fr/ja/ko/pt/ru/zh), CJK subset font
  rebuilt. Verified: the Chinese Wallets dialog renders with no missing glyphs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 17:54:29 -05:00
parent e8a8ce68b2
commit d23ee9b75f
12 changed files with 335 additions and 3 deletions

Binary file not shown.

View File

@@ -218,6 +218,9 @@
"console_welcome": "Willkommen bei ObsidianDragon Konsole",
"console_zoom_in": "Vergrößern",
"console_zoom_out": "Verkleinern",
"contact_global": "In jeder Wallet anzeigen (globaler Kontakt)",
"contact_global_badge_tt": "Globaler Kontakt — in jeder Wallet sichtbar",
"contact_global_tt": "Ein: Dieser Kontakt bleibt sichtbar, egal welche Wallet Sie laden. Aus: Er gehört nur zur aktuellen Wallet.",
"copied": "Kopiert!",
"copy": "Kopieren",
"copy_address": "Vollständige Adresse kopieren",
@@ -489,6 +492,7 @@
"mining_on": "Mining ist AN",
"mining_open_in_explorer": "Im Explorer öffnen",
"mining_payout_address": "Auszahlungsadresse",
"mining_payout_foreign": "⚠ Diese Auszahlungsadresse befindet sich nicht in Ihrer aktuellen Wallet — geschürfte Belohnungen würden an eine andere Wallet gehen. Aktualisieren Sie sie, wenn Sie die Wallet gewechselt haben.",
"mining_payout_tooltip": "Adresse für Mining-Belohnungen",
"mining_pool": "Pool",
"mining_pool_hashrate": "Pool-Hashrate",
@@ -967,6 +971,7 @@
"tt_ui_opacity": "Karten- und Seitenleisten-Deckkraft (100%% = vollständig undurchsichtig, niedriger = durchsichtiger)",
"tt_validate": "Prüfen, ob eine DragonX-Adresse gültig ist",
"tt_verbose": "Detaillierte Verbindungsdiagnosen,\\nDaemon-Status und Port-Besitzer-Info\\nin der Konsolen-Registerkarte protokollieren",
"tt_wallets_button": "Ihre Wallet-Dateien auflisten und zwischen ihnen wechseln",
"tt_website": "Die DragonX-Website öffnen",
"tt_window_opacity": "Hintergrund-Deckkraft (niedriger = Desktop durch Fenster sichtbar)",
"tt_wizard": "Den Ersteinrichtungsassistenten erneut ausführen\\nDer Daemon wird neu gestartet",
@@ -1012,6 +1017,33 @@
"wallet": "WALLET",
"wallet_empty": "Ihre Wallet ist leer",
"wallet_empty_hint": "Wechseln Sie zu Empfangen, um Ihre Adresse zu erhalten und Gelder zu empfangen.",
"wallets_active": "Aktiv",
"wallets_add": "Ordner hinzufügen",
"wallets_add_folder": "Auch einen weiteren Ordner nach Wallet-Dateien durchsuchen:",
"wallets_button": "Wallets…",
"wallets_col_addresses": "Adressen",
"wallets_col_balance": "Guthaben",
"wallets_col_name": "Wallet",
"wallets_col_opened": "Zuletzt geöffnet",
"wallets_col_size": "Größe",
"wallets_create": "Wallet erstellen",
"wallets_creating": "Wallet wird erstellt — der Node wird neu gestartet…",
"wallets_current": "aktuell",
"wallets_exists": "Eine Wallet mit diesem Namen existiert bereits.",
"wallets_folder_hint": "/pfad/zum/ordner mit .dat-Wallet-Dateien",
"wallets_folder_invalid": "Dieser Ordner existiert nicht.",
"wallets_import": "Importieren",
"wallets_import_failed": "Diese Wallet-Datei konnte nicht importiert werden.",
"wallets_import_hint": "Zum Öffnen importieren",
"wallets_import_tt": "Diese Wallet befindet sich außerhalb des Datenverzeichnisses. Beim Öffnen wird sie zuerst hineinkopiert (demnächst verfügbar).",
"wallets_imported": "Wallet importiert — wird gewechselt…",
"wallets_intro": "Wallet-Dateien in Ihrem Datenverzeichnis (und in beliebigen Ordnern, die Sie hinzufügen). Öffnen Sie eine, um zu wechseln — der Node wird neu gestartet, um sie zu laden, und jede Wallet behält ihre eigenen Daten.",
"wallets_name_invalid": "Bitte geben Sie einen gültigen Wallet-Namen ein.",
"wallets_never": "Nie",
"wallets_new_hint": "Name (z. B. Ersparnisse)",
"wallets_new_label": "Eine neue Wallet erstellen:",
"wallets_open": "Öffnen",
"wallets_title": "Wallets",
"warning": "Warnung",
"warning_upper": "WARNUNG!",
"website": "Webseite",

View File

@@ -218,6 +218,9 @@
"console_welcome": "Bienvenido a la Consola de ObsidianDragon",
"console_zoom_in": "Acercar",
"console_zoom_out": "Alejar",
"contact_global": "Mostrar en todas las carteras (contacto global)",
"contact_global_badge_tt": "Contacto global — visible en todas las carteras",
"contact_global_tt": "Activado: este contacto permanece visible sin importar qué cartera cargues. Desactivado: pertenece solo a la cartera actual.",
"copied": "¡Copiado!",
"copy": "Copiar",
"copy_address": "Copiar Dirección Completa",
@@ -489,6 +492,7 @@
"mining_on": "La minería está ENCENDIDA",
"mining_open_in_explorer": "Abrir en explorador",
"mining_payout_address": "Dirección de Pago",
"mining_payout_foreign": "⚠ Esta dirección de pago no está en tu cartera actual — las recompensas minadas irían a otra cartera. Actualízala si cambiaste de cartera.",
"mining_payout_tooltip": "Dirección para recibir recompensas de minería",
"mining_pool": "Pool",
"mining_pool_hashrate": "Hashrate del Pool",
@@ -967,6 +971,7 @@
"tt_ui_opacity": "Opacidad de tarjetas y barra lateral (100%% = totalmente opaco, menor = más transparente)",
"tt_validate": "Comprobar si una dirección DragonX es válida",
"tt_verbose": "Registrar diagnósticos detallados de conexión,\\nestado del daemon e info de propietario de puerto\\nen la pestaña de Consola",
"tt_wallets_button": "Enumera tus archivos de cartera y cambia entre ellos",
"tt_website": "Abrir el sitio web de DragonX",
"tt_window_opacity": "Opacidad del fondo (menor = escritorio visible a través de la ventana)",
"tt_wizard": "Volver a ejecutar el asistente de configuración inicial\\nEl daemon será reiniciado",
@@ -1012,6 +1017,33 @@
"wallet": "CARTERA",
"wallet_empty": "Tu cartera está vacía",
"wallet_empty_hint": "Cambia a Recibir para obtener tu dirección y empezar a recibir fondos.",
"wallets_active": "Activa",
"wallets_add": "Agregar carpeta",
"wallets_add_folder": "Escanear también otra carpeta en busca de archivos de cartera:",
"wallets_button": "Carteras…",
"wallets_col_addresses": "Direcciones",
"wallets_col_balance": "Saldo",
"wallets_col_name": "Cartera",
"wallets_col_opened": "Última apertura",
"wallets_col_size": "Tamaño",
"wallets_create": "Crear cartera",
"wallets_creating": "Creando cartera — el nodo se reiniciará…",
"wallets_current": "actual",
"wallets_exists": "Ya existe una cartera con ese nombre.",
"wallets_folder_hint": "/ruta/a/carpeta con archivos .dat de cartera",
"wallets_folder_invalid": "Esa carpeta no existe.",
"wallets_import": "Importar",
"wallets_import_failed": "No se pudo importar ese archivo de cartera.",
"wallets_import_hint": "Importar para abrir",
"wallets_import_tt": "Esta cartera está fuera del directorio de datos. Al abrirla se copiará primero dentro (próximamente).",
"wallets_imported": "Cartera importada — cambiando…",
"wallets_intro": "Archivos de cartera en tu directorio de datos (y en las carpetas que agregues). Abre uno para cambiar: el nodo se reinicia para cargarlo y cada cartera conserva sus propios datos.",
"wallets_name_invalid": "Introduce un nombre de cartera válido.",
"wallets_never": "Nunca",
"wallets_new_hint": "Nombre (p. ej. ahorros)",
"wallets_new_label": "Crear una nueva cartera:",
"wallets_open": "Abrir",
"wallets_title": "Carteras",
"warning": "Advertencia",
"warning_upper": "¡ADVERTENCIA!",
"website": "Sitio Web",

View File

@@ -218,6 +218,9 @@
"console_welcome": "Bienvenue dans la console ObsidianDragon",
"console_zoom_in": "Agrandir",
"console_zoom_out": "Réduire",
"contact_global": "Afficher dans tous les portefeuilles (contact global)",
"contact_global_badge_tt": "Contact global — visible dans tous les portefeuilles",
"contact_global_tt": "Activé : ce contact reste visible quel que soit le portefeuille chargé. Désactivé : il appartient au portefeuille actuel uniquement.",
"copied": "Copié !",
"copy": "Copier",
"copy_address": "Copier l'adresse complète",
@@ -489,6 +492,7 @@
"mining_on": "Le minage est ACTIVÉ",
"mining_open_in_explorer": "Ouvrir dans l'explorateur",
"mining_payout_address": "Adresse de paiement",
"mining_payout_foreign": "⚠ Cette adresse de paiement ne fait pas partie de votre portefeuille actuel — les récompenses minées iraient vers un autre portefeuille. Mettez-la à jour si vous avez changé de portefeuille.",
"mining_payout_tooltip": "Adresse pour recevoir les récompenses de minage",
"mining_pool": "Pool",
"mining_pool_hashrate": "Hashrate du pool",
@@ -967,6 +971,7 @@
"tt_ui_opacity": "Opacité des cartes et de la barre latérale (100%% = entièrement opaque, plus bas = plus transparent)",
"tt_validate": "Vérifier si une adresse DragonX est valide",
"tt_verbose": "Journaliser les diagnostics de connexion détaillés,\\nl'état du daemon et les informations de propriétaire de port\\ndans l'onglet Console",
"tt_wallets_button": "Répertoriez vos fichiers de portefeuille et passez de l'un à l'autre",
"tt_website": "Ouvrir le site web DragonX",
"tt_window_opacity": "Opacité de l'arrière-plan (plus bas = bureau visible à travers la fenêtre)",
"tt_wizard": "Relancer l'assistant de configuration initiale\\nLe daemon sera redémarré",
@@ -1012,6 +1017,33 @@
"wallet": "PORTEFEUILLE",
"wallet_empty": "Votre portefeuille est vide",
"wallet_empty_hint": "Passez à Recevoir pour obtenir votre adresse et commencer à recevoir des fonds.",
"wallets_active": "Actif",
"wallets_add": "Ajouter le dossier",
"wallets_add_folder": "Analyser aussi un autre dossier à la recherche de fichiers de portefeuille :",
"wallets_button": "Portefeuilles…",
"wallets_col_addresses": "Adresses",
"wallets_col_balance": "Solde",
"wallets_col_name": "Portefeuille",
"wallets_col_opened": "Dernière ouverture",
"wallets_col_size": "Taille",
"wallets_create": "Créer le portefeuille",
"wallets_creating": "Création du portefeuille — le nœud va redémarrer…",
"wallets_current": "actuel",
"wallets_exists": "Un portefeuille portant ce nom existe déjà.",
"wallets_folder_hint": "/chemin/vers/le/dossier contenant les fichiers .dat",
"wallets_folder_invalid": "Ce dossier n'existe pas.",
"wallets_import": "Importer",
"wallets_import_failed": "Impossible d'importer ce fichier de portefeuille.",
"wallets_import_hint": "Importer pour ouvrir",
"wallets_import_tt": "Ce portefeuille se trouve hors du répertoire de données. L'ouvrir le copiera d'abord dedans (bientôt disponible).",
"wallets_imported": "Portefeuille importé — changement en cours…",
"wallets_intro": "Fichiers de portefeuille dans votre répertoire de données (et tout dossier que vous ajoutez). Ouvrez-en un pour changer — le nœud redémarre pour le charger, et chaque portefeuille conserve ses propres données.",
"wallets_name_invalid": "Veuillez saisir un nom de portefeuille valide.",
"wallets_never": "Jamais",
"wallets_new_hint": "Nom (par ex. épargne)",
"wallets_new_label": "Créer un nouveau portefeuille :",
"wallets_open": "Ouvrir",
"wallets_title": "Portefeuilles",
"warning": "Attention",
"warning_upper": "ATTENTION !",
"website": "Site web",

View File

@@ -218,6 +218,9 @@
"console_welcome": "ObsidianDragonコンソールへようこそ",
"console_zoom_in": "拡大",
"console_zoom_out": "縮小",
"contact_global": "すべてのウォレットで表示(グローバル連絡先)",
"contact_global_badge_tt": "グローバル連絡先 — すべてのウォレットで表示",
"contact_global_tt": "オン:この連絡先はどのウォレットを読み込んでも表示されます。オフ:現在のウォレットにのみ属します。",
"copied": "コピーしました!",
"copy": "コピー",
"copy_address": "完全なアドレスをコピー",
@@ -489,6 +492,7 @@
"mining_on": "マイニングはオンです",
"mining_open_in_explorer": "エクスプローラーで開く",
"mining_payout_address": "支払いアドレス",
"mining_payout_foreign": "⚠ この支払いアドレスは現在のウォレットに含まれていません — マイニング報酬が別のウォレットに送られます。ウォレットを切り替えた場合は更新してください。",
"mining_payout_tooltip": "マイニング報酬の受取アドレス",
"mining_pool": "プール",
"mining_pool_hashrate": "プールハッシュレート",
@@ -967,6 +971,7 @@
"tt_ui_opacity": "カードとサイドバーの不透明度100%% = 完全不透明、低い = より透過)",
"tt_validate": "DragonX アドレスが有効かどうかを確認",
"tt_verbose": "詳細な接続診断、デーモン状態、\\nポート所有者情報をコンソールタブに記録",
"tt_wallets_button": "ウォレットファイルを一覧表示して切り替えます",
"tt_website": "DragonX ウェブサイトを開く",
"tt_window_opacity": "背景の不透明度(低い = デスクトップがウィンドウ越しに見える)",
"tt_wizard": "初期セットアップウィザードを再実行\\nデーモンは再起動されます",
@@ -1012,6 +1017,33 @@
"wallet": "ウォレット",
"wallet_empty": "ウォレットは空です",
"wallet_empty_hint": "受信に切り替えてアドレスを取得し、資金の受け取りを開始してください。",
"wallets_active": "アクティブ",
"wallets_add": "フォルダを追加",
"wallets_add_folder": "ウォレットファイルを検索する別のフォルダを追加:",
"wallets_button": "ウォレット…",
"wallets_col_addresses": "アドレス",
"wallets_col_balance": "残高",
"wallets_col_name": "ウォレット",
"wallets_col_opened": "最終使用",
"wallets_col_size": "サイズ",
"wallets_create": "ウォレットを作成",
"wallets_creating": "ウォレットを作成中 — ノードが再起動します…",
"wallets_current": "現在",
"wallets_exists": "その名前のウォレットは既に存在します。",
"wallets_folder_hint": "wallet .dat ファイルのあるフォルダのパス",
"wallets_folder_invalid": "そのフォルダは存在しません。",
"wallets_import": "インポート",
"wallets_import_failed": "そのウォレットファイルをインポートできませんでした。",
"wallets_import_hint": "インポートして開く",
"wallets_import_tt": "このウォレットはデータディレクトリの外にあります。開くには先にコピーが必要です(近日対応予定)。",
"wallets_imported": "ウォレットをインポートしました — 切り替え中…",
"wallets_intro": "データディレクトリ(および追加したフォルダ)内のウォレットファイルです。開いて切り替えると、それを読み込むためにノードが再起動します。各ウォレットは個別のデータを保持します。",
"wallets_name_invalid": "有効なウォレット名を入力してください。",
"wallets_never": "なし",
"wallets_new_hint": "名前savings",
"wallets_new_label": "新しいウォレットを作成:",
"wallets_open": "開く",
"wallets_title": "ウォレット",
"warning": "警告",
"warning_upper": "警告!",
"website": "ウェブサイト",

View File

@@ -218,6 +218,9 @@
"console_welcome": "ObsidianDragon 콘솔에 오신 것을 환영합니다",
"console_zoom_in": "확대",
"console_zoom_out": "축소",
"contact_global": "모든 지갑에 표시(전역 연락처)",
"contact_global_badge_tt": "전역 연락처 — 모든 지갑에서 표시됨",
"contact_global_tt": "켜짐: 어떤 지갑을 불러오든 이 연락처가 계속 표시됩니다. 꺼짐: 현재 지갑에만 속합니다.",
"copied": "복사됨!",
"copy": "복사",
"copy_address": "전체 주소 복사",
@@ -489,6 +492,7 @@
"mining_on": "채굴이 켜져 있습니다",
"mining_open_in_explorer": "탐색기에서 열기",
"mining_payout_address": "지급 주소",
"mining_payout_foreign": "⚠ 이 지급 주소는 현재 지갑에 없습니다 — 채굴한 보상이 다른 지갑으로 전송됩니다. 지갑을 전환했다면 주소를 업데이트하세요.",
"mining_payout_tooltip": "채굴 보상 수신 주소",
"mining_pool": "풀",
"mining_pool_hashrate": "풀 해시레이트",
@@ -967,6 +971,7 @@
"tt_ui_opacity": "카드 및 사이드바 불투명도 (100%% = 완전 불투명, 낮을수록 더 투명)",
"tt_validate": "DragonX 주소가 유효한지 확인",
"tt_verbose": "콘솔 탭에 상세 연결 진단,\\n데몬 상태 및 포트 소유자 정보 기록",
"tt_wallets_button": "지갑 파일 목록을 보고 전환합니다",
"tt_website": "DragonX 웹사이트 열기",
"tt_window_opacity": "배경 불투명도 (낮을수록 = 창을 통해 바탕 화면이 보임)",
"tt_wizard": "초기 설정 마법사 다시 실행\\n데몬이 재시작됩니다",
@@ -1012,6 +1017,33 @@
"wallet": "지갑",
"wallet_empty": "지갑이 비어 있습니다",
"wallet_empty_hint": "수신으로 전환하여 주소를 받고 자금 수신을 시작하세요.",
"wallets_active": "활성",
"wallets_add": "폴더 추가",
"wallets_add_folder": "지갑 파일을 검색할 폴더를 추가로 지정:",
"wallets_button": "지갑…",
"wallets_col_addresses": "주소",
"wallets_col_balance": "잔액",
"wallets_col_name": "지갑",
"wallets_col_opened": "마지막 열람",
"wallets_col_size": "크기",
"wallets_create": "지갑 만들기",
"wallets_creating": "지갑을 만드는 중 — 노드가 재시작됩니다…",
"wallets_current": "현재",
"wallets_exists": "같은 이름의 지갑이 이미 존재합니다.",
"wallets_folder_hint": "wallet-*.dat 파일이 있는 폴더 경로",
"wallets_folder_invalid": "해당 폴더가 존재하지 않습니다.",
"wallets_import": "가져오기",
"wallets_import_failed": "해당 지갑 파일을 가져올 수 없습니다.",
"wallets_import_hint": "가져와서 열기",
"wallets_import_tt": "이 지갑은 데이터 디렉터리 밖에 있습니다. 열면 먼저 안으로 복사됩니다(출시 예정).",
"wallets_imported": "지갑을 가져왔습니다 — 전환 중…",
"wallets_intro": "데이터 디렉터리(및 추가한 폴더)에 있는 지갑 파일입니다. 하나를 열면 전환됩니다 — 노드가 재시작되어 해당 지갑을 불러오며, 각 지갑은 고유한 데이터를 유지합니다.",
"wallets_name_invalid": "유효한 지갑 이름을 입력하세요.",
"wallets_never": "없음",
"wallets_new_hint": "이름(예: savings)",
"wallets_new_label": "새 지갑 만들기:",
"wallets_open": "열기",
"wallets_title": "지갑",
"warning": "경고",
"warning_upper": "경고!",
"website": "웹사이트",

View File

@@ -218,6 +218,9 @@
"console_welcome": "Bem-vindo ao Console ObsidianDragon",
"console_zoom_in": "Aumentar zoom",
"console_zoom_out": "Diminuir zoom",
"contact_global": "Mostrar em todas as carteiras (contato global)",
"contact_global_badge_tt": "Contato global — visível em todas as carteiras",
"contact_global_tt": "Ativado: este contato permanece visível em qualquer carteira que você carregar. Desativado: ele pertence apenas à carteira atual.",
"copied": "Copiado!",
"copy": "Copiar",
"copy_address": "Copiar Endereço Completo",
@@ -489,6 +492,7 @@
"mining_on": "Mineração está LIGADA",
"mining_open_in_explorer": "Abrir no explorador",
"mining_payout_address": "Endereço de Pagamento",
"mining_payout_foreign": "⚠ Este endereço de pagamento não está na sua carteira atual — as recompensas mineradas iriam para uma carteira diferente. Atualize-o se você trocou de carteira.",
"mining_payout_tooltip": "Endereço para receber recompensas de mineração",
"mining_pool": "Pool",
"mining_pool_hashrate": "Hashrate do Pool",
@@ -967,6 +971,7 @@
"tt_ui_opacity": "Opacidade de cartões e barra lateral (100%% = totalmente opaco, menor = mais transparente)",
"tt_validate": "Verificar se um endereço DragonX é válido",
"tt_verbose": "Registrar diagnósticos detalhados de conexão,\\nestado do daemon e info de proprietário de porta\\nna aba Console",
"tt_wallets_button": "Liste os arquivos de carteira e alterne entre eles",
"tt_website": "Abrir o site do DragonX",
"tt_window_opacity": "Opacidade do fundo (menor = área de trabalho visível através da janela)",
"tt_wizard": "Executar novamente o assistente de configuração inicial\\nO daemon será reiniciado",
@@ -1012,6 +1017,33 @@
"wallet": "CARTEIRA",
"wallet_empty": "Sua carteira está vazia",
"wallet_empty_hint": "Mude para Receber para obter seu endereço e começar a receber fundos.",
"wallets_active": "Ativa",
"wallets_add": "Adicionar pasta",
"wallets_add_folder": "Verificar também outra pasta em busca de arquivos de carteira:",
"wallets_button": "Carteiras…",
"wallets_col_addresses": "Endereços",
"wallets_col_balance": "Saldo",
"wallets_col_name": "Carteira",
"wallets_col_opened": "Aberta pela última vez",
"wallets_col_size": "Tamanho",
"wallets_create": "Criar carteira",
"wallets_creating": "Criando carteira — o nó será reiniciado…",
"wallets_current": "atual",
"wallets_exists": "Já existe uma carteira com esse nome.",
"wallets_folder_hint": "/caminho/para/pasta com arquivos .dat de carteira",
"wallets_folder_invalid": "Essa pasta não existe.",
"wallets_import": "Importar",
"wallets_import_failed": "Não foi possível importar esse arquivo de carteira.",
"wallets_import_hint": "Importar para abrir",
"wallets_import_tt": "Esta carteira está fora do diretório de dados. Ao abri-la, ela será copiada para lá primeiro (em breve).",
"wallets_imported": "Carteira importada — alternando…",
"wallets_intro": "Arquivos de carteira no seu diretório de dados (e em quaisquer pastas que você adicionar). Abra um para alternar — o nó reinicia para carregá-lo, e cada carteira mantém seus próprios dados.",
"wallets_name_invalid": "Insira um nome de carteira válido.",
"wallets_never": "Nunca",
"wallets_new_hint": "Nome (ex.: poupança)",
"wallets_new_label": "Criar uma nova carteira:",
"wallets_open": "Abrir",
"wallets_title": "Carteiras",
"warning": "Aviso",
"warning_upper": "AVISO!",
"website": "Website",

View File

@@ -218,6 +218,9 @@
"console_welcome": "Добро пожаловать в консоль ObsidianDragon",
"console_zoom_in": "Увеличить",
"console_zoom_out": "Уменьшить",
"contact_global": "Показывать во всех кошельках (глобальный контакт)",
"contact_global_badge_tt": "Глобальный контакт — виден во всех кошельках",
"contact_global_tt": "Вкл.: этот контакт остаётся видимым, какой бы кошелёк вы ни загрузили. Выкл.: он принадлежит только текущему кошельку.",
"copied": "Скопировано!",
"copy": "Копировать",
"copy_address": "Копировать полный адрес",
@@ -489,6 +492,7 @@
"mining_on": "Майнинг ВКЛЮЧЁН",
"mining_open_in_explorer": "Открыть в обозревателе",
"mining_payout_address": "Адрес выплат",
"mining_payout_foreign": "⚠ Этот адрес выплат отсутствует в вашем текущем кошельке — намайненные вознаграждения будут отправлены в другой кошелёк. Обновите его, если вы сменили кошелёк.",
"mining_payout_tooltip": "Адрес для получения вознаграждений за майнинг",
"mining_pool": "Пул",
"mining_pool_hashrate": "Хешрейт пула",
@@ -967,6 +971,7 @@
"tt_ui_opacity": "Непрозрачность карточек и боковой панели (100%% = полностью непрозрачно, ниже = прозрачнее)",
"tt_validate": "Проверить, действителен ли адрес DragonX",
"tt_verbose": "Записывать подробную диагностику подключений,\\nсостояние демона и информацию о владельце порта\\nна вкладке Консоль",
"tt_wallets_button": "Показать файлы кошельков и переключаться между ними",
"tt_website": "Открыть сайт DragonX",
"tt_window_opacity": "Непрозрачность фона (ниже = рабочий стол виден сквозь окно)",
"tt_wizard": "Повторно запустить мастер начальной настройки\\nДемон будет перезапущен",
@@ -1012,6 +1017,33 @@
"wallet": "КОШЕЛЁК",
"wallet_empty": "Ваш кошелёк пуст",
"wallet_empty_hint": "Перейдите к получению, чтобы получить свой адрес и начать получать средства.",
"wallets_active": "Активен",
"wallets_add": "Добавить папку",
"wallets_add_folder": "Также сканировать другую папку на наличие файлов кошельков:",
"wallets_button": "Кошельки…",
"wallets_col_addresses": "Адреса",
"wallets_col_balance": "Баланс",
"wallets_col_name": "Кошелёк",
"wallets_col_opened": "Последнее открытие",
"wallets_col_size": "Размер",
"wallets_create": "Создать кошелёк",
"wallets_creating": "Создание кошелька — узел будет перезапущен…",
"wallets_current": "текущий",
"wallets_exists": "Кошелёк с таким именем уже существует.",
"wallets_folder_hint": "/путь/к/папке с файлами кошельков .dat",
"wallets_folder_invalid": "Такой папки не существует.",
"wallets_import": "Импорт",
"wallets_import_failed": "Не удалось импортировать этот файл кошелька.",
"wallets_import_hint": "Импортировать для открытия",
"wallets_import_tt": "Этот кошелёк находится вне папки данных. При открытии он сначала будет скопирован в неё (скоро).",
"wallets_imported": "Кошелёк импортирован — переключение…",
"wallets_intro": "Файлы кошельков в вашей папке данных (и в любых добавленных вами папках). Откройте один, чтобы переключиться — узел перезапустится, чтобы загрузить его, и у каждого кошелька свои данные.",
"wallets_name_invalid": "Введите допустимое имя кошелька.",
"wallets_never": "Никогда",
"wallets_new_hint": "Имя (например, savings)",
"wallets_new_label": "Создать новый кошелёк:",
"wallets_open": "Открыть",
"wallets_title": "Кошельки",
"warning": "Предупреждение",
"warning_upper": "ПРЕДУПРЕЖДЕНИЕ!",
"website": "Веб-сайт",

View File

@@ -218,6 +218,9 @@
"console_welcome": "欢迎使用 ObsidianDragon 控制台",
"console_zoom_in": "放大",
"console_zoom_out": "缩小",
"contact_global": "在每个钱包中显示(全局联系人)",
"contact_global_badge_tt": "全局联系人——在每个钱包中可见",
"contact_global_tt": "开启:无论您加载哪个钱包,此联系人都保持可见。关闭:它仅属于当前钱包。",
"copied": "已复制!",
"copy": "复制",
"copy_address": "复制完整地址",
@@ -489,6 +492,7 @@
"mining_on": "挖矿已开启",
"mining_open_in_explorer": "在浏览器中打开",
"mining_payout_address": "支付地址",
"mining_payout_foreign": "⚠ 此支付地址不在您当前的钱包中——挖矿奖励将进入另一个钱包。如果您切换过钱包,请更新它。",
"mining_payout_tooltip": "接收挖矿奖励的地址",
"mining_pool": "矿池",
"mining_pool_hashrate": "矿池算力",
@@ -967,6 +971,7 @@
"tt_ui_opacity": "卡片和侧边栏不透明度100%% = 完全不透明,越低越透明)",
"tt_validate": "检查 DragonX 地址是否有效",
"tt_verbose": "将详细连接诊断、守护进程状态\\n和端口所有者信息记录到控制台选项卡",
"tt_wallets_button": "列出您的钱包文件并在它们之间切换",
"tt_website": "打开 DragonX 网站",
"tt_window_opacity": "背景不透明度(越低 = 桌面透过窗口可见)",
"tt_wizard": "重新运行初始设置向导\\n守护进程将被重启",
@@ -1012,6 +1017,33 @@
"wallet": "钱包",
"wallet_empty": "您的钱包是空的",
"wallet_empty_hint": "切换到接收页面获取您的地址并开始接收资金。",
"wallets_active": "活跃",
"wallets_add": "添加文件夹",
"wallets_add_folder": "同时扫描另一个文件夹中的钱包文件:",
"wallets_button": "钱包…",
"wallets_col_addresses": "地址",
"wallets_col_balance": "余额",
"wallets_col_name": "钱包",
"wallets_col_opened": "上次打开",
"wallets_col_size": "大小",
"wallets_create": "创建钱包",
"wallets_creating": "正在创建钱包——节点将重启…",
"wallets_current": "当前",
"wallets_exists": "已存在同名钱包。",
"wallets_folder_hint": "/含 .dat 钱包文件的文件夹路径",
"wallets_folder_invalid": "该文件夹不存在。",
"wallets_import": "导入",
"wallets_import_failed": "无法导入该钱包文件。",
"wallets_import_hint": "导入以打开",
"wallets_import_tt": "此钱包位于数据目录之外。打开它会先将其复制进来(即将推出)。",
"wallets_imported": "钱包已导入——正在切换…",
"wallets_intro": "位于您数据目录(以及您添加的任何文件夹)中的钱包文件。打开一个即可切换——节点将重启以加载它,且每个钱包各自保留其数据。",
"wallets_name_invalid": "请输入有效的钱包名称。",
"wallets_never": "从未",
"wallets_new_hint": "名称(例如 savings",
"wallets_new_label": "创建新钱包:",
"wallets_open": "打开",
"wallets_title": "钱包",
"warning": "警告",
"warning_upper": "警告!",
"website": "网站",

View File

@@ -282,6 +282,12 @@ void App::buildSweepCatalog()
e2.lastOpenedEpoch = 1719400000; e2.syncedHere = true;
a.walletIndex().upsert(e1);
a.walletIndex().upsert(e2);
// An out-of-datadir wallet (in an extra folder) so the Import action shows too.
const std::string extra = util::Platform::getConfigDir() + "extra-wallets";
fs::create_directories(extra, ec);
if (!fs::exists(extra + "/my-wallet.dat", ec))
std::ofstream(extra + "/my-wallet.dat", std::ios::binary) << std::string(80000, '\0');
a.walletIndex().addExtraFolder(extra);
if (a.settings()) a.settings()->setActiveWalletFile("wallet.dat");
ui::WalletsDialog::show(&a);
},

View File

@@ -9,6 +9,7 @@
#pragma once
#include <cctype>
#include <cstdint>
#include <filesystem>
#include <string>
@@ -120,9 +121,10 @@ public:
s_open = false; // node restarts; the main UI shows the reconnect overlay
}
} else {
// Out-of-datadir wallets can't be loaded directly (the daemon rejects paths);
// opening one means importing (copy into the datadir) — a P3 action.
ImGui::TextDisabled("%s", TR("wallets_import_hint"));
// Out-of-datadir wallets can't be loaded directly (the daemon rejects paths).
// Import = copy into the datadir under a wallet-*.dat name, then switch to it.
if (StyledButton(TR("wallets_import"), ImVec2(90.0f * dp, 0)))
importAndOpen(app, r);
if (ImGui::IsItemHovered()) Tooltip("%s", TR("wallets_import_tt"));
}
ImGui::PopID();
@@ -132,6 +134,28 @@ public:
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
// Create a fresh named wallet — the daemon mints it (with a seed phrase) on first load.
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("wallets_new_label"));
ImGui::SetNextItemWidth(-150.0f * dp);
ImGui::InputTextWithHint("##newWalletName", TR("wallets_new_hint"), s_newName, sizeof(s_newName));
ImGui::SameLine();
if (StyledButton(TR("wallets_create"), ImVec2(120.0f * dp, 0))) {
std::string name = normalizeWalletName(s_newName);
std::error_code ec;
if (name.empty()) {
Notifications::instance().warning(TR("wallets_name_invalid"));
} else if (std::filesystem::exists(util::Platform::getDragonXDataDir() + "/" + name, ec)) {
Notifications::instance().warning(TR("wallets_exists"));
} else {
s_newName[0] = '\0';
Notifications::instance().info(TR("wallets_creating"));
app->switchToWallet(name); // daemon creates it on start (-usemnemonic=1 -> seed-backed)
s_open = false;
}
}
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
// Add a folder to also scan for wallet files (no native picker — a validated path input).
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("wallets_add_folder"));
ImGui::SetNextItemWidth(-150.0f * dp);
@@ -167,6 +191,42 @@ private:
long long sizeBytes = 0;
};
// Force a "wallet-...dat" filename (plain, no path) so a new/imported wallet shows in the
// datadir scan and can't collide with the daemon's internal .dat files. Returns "" if the
// input has no usable characters.
static std::string normalizeWalletName(const std::string& raw) {
std::string s = raw;
while (!s.empty() && static_cast<unsigned char>(s.front()) <= ' ') s.erase(s.begin());
while (!s.empty() && static_cast<unsigned char>(s.back()) <= ' ') s.pop_back();
auto slash = s.find_last_of("/\\");
if (slash != std::string::npos) s = s.substr(slash + 1);
if (s.size() > 4 && s.substr(s.size() - 4) == ".dat") s = s.substr(0, s.size() - 4);
std::string clean;
for (char c : s)
if (std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_') clean += c;
if (clean.empty()) return "";
if (clean.rfind("wallet", 0) != 0) clean = "wallet-" + clean;
return clean + ".dat";
}
// Copy an out-of-datadir wallet file into the datadir (the daemon only loads plain filenames
// from there) under a wallet-*.dat name, then switch to it. Never overwrites an existing file.
static void importAndOpen(App* app, const WalletRow& r) {
namespace fs = std::filesystem;
std::string dest = normalizeWalletName(r.fileName);
if (dest.empty()) { Notifications::instance().warning(TR("wallets_name_invalid")); return; }
std::error_code ec;
const std::string datadir = util::Platform::getDragonXDataDir();
fs::create_directories(datadir, ec);
const std::string destPath = datadir + "/" + dest;
if (fs::exists(destPath, ec)) { Notifications::instance().warning(TR("wallets_exists")); return; }
fs::copy_file(r.dir + "/" + r.fileName, destPath, ec);
if (ec) { Notifications::instance().error(TR("wallets_import_failed")); return; }
Notifications::instance().success(TR("wallets_imported"));
app->switchToWallet(dest);
s_open = false;
}
// Scan the datadir (wallet-prefixed *.dat only, to skip peers.dat / asmap.dat / fee_estimates.dat)
// and each user-added folder (any *.dat). Exception-safe: iterates with error_codes.
static void scan() {
@@ -200,6 +260,7 @@ private:
static inline App* s_app = nullptr;
static inline bool s_needScan = false;
static inline char s_newFolder[512] = "";
static inline char s_newName[128] = "";
static inline std::vector<WalletRow> s_rows;
};

View File

@@ -270,6 +270,15 @@ void I18n::loadBuiltinEnglish()
strings_["wallets_folder_hint"] = "/path/to/folder with wallet .dat files";
strings_["wallets_add"] = "Add folder";
strings_["wallets_folder_invalid"] = "That folder doesn't exist.";
strings_["wallets_import"] = "Import";
strings_["wallets_new_label"] = "Create a new wallet:";
strings_["wallets_new_hint"] = "Name (e.g. savings)";
strings_["wallets_create"] = "Create wallet";
strings_["wallets_name_invalid"] = "Please enter a valid wallet name.";
strings_["wallets_exists"] = "A wallet with that name already exists.";
strings_["wallets_imported"] = "Wallet imported — switching…";
strings_["wallets_import_failed"] = "Could not import that wallet file.";
strings_["wallets_creating"] = "Creating wallet — the node will restart…";
strings_["tt_seed_migrate"] = "Create a new seed-phrase wallet and move your funds into it";
// Migrate-to-seed modal
strings_["mig_title"] = "Migrate to a seed wallet";