From d23ee9b75f6033d3d66cf49b5382a9a63a6b6bc0 Mon Sep 17 00:00:00 2001 From: DanS Date: Thu, 9 Jul 2026 17:54:29 -0500 Subject: [PATCH] feat(wallet): new/import wallets + multi-wallet i18n (P3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the wallet-files dialog and localizes the feature. - Create a new wallet: a name is normalized to wallet-.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) --- res/fonts/NotoSansCJK-Subset.ttf | Bin 555060 -> 558676 bytes res/lang/de.json | 32 +++++++++++++++ res/lang/es.json | 32 +++++++++++++++ res/lang/fr.json | 32 +++++++++++++++ res/lang/ja.json | 32 +++++++++++++++ res/lang/ko.json | 32 +++++++++++++++ res/lang/pt.json | 32 +++++++++++++++ res/lang/ru.json | 32 +++++++++++++++ res/lang/zh.json | 32 +++++++++++++++ src/app_sweep.cpp | 6 +++ src/ui/windows/wallets_dialog.h | 67 +++++++++++++++++++++++++++++-- src/util/i18n.cpp | 9 +++++ 12 files changed, 335 insertions(+), 3 deletions(-) diff --git a/res/fonts/NotoSansCJK-Subset.ttf b/res/fonts/NotoSansCJK-Subset.ttf index 7b393b039b7907dc80ed31f1b9c0d129ad1a3343..1a753e4879d37e2c3919d0950e0a9c12662aece1 100644 GIT binary patch delta 28681 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 E6D2%?9smFU delta 25535 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 bOc$IdkRRO>tV}VVO|YsNF+(O=KfC@9mKKd+ diff --git a/res/lang/de.json b/res/lang/de.json index 3f3cc31..8e8e759 100644 --- a/res/lang/de.json +++ b/res/lang/de.json @@ -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", diff --git a/res/lang/es.json b/res/lang/es.json index 5270b8b..6945ac2 100644 --- a/res/lang/es.json +++ b/res/lang/es.json @@ -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", diff --git a/res/lang/fr.json b/res/lang/fr.json index 2c47b03..d2c3ff1 100644 --- a/res/lang/fr.json +++ b/res/lang/fr.json @@ -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", diff --git a/res/lang/ja.json b/res/lang/ja.json index 43aad23..0b4dd42 100644 --- a/res/lang/ja.json +++ b/res/lang/ja.json @@ -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": "ウェブサイト", diff --git a/res/lang/ko.json b/res/lang/ko.json index 7e1e71a..cb0bb86 100644 --- a/res/lang/ko.json +++ b/res/lang/ko.json @@ -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": "웹사이트", diff --git a/res/lang/pt.json b/res/lang/pt.json index ac9ab4f..6d857f1 100644 --- a/res/lang/pt.json +++ b/res/lang/pt.json @@ -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", diff --git a/res/lang/ru.json b/res/lang/ru.json index 8009552..6d08e6f 100644 --- a/res/lang/ru.json +++ b/res/lang/ru.json @@ -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": "Веб-сайт", diff --git a/res/lang/zh.json b/res/lang/zh.json index 03b03b4..f2dede6 100644 --- a/res/lang/zh.json +++ b/res/lang/zh.json @@ -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": "网站", diff --git a/src/app_sweep.cpp b/src/app_sweep.cpp index 882a6cd..697ef50 100644 --- a/src/app_sweep.cpp +++ b/src/app_sweep.cpp @@ -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); }, diff --git a/src/ui/windows/wallets_dialog.h b/src/ui/windows/wallets_dialog.h index 6ff5de7..6785ea5 100644 --- a/src/ui/windows/wallets_dialog.h +++ b/src/ui/windows/wallets_dialog.h @@ -9,6 +9,7 @@ #pragma once +#include #include #include #include @@ -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(s.front()) <= ' ') s.erase(s.begin()); + while (!s.empty() && static_cast(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(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 s_rows; }; diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index bc62a8b..905ad6c 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -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";