Browse Source

Merge pull request #9232 from accumulator/issue9209

qt/transaction_dialog: fix translation issues
master
accumulator 1 year ago committed by GitHub
parent
commit
f3a4bb4b56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 17
      electrum/gui/common_qt/i18n.py
  2. 13
      electrum/gui/qml/__init__.py
  3. 6
      electrum/gui/qt/__init__.py
  4. 4
      electrum/gui/qt/lightning_tx_dialog.py
  5. 54
      electrum/gui/qt/transaction_dialog.py

17
electrum/gui/common_qt/i18n.py

@ -0,0 +1,17 @@
from PyQt6.QtCore import QTranslator
from electrum.i18n import _
class ElectrumTranslator(QTranslator):
"""Delegator for Qt translations to gettext"""
def __init__(self, parent=None):
super().__init__(parent)
# explicit enumeration of translatable strings from Qt standard library, so these
# will be included in the electrum gettext translation template
self._strings = [_('&Undo'), _('&Redo'), _('Cu&t'), _('&Copy'), _('&Paste'), _('Select All'),
_('Copy &Link Location')]
def translate(self, context, source_text: str, disambiguation, n):
return _(source_text, context=context)

13
electrum/gui/qml/__init__.py

@ -20,14 +20,15 @@ except Exception as e:
"Error: Could not import PyQt6.QtQml. On Linux systems, " "Error: Could not import PyQt6.QtQml. On Linux systems, "
"you may try 'sudo apt-get install python3-pyqt6.qtquick'") from e "you may try 'sudo apt-get install python3-pyqt6.qtquick'") from e
from PyQt6.QtCore import (Qt, QCoreApplication, QLocale, QTranslator, QTimer, QT_VERSION_STR, PYQT_VERSION_STR) from PyQt6.QtCore import (Qt, QCoreApplication, QLocale, QTimer, QT_VERSION_STR, PYQT_VERSION_STR)
from PyQt6.QtGui import QGuiApplication from PyQt6.QtGui import QGuiApplication
from electrum.i18n import _
from electrum.plugin import run_hook from electrum.plugin import run_hook
from electrum.util import profiler from electrum.util import profiler
from electrum.logging import Logger from electrum.logging import Logger
from electrum.gui import BaseElectrumGui from electrum.gui import BaseElectrumGui
from electrum.gui.common_qt.i18n import ElectrumTranslator
if TYPE_CHECKING: if TYPE_CHECKING:
from electrum.daemon import Daemon from electrum.daemon import Daemon
@ -37,14 +38,6 @@ if TYPE_CHECKING:
from .qeapp import ElectrumQmlApplication, Exception_Hook from .qeapp import ElectrumQmlApplication, Exception_Hook
class ElectrumTranslator(QTranslator):
def __init__(self, parent=None):
super().__init__(parent)
def translate(self, context, source_text, disambiguation, n):
return _(source_text, context=context)
class ElectrumGui(BaseElectrumGui, Logger): class ElectrumGui(BaseElectrumGui, Logger):
@profiler @profiler
def __init__(self, config: 'SimpleConfig', daemon: 'Daemon', plugins: 'Plugins'): def __init__(self, config: 'SimpleConfig', daemon: 'Daemon', plugins: 'Plugins'):

6
electrum/gui/qt/__init__.py

@ -41,6 +41,7 @@ except Exception as e:
from PyQt6.QtGui import QGuiApplication from PyQt6.QtGui import QGuiApplication
from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QWidget, QMenu, QMessageBox, QDialog from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QWidget, QMenu, QMessageBox, QDialog
from PyQt6.QtCore import QObject, pyqtSignal, QTimer, Qt from PyQt6.QtCore import QObject, pyqtSignal, QTimer, Qt
import PyQt6.QtCore as QtCore import PyQt6.QtCore as QtCore
try: try:
@ -72,6 +73,8 @@ from electrum.storage import WalletStorage
from electrum.wizard import WizardViewState from electrum.wizard import WizardViewState
from electrum.keystore import load_keystore from electrum.keystore import load_keystore
from electrum.gui.common_qt.i18n import ElectrumTranslator
from .util import read_QIcon, ColorScheme, custom_message_box, MessageBoxMixin, WWLabel from .util import read_QIcon, ColorScheme, custom_message_box, MessageBoxMixin, WWLabel
from .main_window import ElectrumWindow from .main_window import ElectrumWindow
from .network_dialog import NetworkDialog from .network_dialog import NetworkDialog
@ -110,7 +113,6 @@ class QElectrumApplication(QApplication):
alias_received_signal = pyqtSignal() alias_received_signal = pyqtSignal()
class ElectrumGui(BaseElectrumGui, Logger): class ElectrumGui(BaseElectrumGui, Logger):
network_dialog: Optional['NetworkDialog'] network_dialog: Optional['NetworkDialog']
@ -137,6 +139,8 @@ class ElectrumGui(BaseElectrumGui, Logger):
self.app = QElectrumApplication(sys.argv) self.app = QElectrumApplication(sys.argv)
self.app.installEventFilter(self.efilter) self.app.installEventFilter(self.efilter)
self.app.setWindowIcon(read_QIcon("electrum.png")) self.app.setWindowIcon(read_QIcon("electrum.png"))
self.translator = ElectrumTranslator()
self.app.installTranslator(self.translator)
self._cleaned_up = False self._cleaned_up = False
# timer # timer
self.timer = QTimer(self.app) self.timer = QTimer(self.app)

