diff --git a/USERGUIDE.md b/USERGUIDE.md index 19afaa8..114f2d0 100644 --- a/USERGUIDE.md +++ b/USERGUIDE.md @@ -583,8 +583,7 @@ The Settings overlay (gear icon / menu *Settings*) contains exactly two preferen the door to the server settings: - **Language** — Italiano, English, Español, Français, Português, Deutsch. Applies - immediately. Default: English. (A few low-level error messages are currently shown in - Italian regardless of language.) + immediately. Default: English. - **Amount unit** — how amounts are displayed *and interpreted when you type them*: | Unit | In satoshi | Decimals shown | diff --git a/src/App/Localization/Loc.cs b/src/App/Localization/Loc.cs index c6c367b..a61c25b 100644 --- a/src/App/Localization/Loc.cs +++ b/src/App/Localization/Loc.cs @@ -403,6 +403,24 @@ public sealed class Loc "Certificados SSL redefinidos: tente novamente a conexão.", "SSL-Zertifikate zurückgesetzt: Verbindung erneut versuchen."], ["msg.error"] = ["Errore", "Error", "Error", "Erreur", "Erro", "Fehler"], + ["msg.broadcast.error"] = ["Errore broadcast", "Broadcast error", "Error de transmisión", "Erreur de diffusion", "Erro de transmissão", "Übertragungsfehler"], + ["msg.peer.discovery.error"] = ["Errore nella scoperta peer", "Peer discovery error", "Error al descubrir peers", "Erreur de découverte des pairs", "Erro na descoberta de peers", "Fehler bei der Peer-Suche"], + ["msg.peer.discovery.found"] = ["Trovati {0} nuovi server dai peer (totale {1}).", "Found {0} new servers from peers (total {1}).", "Se encontraron {0} nuevos servidores de peers (total {1}).", "{0} nouveaux serveurs trouvés via les pairs (total {1}).", "Encontrados {0} novos servidores dos peers (total {1}).", "{0} neue Server von Peers gefunden (insgesamt {1})."], + ["msg.peer.discovery.none"] = ["Nessun nuovo server annunciato (totale {0}).", "No new servers announced (total {0}).", "No se anunciaron nuevos servidores (total {0}).", "Aucun nouveau serveur annoncé (total {0}).", "Nenhum novo servidor anunciado (total {0}).", "Keine neuen Server angekündigt (insgesamt {0})."], + ["msg.send.sync.first"] = ["Sincronizza prima di inviare.", "Synchronize before sending.", "Sincroniza antes de enviar.", "Synchronisez avant d'envoyer.", "Sincronize antes de enviar.", "Vor dem Senden synchronisieren."], + ["msg.send.connect.first"] = ["Connettiti al server e sincronizza prima di inviare.", "Connect to the server and synchronize before sending.", "Conéctate al servidor y sincroniza antes de enviar.", "Connectez-vous au serveur et synchronisez avant d'envoyer.", "Conecte-se ao servidor e sincronize antes de enviar.", "Mit dem Server verbinden und vor dem Senden synchronisieren."], + ["msg.amount.invalid"] = ["Importo non valido.", "Invalid amount.", "Importe no válido.", "Montant invalide.", "Valor inválido.", "Ungültiger Betrag."], + ["msg.feerate.invalid"] = ["Fee rate non valido.", "Invalid fee rate.", "Tarifa no válida.", "Taux de frais invalide.", "Taxa inválida.", "Ungültige Gebührenrate."], + ["msg.broadcasted"] = ["Trasmessa", "Broadcast", "Transmitida", "Diffusée", "Transmitida", "Übertragen"], + ["msg.donate.thanks"] = ["Grazie! txid", "Thank you! txid", "¡Gracias! txid", "Merci ! txid", "Obrigado! txid", "Danke! txid"], + ["msg.unsigned.watchonly"] = [" · NON firmata (watch-only)", " · NOT signed (watch-only)", " · NO firmada (watch-only)", " · NON signée (watch-only)", " · NÃO assinada (watch-only)", " · NICHT signiert (watch-only)"], + ["msg.cert.mismatch"] = [ + "Il certificato TLS di {0} è cambiato rispetto a quello salvato. Se il server ha rinnovato il certificato, esegui il reset dei certificati SSL.", + "The TLS certificate of {0} has changed from the one saved. If the server renewed its certificate, reset the SSL certificates.", + "El certificado TLS de {0} ha cambiado respecto al guardado. Si el servidor renovó su certificado, restablece los certificados SSL.", + "Le certificat TLS de {0} a changé par rapport à celui enregistré. Si le serveur a renouvelé son certificat, réinitialisez les certificats SSL.", + "O certificado TLS de {0} mudou em relação ao salvo. Se o servidor renovou o certificado, redefina os certificados SSL.", + "Das TLS-Zertifikat von {0} hat sich gegenüber dem gespeicherten geändert. Wenn der Server das Zertifikat erneuert hat, setzen Sie die SSL-Zertifikate zurück."], // Contacts ["contacts.name"] = ["Nome", "Name", "Nombre", "Nom", "Nome", "Name"], diff --git a/src/App/ViewModels/MainWindowViewModel.Donate.cs b/src/App/ViewModels/MainWindowViewModel.Donate.cs index beb7f36..7bbb0cb 100644 --- a/src/App/ViewModels/MainWindowViewModel.Donate.cs +++ b/src/App/ViewModels/MainWindowViewModel.Donate.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using NBitcoin; +using PalladiumWallet.App.Localization; using PalladiumWallet.Core.Chain; using PalladiumWallet.Core.Net; using PalladiumWallet.Core.Wallet; @@ -32,7 +33,7 @@ public partial class MainWindowViewModel if (_account is null || _doc?.Cache is null || _lastTransactions is null) { - DonatePreview = "Sincronizza prima di inviare."; + DonatePreview = Loc.Tr("msg.send.sync.first"); return; } try @@ -40,7 +41,7 @@ public partial class MainWindowViewModel var destination = BitcoinAddress.Create(DevAddress, PalladiumNetworks.For(Net)); if (!CoinAmount.TryParseIn(DonateAmount.Trim(), _config.Unit, out var amount) || amount <= 0) { - DonatePreview = "Importo non valido."; + DonatePreview = Loc.Tr("msg.amount.invalid"); return; } if (!decimal.TryParse(SendFeeRate, System.Globalization.NumberStyles.Any, @@ -61,7 +62,7 @@ public partial class MainWindowViewModel { _pendingDonate = null; HasPendingDonate = false; - DonatePreview = $"Errore: {ex.Message}"; + DonatePreview = $"{Loc.Tr("msg.error")}: {ex.Message}"; } await Task.CompletedTask; } @@ -74,7 +75,7 @@ public partial class MainWindowViewModel try { var txid = await _client.BroadcastAsync(_pendingDonate.ToHex()); - DonatePreview = $"Grazie! txid: {txid}"; + DonatePreview = $"{Loc.Tr("msg.donate.thanks")}: {txid}"; DonateAmount = ""; _pendingDonate = null; HasPendingDonate = false; @@ -82,7 +83,7 @@ public partial class MainWindowViewModel } catch (Exception ex) { - DonatePreview = $"Errore broadcast: {ex.Message}"; + DonatePreview = $"{Loc.Tr("msg.broadcast.error")}: {ex.Message}"; } } diff --git a/src/App/ViewModels/MainWindowViewModel.Send.cs b/src/App/ViewModels/MainWindowViewModel.Send.cs index 38fec61..22dc582 100644 --- a/src/App/ViewModels/MainWindowViewModel.Send.cs +++ b/src/App/ViewModels/MainWindowViewModel.Send.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using NBitcoin; +using PalladiumWallet.App.Localization; using PalladiumWallet.App.Services; using PalladiumWallet.Core.Chain; using PalladiumWallet.Core.Net; @@ -47,14 +48,14 @@ public partial class MainWindowViewModel { if (_account is null || _doc?.Cache is null) { - SendPreview = "Sincronizza prima di inviare."; + SendPreview = Loc.Tr("msg.send.sync.first"); return; } try { if (_lastTransactions is null) { - SendPreview = "Connettiti al server e sincronizza prima di inviare."; + SendPreview = Loc.Tr("msg.send.connect.first"); return; } @@ -62,12 +63,12 @@ public partial class MainWindowViewModel long amount = 0; if (!SendAll && !CoinAmount.TryParseIn(SendAmount, _config.Unit, out amount)) { - SendPreview = "Importo non valido."; + SendPreview = Loc.Tr("msg.amount.invalid"); return; } if (!decimal.TryParse(SendFeeRate, out var feeRate) || feeRate <= 0) { - SendPreview = "Fee rate non valido."; + SendPreview = Loc.Tr("msg.feerate.invalid"); return; } @@ -78,14 +79,14 @@ public partial class MainWindowViewModel SendPreview = $"txid {_pendingSend.Txid[..16]}… · " + $"fee {Fmt(_pendingSend.Fee.Satoshi)} " + $"({_pendingSend.Transaction.GetVirtualSize()} vB)" + - (_pendingSend.Signed ? "" : " · NON firmata (watch-only)"); + (_pendingSend.Signed ? "" : Loc.Tr("msg.unsigned.watchonly")); HasPendingSend = _pendingSend.Signed; } catch (Exception ex) { _pendingSend = null; HasPendingSend = false; - SendPreview = $"Errore: {ex.Message}"; + SendPreview = $"{Loc.Tr("msg.error")}: {DescribeError(ex)}"; } await Task.CompletedTask; } @@ -98,7 +99,7 @@ public partial class MainWindowViewModel try { var txid = await _client.BroadcastAsync(_pendingSend.ToHex()); - SendPreview = $"Trasmessa: {txid}"; + SendPreview = $"{Loc.Tr("msg.broadcasted")}: {txid}"; SendTo = SendAmount = ""; _pendingSend = null; HasPendingSend = false; @@ -106,7 +107,7 @@ public partial class MainWindowViewModel } catch (Exception ex) { - SendPreview = $"Errore broadcast: {ex.Message}"; + SendPreview = $"{Loc.Tr("msg.broadcast.error")}: {DescribeError(ex)}"; } } } diff --git a/src/App/ViewModels/MainWindowViewModel.Sync.cs b/src/App/ViewModels/MainWindowViewModel.Sync.cs index 6924ded..68e0be0 100644 --- a/src/App/ViewModels/MainWindowViewModel.Sync.cs +++ b/src/App/ViewModels/MainWindowViewModel.Sync.cs @@ -319,14 +319,14 @@ public partial class MainWindowViewModel IsConnected = false; ConnectionStatus = Loc.Tr("conn.none"); ConnectionStatusShort = Loc.Tr("conn.none"); - StatusMessage = ex.Message; + StatusMessage = DescribeError(ex); } catch (Exception ex) { IsConnected = _client?.IsConnected == true; ConnectionStatus = IsConnected ? ConnectionStatus : Loc.Tr("conn.none"); ConnectionStatusShort = IsConnected ? Loc.Tr("conn.connectedto") : Loc.Tr("conn.none"); - StatusMessage = $"Errore: {ex.Message}"; + StatusMessage = $"{Loc.Tr("msg.error")}: {DescribeError(ex)}"; if (_account is not null) { _syncFailed = true; @@ -378,7 +378,8 @@ public partial class MainWindowViewModel } if (temp is null) { - StatusMessage = $"Errore nella scoperta peer: {lastError?.Message ?? Loc.Tr("conn.none")}"; + StatusMessage = $"{Loc.Tr("msg.peer.discovery.error")}: " + + (lastError is null ? Loc.Tr("conn.none") : DescribeError(lastError)); return; } try @@ -401,15 +402,26 @@ public partial class MainWindowViewModel SelectedKnownServer = KnownServers.FirstOrDefault(s => s.Host == selected?.Host) ?? KnownServers.FirstOrDefault(); StatusMessage = added > 0 - ? $"Trovati {added} nuovi server dai peer (totale {KnownServers.Count})." - : $"Nessun nuovo server annunciato (totale {KnownServers.Count})."; + ? string.Format(Loc.Tr("msg.peer.discovery.found"), added, KnownServers.Count) + : string.Format(Loc.Tr("msg.peer.discovery.none"), KnownServers.Count); } catch (Exception ex) { - StatusMessage = $"Errore nella scoperta peer: {ex.Message}"; + StatusMessage = $"{Loc.Tr("msg.peer.discovery.error")}: {DescribeError(ex)}"; } } + /// + /// Translates known Core exception types to the current UI language; falls back to the + /// exception's own message (Core has no localization, so it is English by default). + /// + private static string DescribeError(Exception ex) => ex switch + { + CertificatePinMismatchException cert => + string.Format(Loc.Tr("msg.cert.mismatch"), $"{cert.Host}:{cert.Port}"), + _ => ex.Message, + }; + private void OnServerNotification(string method, System.Text.Json.JsonElement payload) { if (method is "blockchain.scripthash.subscribe" or "blockchain.headers.subscribe") diff --git a/src/App/ViewModels/MainWindowViewModel.Wizard.cs b/src/App/ViewModels/MainWindowViewModel.Wizard.cs index 156cf3d..927c757 100644 --- a/src/App/ViewModels/MainWindowViewModel.Wizard.cs +++ b/src/App/ViewModels/MainWindowViewModel.Wizard.cs @@ -416,7 +416,7 @@ public partial class MainWindowViewModel } catch (Exception ex) { - StatusMessage = $"Errore: {ex.Message}"; + StatusMessage = $"{Loc.Tr("msg.error")}: {ex.Message}"; } } @@ -473,7 +473,7 @@ public partial class MainWindowViewModel catch (Exception ex) { newLock.Dispose(); - StatusMessage = $"Errore: {ex.Message}"; + StatusMessage = $"{Loc.Tr("msg.error")}: {ex.Message}"; } } diff --git a/src/Core/Net/ElectrumClient.cs b/src/Core/Net/ElectrumClient.cs index 9a31846..02eb1a4 100644 --- a/src/Core/Net/ElectrumClient.cs +++ b/src/Core/Net/ElectrumClient.cs @@ -284,5 +284,9 @@ public sealed class ElectrumServerException(string error) : Exception(error); /// It is unlocked with an explicit reset of the certificates. /// public sealed class CertificatePinMismatchException(string host, int port) : Exception( - $"Il certificato TLS di {host}:{port} è cambiato rispetto a quello salvato. " + - "Se il server ha rinnovato il certificato, esegui il reset dei certificati SSL."); + $"The TLS certificate of {host}:{port} has changed from the one saved. " + + "If the server renewed its certificate, reset the SSL certificates.") +{ + public string Host { get; } = host; + public int Port { get; } = port; +}