Additional translation strings; separate Babel deps

This commit is contained in:
kdmukai
2024-10-11 09:24:34 -05:00
parent 9d81d067a5
commit 6a81e9d59c
12 changed files with 613 additions and 373 deletions
+520 -333
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
Babel==2.16.0
-1
View File
@@ -1,4 +1,3 @@
Babel==2.16.0
embit==0.7.0 embit==0.7.0
numpy==1.25.2 numpy==1.25.2
Pillow==9.4.0 Pillow==9.4.0
+1 -1
View File
@@ -1,7 +1,7 @@
[extract_messages] [extract_messages]
add_comments = TRANSLATOR_NOTE: add_comments = TRANSLATOR_NOTE:
strip_comments = yes strip_comments = yes
add_location = file add_location = full
input_dirs = src input_dirs = src
output_file = babel/messages.pot output_file = babel/messages.pot
+7 -4
View File
@@ -866,11 +866,14 @@ class BtcAmount(BaseComponent):
denomination = Settings.get_instance().get_value(SettingsConstants.SETTING__BTC_DENOMINATION) denomination = Settings.get_instance().get_value(SettingsConstants.SETTING__BTC_DENOMINATION)
network = Settings.get_instance().get_value(SettingsConstants.SETTING__NETWORK) network = Settings.get_instance().get_value(SettingsConstants.SETTING__NETWORK)
btc_unit = "tBtc" # TRANSLATOR_NOTE: Testnet bitcoin
sats_unit = "tSats" btc_unit = _("tBtc")
# TRANSLATOR_NOTE: Testnet sats
sats_unit = _("tSats")
if network == SettingsConstants.MAINNET: if network == SettingsConstants.MAINNET:
btc_unit = "btc" btc_unit = _("btc")
sats_unit = "sats" sats_unit = _("sats")
btc_color = GUIConstants.ACCENT_COLOR btc_color = GUIConstants.ACCENT_COLOR
elif network == SettingsConstants.TESTNET: elif network == SettingsConstants.TESTNET:
+1 -1
View File
@@ -99,7 +99,7 @@ class ScanScreen(BaseScreen):
cur_time = time.time() cur_time = time.time()
cur_fps = num_frames / (cur_time - start_time) cur_fps = num_frames / (cur_time - start_time)
if self.decoder and self.decoder.get_percent_complete() > 0 and self.decoder.is_psbt: if self.decoder and self.decoder.get_percent_complete() > 0 and self.decoder.is_psbt:
scan_text = str(self.decoder.get_percent_complete()) + "% Complete" scan_text = _("{}% Complete").format(str(self.decoder.get_percent_complete()))
if show_framerate: if show_framerate:
scan_text += f" {cur_fps:0.2f} | {self.decoder_fps}" scan_text += f" {cur_fps:0.2f} | {self.decoder_fps}"
else: else:
+8 -3
View File
@@ -724,10 +724,12 @@ class QRDisplayScreen(BaseScreen):
) )
chevron_down_icon.render() chevron_down_icon.render()
# TRANSLATOR_NOTE: Increase QR code screen brightness
text = _("Brighter")
TextArea( TextArea(
image_draw=img_draw, image_draw=img_draw,
canvas=rectangle, canvas=rectangle,
text="Brighter", text=text,
font_size=GUIConstants.get_body_font_size(), font_size=GUIConstants.get_body_font_size(),
font_name=GUIConstants.get_button_font_name(), font_name=GUIConstants.get_button_font_name(),
background_color=(0, 0, 0, overlay_opacity), background_color=(0, 0, 0, overlay_opacity),
@@ -740,10 +742,12 @@ class QRDisplayScreen(BaseScreen):
allow_text_overflow=False allow_text_overflow=False
).render() ).render()
# TRANSLATOR_NOTE: Decrease QR code screen brightness
text = _("Darker")
TextArea( TextArea(
image_draw=img_draw, image_draw=img_draw,
canvas=rectangle, canvas=rectangle,
text="Darker", text=text,
font_size=GUIConstants.get_body_font_size(), font_size=GUIConstants.get_body_font_size(),
font_name=GUIConstants.get_button_font_name(), font_name=GUIConstants.get_button_font_name(),
background_color=(0, 0, 0, overlay_opacity), background_color=(0, 0, 0, overlay_opacity),
@@ -1037,8 +1041,9 @@ class PowerOffNotRequiredScreen(BaseTopNavScreen):
self.show_back_button = True self.show_back_button = True
super().__post_init__() super().__post_init__()
text = _("It is safe to disconnect power at any time.")
self.components.append(TextArea( self.components.append(TextArea(
text="It is safe to disconnect power at any time.", text=text,
screen_y=self.top_nav.height, screen_y=self.top_nav.height,
height=self.canvas_height - self.top_nav.height, height=self.canvas_height - self.top_nav.height,
)) ))
+1
View File
@@ -16,6 +16,7 @@ from seedsigner.views.view import BackStackView, MainMenuView, NotYetImplemented
class PSBTSelectSeedView(View): class PSBTSelectSeedView(View):
# TODO: Class-level attrs vs dynamic gettext calls
SCAN_SEED = ("Scan a seed", SeedSignerIconConstants.QRCODE) SCAN_SEED = ("Scan a seed", SeedSignerIconConstants.QRCODE)
TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD) TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD) TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
+17 -15
View File
@@ -1,5 +1,7 @@
import re import re
from gettext import gettext as _
from embit.descriptor import Descriptor from embit.descriptor import Descriptor
from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON
@@ -20,8 +22,8 @@ class ScanView(View):
dedicated errors when an unexpected QR type is scanned (e.g. Scan PSBT was dedicated errors when an unexpected QR type is scanned (e.g. Scan PSBT was
selected but a SeedQR was scanned). selected but a SeedQR was scanned).
""" """
instructions_text = "Scan a QR code" instructions_text = _("Scan a QR code")
invalid_qr_type_message = "QRCode not recognized or not yet supported." invalid_qr_type_message = _("QRCode not recognized or not yet supported.")
def __init__(self): def __init__(self):
@@ -56,7 +58,7 @@ class ScanView(View):
# `seed__compactseedqr` as "seed: compactseedqr"). # `seed__compactseedqr` as "seed: compactseedqr").
return Destination(ErrorView, view_args=dict( return Destination(ErrorView, view_args=dict(
title="Error", title="Error",
status_headline="Wrong QR Type", status_headline=_("Wrong QR Type"),
text=self.invalid_qr_type_message + f""", received "{self.decoder.qr_type.replace("__", ": ").replace("_", " ")}\" format""", text=self.invalid_qr_type_message + f""", received "{self.decoder.qr_type.replace("__", ": ").replace("_", " ")}\" format""",
button_text="Back", button_text="Back",
next_destination=Destination(BackStackView, skip_current_view=True), next_destination=Destination(BackStackView, skip_current_view=True),
@@ -155,10 +157,10 @@ class ScanView(View):
# start everything over. # start everything over.
self.controller.resume_main_flow = None self.controller.resume_main_flow = None
return Destination(ErrorView, view_args=dict( return Destination(ErrorView, view_args=dict(
title="Error", title=_("Error"),
status_headline="Unknown QR Type", status_headline=_("Unknown QR Type"),
text="QRCode is invalid or is a data format not yet supported.", text=_("QRCode is invalid or is a data format not yet supported."),
button_text="Done", button_text=_("Done"),
next_destination=Destination(MainMenuView, clear_history=True), next_destination=Destination(MainMenuView, clear_history=True),
)) ))
@@ -167,8 +169,8 @@ class ScanView(View):
class ScanPSBTView(ScanView): class ScanPSBTView(ScanView):
instructions_text = "Scan PSBT" instructions_text = _("Scan PSBT")
invalid_qr_type_message = "Expected a PSBT" invalid_qr_type_message = _("Expected a PSBT")
@property @property
def is_valid_qr_type(self): def is_valid_qr_type(self):
@@ -177,8 +179,8 @@ class ScanPSBTView(ScanView):
class ScanSeedQRView(ScanView): class ScanSeedQRView(ScanView):
instructions_text = "Scan SeedQR" instructions_text = _("Scan SeedQR")
invalid_qr_type_message = f"Expected a SeedQR" invalid_qr_type_message = _("Expected a SeedQR")
@property @property
def is_valid_qr_type(self): def is_valid_qr_type(self):
@@ -187,8 +189,8 @@ class ScanSeedQRView(ScanView):
class ScanWalletDescriptorView(ScanView): class ScanWalletDescriptorView(ScanView):
instructions_text = "Scan descriptor" instructions_text = _("Scan descriptor")
invalid_qr_type_message = "Expected a wallet descriptor QR" invalid_qr_type_message = _("Expected a wallet descriptor QR")
@property @property
def is_valid_qr_type(self): def is_valid_qr_type(self):
@@ -197,8 +199,8 @@ class ScanWalletDescriptorView(ScanView):
class ScanAddressView(ScanView): class ScanAddressView(ScanView):
instructions_text = "Scan address QR" instructions_text = _("Scan address QR")
invalid_qr_type_message = "Expected an address QR" invalid_qr_type_message = _("Expected an address QR")
@property @property
def is_valid_qr_type(self): def is_valid_qr_type(self):
+44 -10
View File
@@ -94,7 +94,7 @@ class SeedSelectSeedView(View):
seeds = self.controller.storage.seeds seeds = self.controller.storage.seeds
if self.flow == Controller.FLOW__VERIFY_SINGLESIG_ADDR: if self.flow == Controller.FLOW__VERIFY_SINGLESIG_ADDR:
title = "Verify Address" title = _("Verify Address")
if not seeds: if not seeds:
text = _("Load the seed to verify") text = _("Load the seed to verify")
else: else:
@@ -433,11 +433,13 @@ class SeedDiscardView(View):
button_data = [self.KEEP, self.DISCARD] button_data = [self.KEEP, self.DISCARD]
fingerprint = self.seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK)) fingerprint = self.seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
# TRANSLATOR_NOTE: Inserts the seed fingerprint
text = _("Wipe seed {} from the device?").format(fingerprint)
selected_menu_num = self.run_screen( selected_menu_num = self.run_screen(
WarningScreen, WarningScreen,
title=_("Discard Seed?"), title=_("Discard Seed?"),
status_headline=None, status_headline=None,
text=_("Wipe seed {} from the device?").format(fingerprint), text=text,
show_back_button=False, show_back_button=False,
button_data=button_data, button_data=button_data,
) )
@@ -1003,6 +1005,7 @@ class SeedWordsView(View):
if self.bip85_data is not None: if self.bip85_data is not None:
mnemonic = self.seed.get_bip85_child_mnemonic(self.bip85_data["child_index"], self.bip85_data["num_words"]).split() mnemonic = self.seed.get_bip85_child_mnemonic(self.bip85_data["child_index"], self.bip85_data["num_words"]).split()
# TRANSLATOR_NOTE: Inserts the child index (e.g. "Child #0")
title = _("Child #{}").format(self.bip85_data["child_index"]) title = _("Child #{}").format(self.bip85_data["child_index"])
else: else:
mnemonic = self.seed.mnemonic_display_list mnemonic = self.seed.mnemonic_display_list
@@ -1222,8 +1225,10 @@ class SeedWordsBackupTestView(View):
button_data = [real_word, fake_word1, fake_word2, fake_word3] button_data = [real_word, fake_word1, fake_word2, fake_word3]
random.shuffle(button_data) random.shuffle(button_data)
# TRANSLATOR_NOTE: Inserts the word number (e.g. "Verify Word #1")
title = _("Verify Word #{}").format(self.cur_index + 1)
selected_menu_num = ButtonListScreen( selected_menu_num = ButtonListScreen(
title=_("Verify Word #{}").format(self.cur_index + 1), title=title,
show_back_button=False, show_back_button=False,
button_data=button_data, button_data=button_data,
is_bottom_list=True, is_bottom_list=True,
@@ -1275,12 +1280,18 @@ class SeedWordsBackupTestMistakeView(View):
RETRY = _("Try Again") RETRY = _("Try Again")
button_data = [REVIEW, RETRY] button_data = [REVIEW, RETRY]
# TRANSLATOR_NOTE: Inserts the word number and the word (e.g. "Word #1 is not "apple"!")
text = _("Word #{} is not \"{}\"!").format(self.cur_index + 1, self.wrong_word)
# TRANSLATOR_NOTE: User selected the wrong word during the mnemonic backup test (e.g. incorrectly said the 5th word was "zoo")
status_headline = _("Wrong Word!")
selected_menu_num = DireWarningScreen( selected_menu_num = DireWarningScreen(
title=_("Verification Error"), title=_("Verification Error"),
show_back_button=False, show_back_button=False,
status_headline=_("Wrong Word!"), status_headline=status_headline,
text=_("Word #{} is not \"{}\"!").format(self.cur_index + 1, self.wrong_word),
button_data=button_data, button_data=button_data,
text=text,
).display() ).display()
if button_data[selected_menu_num] == REVIEW: if button_data[selected_menu_num] == REVIEW:
@@ -1870,11 +1881,25 @@ class AddressVerificationSuccessView(View):
if sig_type == SettingsConstants.MULTISIG: if sig_type == SettingsConstants.MULTISIG:
source = _("multisig") source = _("multisig")
else: else:
# TRANSLATOR_NOTE: Inserts the seed fingerprint
source = _("seed {}").format(self.seed.get_fingerprint()) source = _("seed {}").format(self.seed.get_fingerprint())
# TRANSLATOR_NOTE: Used in a sentence describing the address type (change or receive)
change_text = _("change")
# TRANSLATOR_NOTE: Used in a sentence describing the address type (change or receive)
receive_text = _("receive")
# TRANSLATOR_NOTE: Address verification success message (e.g. "bc1qabc = seed 12345678's receive address #0.")
text = _("{} = {}'s {} address #{}.""").format(
address[:7],
source,
change_text if verified_index_is_change else receive_text,
verified_index
)
LargeIconStatusScreen( LargeIconStatusScreen(
status_headline=_("Address Verified"), status_headline=_("Address Verified"),
text=f"""{address[:7]} = {source}'s {"change" if verified_index_is_change else "receive"} address #{verified_index}.""", text=text,
show_back_button=False, show_back_button=False,
).display() ).display()
@@ -1981,12 +2006,17 @@ class SeedSignMessageStartView(View):
# calculate the actual receive address # calculate the actual receive address
addr_format = embit_utils.parse_derivation_path(derivation_path) addr_format = embit_utils.parse_derivation_path(derivation_path)
if not addr_format["clean_match"]: if not addr_format["clean_match"]:
raise NotYetImplementedView("Signing messages for custom derivation paths not supported") raise NotYetImplementedView(_("Signing messages for custom derivation paths not supported"))
# Note: addr_format["network"] can be MAINNET or [TESTNET, REGTEST] # Note: addr_format["network"] can be MAINNET or [TESTNET, REGTEST]
if self.settings.get_value(SettingsConstants.SETTING__NETWORK) not in addr_format["network"]: if self.settings.get_value(SettingsConstants.SETTING__NETWORK) not in addr_format["network"]:
from seedsigner.views.view import NetworkMismatchErrorView from seedsigner.views.view import NetworkMismatchErrorView
self.set_redirect(Destination(NetworkMismatchErrorView, view_args=dict(text=f"Current network setting ({self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK)}) doesn't match {self.derivation_path}"))) # TRANSLATOR_NOTE: Inserts mainnet/testnet/regtest and derivation path
text = _("Current network setting ({}) doesn't match {}").format(
self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK),
self.derivation_path,
)
self.set_redirect(Destination(NetworkMismatchErrorView, view_args=dict(text=text)))
# cleanup. Note: We could leave this in place so the user can resume the # cleanup. Note: We could leave this in place so the user can resume the
# flow, but for now we avoid complications and keep things simple. # flow, but for now we avoid complications and keep things simple.
@@ -2063,7 +2093,7 @@ class SeedSignMessageConfirmAddressView(View):
seed = self.controller.storage.seeds[seed_num] seed = self.controller.storage.seeds[seed_num]
addr_format = embit_utils.parse_derivation_path(self.derivation_path) addr_format = embit_utils.parse_derivation_path(self.derivation_path)
if not addr_format["clean_match"] or addr_format["script_type"] == SettingsConstants.CUSTOM_DERIVATION: if not addr_format["clean_match"] or addr_format["script_type"] == SettingsConstants.CUSTOM_DERIVATION:
raise Exception("Signing messages for custom derivation paths not supported") raise Exception(_("Signing messages for custom derivation paths not supported"))
if addr_format["network"] != SettingsConstants.MAINNET: if addr_format["network"] != SettingsConstants.MAINNET:
# We're in either Testnet or Regtest or...? # We're in either Testnet or Regtest or...?
@@ -2071,7 +2101,11 @@ class SeedSignMessageConfirmAddressView(View):
addr_format["network"] = self.settings.get_value(SettingsConstants.SETTING__NETWORK) addr_format["network"] = self.settings.get_value(SettingsConstants.SETTING__NETWORK)
else: else:
from seedsigner.views.view import NetworkMismatchErrorView from seedsigner.views.view import NetworkMismatchErrorView
self.set_redirect(Destination(NetworkMismatchErrorView, view_args=dict(text=f"Current network setting ({self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK)}) doesn't match {self.derivation_path}"))) text = _("Current network setting ({}) doesn't match {}").format(
self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK),
self.derivation_path,
)
self.set_redirect(Destination(NetworkMismatchErrorView, view_args=dict(text=text)))
# cleanup. Note: We could leave this in place so the user can resume the # cleanup. Note: We could leave this in place so the user can resume the
# flow, but for now we avoid complications and keep things simple. # flow, but for now we avoid complications and keep things simple.
+4 -3
View File
@@ -650,8 +650,8 @@ class ToolsAddressExplorerAddressListView(View):
data[addr_storage_key].append(address) data[addr_storage_key].append(address)
else: else:
# TODO: Custom derivation path # TODO: Custom derivation path
raise Exception("Custom Derivation address explorer not yet implemented") raise Exception(_("Custom Derivation address explorer not yet implemented"))
elif "wallet_descriptor" in data: elif "wallet_descriptor" in data:
descriptor: Descriptor = data["wallet_descriptor"] descriptor: Descriptor = data["wallet_descriptor"]
if descriptor.is_basic_multisig: if descriptor.is_basic_multisig:
@@ -661,7 +661,7 @@ class ToolsAddressExplorerAddressListView(View):
data[addr_storage_key].append(address) data[addr_storage_key].append(address)
else: else:
raise Exception("Single sig descriptors not yet supported") raise Exception(_("Single sig descriptors not yet supported"))
finally: finally:
# Everything is set. Stop the loading screen # Everything is set. Stop the loading screen
self.loading_screen.stop() self.loading_screen.stop()
@@ -679,6 +679,7 @@ class ToolsAddressExplorerAddressListView(View):
end_digits = -4 end_digits = -4
button_data.append(f"{cur_index}:{address[:8]}...{address[end_digits:]}") button_data.append(f"{cur_index}:{address[:8]}...{address[end_digits:]}")
# TRANSLATOR_NOTE: Insert the number of addrs displayed per screen (e.g. "Next 10")
button_data.append((_("Next {}").format(addrs_per_screen), None, None, None, SeedSignerIconConstants.CHEVRON_RIGHT)) button_data.append((_("Next {}").format(addrs_per_screen), None, None, None, SeedSignerIconConstants.CHEVRON_RIGHT))
selected_menu_num = self.run_screen( selected_menu_num = self.run_screen(
+9 -2
View File
@@ -347,7 +347,10 @@ class NetworkMismatchErrorView(ErrorView):
self.title = _("Network Mismatch") self.title = _("Network Mismatch")
self.button_text = _("Change Setting") self.button_text = _("Change Setting")
if not self.text: if not self.text:
self.text = f"Current network setting ({self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK)}) doesn't match current action." # TRANSLATOR_NOTE: Inserts mainnet/testnet/regtest
self.text = _("Current network setting ({}) doesn't match current action.").format(
self.settings.get_value_display_name(SettingsConstants.SETTING__NETWORK),
)
if not self.next_destination: if not self.next_destination:
from seedsigner.views.settings_views import SettingsEntryUpdateSelectionView from seedsigner.views.settings_views import SettingsEntryUpdateSelectionView
@@ -382,7 +385,11 @@ class OptionDisabledView(View):
self.UPDATE_SETTING = _("Update Setting") self.UPDATE_SETTING = _("Update Setting")
self.DONE = _("Done") self.DONE = _("Done")
self.settings_entry = SettingsDefinition.get_settings_entry(self.settings_attr) self.settings_entry = SettingsDefinition.get_settings_entry(self.settings_attr)
self.error_msg = f"\"{self.settings_entry.display_name}\" is currently disabled in Settings."
# TRANSLATOR_NOTE: Inserts the name of a settings option (e.g. "Persistent Settings" is currently...)
self.error_msg = _("\"{}\" is currently disabled in Settings.").format(
self.settings_entry.display_name,
)
def run(self): def run(self):