4
electrum/gui/qt/lightning_tx_dialog.py

@ -41,7 +41,6 @@ if TYPE_CHECKING:
from .main_window import ElectrumWindow from .main_window import ElectrumWindow
class LightningTxDialog(WindowModalDialog): class LightningTxDialog(WindowModalDialog):
def __init__(self, parent: 'ElectrumWindow', tx_item: dict): def __init__(self, parent: 'ElectrumWindow', tx_item: dict):
@ -68,12 +67,13 @@ class LightningTxDialog(WindowModalDialog):
fee_msat = tx_item['fee_msat'] fee_msat = tx_item['fee_msat']
fee_sat = Decimal(fee_msat) / 1000 if fee_msat is not None else None fee_sat = Decimal(fee_msat) / 1000 if fee_msat is not None else None
fee_str = self.main_window.format_amount_and_units(fee_sat, timestamp=self.timestamp) fee_str = self.main_window.format_amount_and_units(fee_sat, timestamp=self.timestamp)
vbox.addWidget(QLabel(_("Fee") + f": {fee_str}")) vbox.addWidget(QLabel(_("Fee: {}").format(fee_str)))
time_str = datetime.datetime.fromtimestamp(self.timestamp).isoformat(' ')[:-3] time_str = datetime.datetime.fromtimestamp(self.timestamp).isoformat(' ')[:-3]
vbox.addWidget(QLabel(_("Date") + ": " + time_str)) vbox.addWidget(QLabel(_("Date") + ": " + time_str))
self.tx_desc_label = QLabel(_("Description:")) self.tx_desc_label = QLabel(_("Description:"))
vbox.addWidget(self.tx_desc_label) vbox.addWidget(self.tx_desc_label)
self.tx_desc = ButtonsLineEdit(self.label) self.tx_desc = ButtonsLineEdit(self.label)
def on_edited(): def on_edited():
text = self.tx_desc.text() text = self.tx_desc.text()
if self.main_window.wallet.set_label(self.payment_hash, text): if self.main_window.wallet.set_label(self.payment_hash, text):

54
electrum/gui/qt/transaction_dialog.py

@ -333,13 +333,13 @@ class TxInOutWidget(QWidget):
txin_idx = int(name.split()[1]) # split "txio_idx N", translate N -> int txin_idx = int(name.split()[1]) # split "txio_idx N", translate N -> int
txin = self.tx.inputs()[txin_idx] txin = self.tx.inputs()[txin_idx]
menu.addAction(f"Tx Input #{txin_idx}").setDisabled(True) menu.addAction(_("Tx Input #{}").format(txin_idx)).setDisabled(True)
menu.addSeparator() menu.addSeparator()
if txin.is_coinbase_input(): if txin.is_coinbase_input():
menu.addAction(_("Coinbase Input")).setDisabled(True) menu.addAction(_("Coinbase Input")).setDisabled(True)
else: else:
show_list += [(_("Show Prev Tx"), lambda: self._open_internal_link(txin.prevout.txid.hex()))] show_list += [(_("Show Prev Tx"), lambda: self._open_internal_link(txin.prevout.txid.hex()))]
copy_list += [(_("Copy") + " " + _("Outpoint"), lambda: self.main_window.do_copy(txin.prevout.to_str()))] copy_list += [(_("Copy Outpoint"), lambda: self.main_window.do_copy(txin.prevout.to_str()))]
addr = self.wallet.adb.get_txin_address(txin) addr = self.wallet.adb.get_txin_address(txin)
if addr: if addr:
if self.wallet.is_mine(addr): if self.wallet.is_mine(addr):
@ -379,11 +379,11 @@ class TxInOutWidget(QWidget):
copy_list = [] copy_list = []
# figure out which output they right-clicked on. output lines have an anchor named "txio_idx N" # figure out which output they right-clicked on. output lines have an anchor named "txio_idx N"
txout_idx = int(name.split()[1]) # split "txio_idx N", translate N -> int txout_idx = int(name.split()[1]) # split "txio_idx N", translate N -> int
menu.addAction(f"Tx Output #{txout_idx}").setDisabled(True) menu.addAction(_("Tx Output #{}").format(txout_idx)).setDisabled(True)
menu.addSeparator() menu.addSeparator()
if tx_hash := self.tx.txid(): if tx_hash := self.tx.txid():
outpoint = TxOutpoint(bytes.fromhex(tx_hash), txout_idx) outpoint = TxOutpoint(bytes.fromhex(tx_hash), txout_idx)
copy_list += [(_("Copy") + " " + _("Outpoint"), lambda: self.main_window.do_copy(outpoint.to_str()))] copy_list += [(_("Copy Outpoint"), lambda: self.main_window.do_copy(outpoint.to_str()))]
if addr := self.tx.outputs()[txout_idx].address: if addr := self.tx.outputs()[txout_idx].address:
if self.wallet.is_mine(addr): if self.wallet.is_mine(addr):
show_list += [(_("Address Details"), lambda: self.main_window.show_address(addr, parent=self))] show_list += [(_("Address Details"), lambda: self.main_window.show_address(addr, parent=self))]
@ -810,7 +810,7 @@ class TxDialog(QDialog, MessageBoxMixin):
txid = self.tx.txid() txid = self.tx.txid()
fx = self.main_window.fx fx = self.main_window.fx
tx_item_fiat = None tx_item_fiat = None
if (txid is not None and fx.is_enabled() and amount is not None): if txid is not None and fx.is_enabled() and amount is not None:
tx_item_fiat = self.wallet.get_tx_item_fiat( tx_item_fiat = self.wallet.get_tx_item_fiat(
tx_hash=txid, amount_sat=abs(amount), fx=fx, tx_fee=fee) tx_hash=txid, amount_sat=abs(amount), fx=fx, tx_fee=fee)
lnworker_history = self.wallet.lnworker.get_onchain_history() if self.wallet.lnworker else {} lnworker_history = self.wallet.lnworker.get_onchain_history() if self.wallet.lnworker else {}
@ -840,31 +840,29 @@ class TxDialog(QDialog, MessageBoxMixin):
self.tx_desc.setText(desc) self.tx_desc.setText(desc)
self.tx_desc.show() self.tx_desc.show()
self.tx_desc_label.show() self.tx_desc_label.show()
self.status_label.setText(_('Status:') + ' ' + tx_details.status) self.status_label.setText(_('Status: {}').format(tx_details.status))
if tx_mined_status.timestamp: if tx_mined_status.timestamp:
time_str = datetime.datetime.fromtimestamp(tx_mined_status.timestamp).isoformat(' ')[:-3] time_str = datetime.datetime.fromtimestamp(tx_mined_status.timestamp).isoformat(' ')[:-3]
self.date_label.setText(_("Date: {}").format(time_str)) self.date_label.setText(_("Date: {}").format(time_str))
self.date_label.show() self.date_label.show()
elif exp_n is not None: elif exp_n is not None:
text = "{}: {}".format( self.date_label.setText(_('Position in mempool: {}').format(self.config.depth_tooltip(exp_n)))
_('Position in mempool'),
self.config.depth_tooltip(exp_n))
self.date_label.setText(text)
self.date_label.show() self.date_label.show()
else: else:
self.date_label.hide() self.date_label.hide()
if self.tx.locktime <= NLOCKTIME_BLOCKHEIGHT_MAX: if self.tx.locktime <= NLOCKTIME_BLOCKHEIGHT_MAX:
locktime_final_str = f"LockTime: {self.tx.locktime} (height)" locktime_str = _('height')
else: else:
locktime_final_str = f"LockTime: {self.tx.locktime} ({datetime.datetime.fromtimestamp(self.tx.locktime)})" locktime_str = datetime.datetime.fromtimestamp(self.tx.locktime)
locktime_final_str = _("LockTime: {} ({})").format(self.tx.locktime, locktime_str)
self.locktime_final_label.setText(locktime_final_str) self.locktime_final_label.setText(locktime_final_str)
self.rbf_label.setText(_('Replace by fee') + f": {self.tx.is_rbf_enabled()}") # TODO: 'Yes'/'No' might be better translatable than 'True'/'False'?
self.rbf_label.setText(_('Replace by fee: {}').format(_('True') if self.tx.is_rbf_enabled() else _('False')))
if tx_mined_status.header_hash: if tx_mined_status.header_hash:
self.block_height_label.setText(_("At block height: {}") self.block_height_label.setText(_("At block height: {}").format(tx_mined_status.height))
.format(tx_mined_status.height))
else: else:
self.block_height_label.hide() self.block_height_label.hide()
if amount is None and ln_amount is None: if amount is None and ln_amount is None:
@ -872,36 +870,38 @@ class TxDialog(QDialog, MessageBoxMixin):
elif amount is None: elif amount is None:
amount_str = '' amount_str = ''
else: else:
if amount > 0: amount_str = ''
amount_str = _("Amount received:") + ' %s'% format_amount(amount) + ' ' + base_unit
else:
amount_str = _("Amount sent:") + ' %s' % format_amount(-amount) + ' ' + base_unit
if fx.is_enabled(): if fx.is_enabled():
if tx_item_fiat: # historical tx -> using historical price if tx_item_fiat: # historical tx -> using historical price
amount_str += ' ({})'.format(tx_item_fiat['fiat_value'].to_ui_string()) amount_str += ' ({})'.format(tx_item_fiat['fiat_value'].to_ui_string())
elif tx_details.is_related_to_wallet: # probably "tx preview" -> using current price elif tx_details.is_related_to_wallet: # probably "tx preview" -> using current price
amount_str += ' ({})'.format(format_fiat_and_units(abs(amount))) amount_str += ' ({})'.format(format_fiat_and_units(abs(amount)))
amount_str = format_amount(abs(amount)) + ' ' + base_unit + amount_str
if amount > 0:
amount_str = _("Amount received: {}").format(amount_str)
else:
amount_str = _("Amount sent: {}").format(amount_str)
if amount_str: if amount_str:
self.amount_label.setText(amount_str) self.amount_label.setText(amount_str)
else: else:
self.amount_label.hide() self.amount_label.hide()
size_str = _("Size:") + f" {size} {UI_UNIT_NAME_TXSIZE_VBYTES}" size_str = _("Size: {} {}").format(size, UI_UNIT_NAME_TXSIZE_VBYTES)
if fee is None: if fee is None:
if prog := self._fetch_txin_data_progress: if prog := self._fetch_txin_data_progress:
if not prog.has_errored: if not prog.has_errored:
fee_str = _("Downloading input data...") + f" ({prog.num_tasks_done}/{prog.num_tasks_total})" fee_str = _("Downloading input data... {}").format(f"({prog.num_tasks_done}/{prog.num_tasks_total})")
else: else:
fee_str = _("Downloading input data...") + f" error." fee_str = _("Downloading input data... {}").format(_("error"))
else: else:
fee_str = _("Fee") + ': ' + _("unknown") fee_str = _("Fee: {}").format(_("unknown"))
else: else:
fee_str = _("Fee") + f': {format_amount(fee)} {base_unit}' fee_str = _("Fee: {}").format(f'{format_amount(fee)} {base_unit}')
if fx.is_enabled(): if fx.is_enabled():
if tx_item_fiat: # historical tx -> using historical price if tx_item_fiat: # historical tx -> using historical price
fee_str += ' ({})'.format(tx_item_fiat['fiat_fee'].to_ui_string()) fee_str += ' ({})'.format(tx_item_fiat['fiat_fee'].to_ui_string())
elif tx_details.is_related_to_wallet: # probably "tx preview" -> using current price elif tx_details.is_related_to_wallet: # probably "tx preview" -> using current price
fee_str += ' ({})'.format(format_fiat_and_units(fee)) fee_str += ' ({})'.format(format_fiat_and_units(fee))
if fee is not None:
fee_rate = Decimal(fee) / size # sat/byte fee_rate = Decimal(fee) / size # sat/byte
fee_str += ' ( %s ) ' % self.main_window.format_fee_rate(fee_rate * 1000) fee_str += ' ( %s ) ' % self.main_window.format_fee_rate(fee_rate * 1000)
if isinstance(self.tx, PartialTransaction): if isinstance(self.tx, PartialTransaction):
@ -924,10 +924,10 @@ class TxDialog(QDialog, MessageBoxMixin):
if ln_amount is None or ln_amount == 0: if ln_amount is None or ln_amount == 0:
ln_amount_str = '' ln_amount_str = ''
elif ln_amount > 0: elif ln_amount > 0:
ln_amount_str = _('Amount received in channels') + ': ' + format_amount(ln_amount) + ' ' + base_unit ln_amount_str = _('Amount received in channels: {}').format(format_amount(ln_amount) + ' ' + base_unit)
else: else:
assert ln_amount < 0, f"{ln_amount!r}" assert ln_amount < 0, f"{ln_amount!r}"
ln_amount_str = _('Amount withdrawn from channels') + ': ' + format_amount(-ln_amount) + ' ' + base_unit ln_amount_str = _('Amount withdrawn from channels: {}').format(format_amount(-ln_amount) + ' ' + base_unit)
if ln_amount_str: if ln_amount_str:
self.ln_amount_label.setText(ln_amount_str) self.ln_amount_label.setText(ln_amount_str)
else: else:

Loading…
Cancel
Save