refactor(l10n): make multisig wallet policy display translatable

This commit is contained in:
okaybro
2026-02-19 08:11:58 +00:00
parent f410707133
commit 7f7f977a9e
6 changed files with 59 additions and 6 deletions
+11 -1
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: seedsigner 0.8.6\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-02-16 21:42+0000\n"
"POT-Creation-Date: 2026-02-19 07:41+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1340,6 +1340,11 @@ msgstr ""
msgid "Verify addr"
msgstr ""
#. Multisig policy display showing signing threshold (e.g. "2 of 3")
#: src/seedsigner/views/seed_views.py
msgid "{threshold} of {n}"
msgstr ""
#: src/seedsigner/views/seed_views.py
msgid "Signing messages for custom derivation paths not supported"
msgstr ""
@@ -1466,6 +1471,11 @@ msgstr ""
msgid "Change addresses"
msgstr ""
#. Multisig wallet policy display (e.g. "2 / 3 multisig")
#: src/seedsigner/views/tools_views.py
msgid "{threshold} / {n} multisig"
msgstr ""
#. a status message that our payment addresses are being calculated
#: src/seedsigner/views/tools_views.py
msgid "Calculating addrs..."
+1 -1
View File
@@ -489,7 +489,7 @@ class ToolsAddressExplorerAddressTypeScreen(ButtonListScreen):
self.components.append(IconTextLine(
# TRANSLATOR_NOTE: a label for a BIP-380-ish Output Descriptor
label_text=_("Wallet descriptor"),
value_text=self.wallet_descriptor_display_name, # TODO: English text from embit (e.g. "1 / 2 multisig"); make l10 friendly
value_text=self.wallet_descriptor_display_name,
is_text_centered=True,
screen_x=GUIConstants.EDGE_PADDING,
screen_y=self.top_nav.height + GUIConstants.COMPONENT_PADDING,
+8
View File
@@ -104,6 +104,14 @@ def get_multisig_address(descriptor: Descriptor, index: int = 0, is_change: bool
def get_multisig_policy(descriptor: Descriptor) -> tuple:
"""Extract (threshold, n) from a basic multisig descriptor."""
if not descriptor.is_basic_multisig:
raise ValueError(f"Expected a basic multisig descriptor, got: {descriptor.brief_policy}")
return (str(descriptor.miniscript.args[0]), str(len(descriptor.keys)))
def get_embit_network_name(settings_name):
""" Convert SeedSigner SettingsConstants for `network` to embit's NETWORK key """
lookup = {
+4 -2
View File
@@ -2068,8 +2068,10 @@ class MultisigWalletDescriptorView(View):
fingerprint = hexlify(key.fingerprint).decode()
fingerprints.append(fingerprint)
policy = descriptor.brief_policy.split("multisig")[0].strip()
# policy = " / ".join(policy.split(" of ")) # i18n w/o l10n since coming from non-l10n embit
from seedsigner.helpers.embit_utils import get_multisig_policy
threshold, n = get_multisig_policy(descriptor)
# TRANSLATOR_NOTE: Multisig policy display showing signing threshold (e.g. "2 of 3")
policy = _("{threshold} of {n}").format(threshold=threshold, n=n)
button_data = [self.OK]
if self.controller.resume_main_flow:
+6 -2
View File
@@ -599,8 +599,12 @@ class ToolsAddressExplorerAddressTypeView(View):
wallet_descriptor_display_name = None
if "wallet_descriptor" in data:
wallet_descriptor_display_name = data["wallet_descriptor"].brief_policy.replace(" (sorted)", "")
wallet_descriptor_display_name = " / ".join(wallet_descriptor_display_name.split(" of ")) # i18n w/o l10n since coming from non-l10n embit
from seedsigner.helpers.embit_utils import get_multisig_policy
threshold, n = get_multisig_policy(data["wallet_descriptor"])
# TRANSLATOR_NOTE: Multisig wallet policy display (e.g. "2 / 3 multisig")
wallet_descriptor_display_name = _("{threshold} / {n} multisig").format(
threshold=threshold, n=n
)
script_type = data["script_type"] if "script_type" in data else None
+29
View File
@@ -345,6 +345,35 @@ def test_get_multisig_address():
func(descriptor=descriptor, index=args[1], is_change=args[2], embit_network=args[3])
def test_get_multisig_policy():
"""
tests seedsigner.helpers.embit_utils.get_multisig_policy()
"""
from embit.descriptor import Descriptor
# Reuses the same 2-of-3 multisig descriptors from test_get_multisig_address
vectors_descriptor_expected = {
# native segwit 2-of-3
"wsh(sortedmulti(2,[8d55ff0d/48h/1h/0h/2h]tpubDDxNVWk924RTUhdkVB2uLHw1hGMPNMGufpZefhkkswjbZppVZcuMdjYKQN4ewUog9vbL6RBLFPRWcgTGT7kYP79N6thyJ43ELUs4N2szXMg/{0,1}/*,[73c5da0a/48h/1h/0h/2h]tpubDFH9dgzveyD8zTbPUFuLrGmCydNvxehyNdUXKJAQN8x4aZ4j6UZqGfnqFrD4NqyaTVGKbvEW54tsvPTK2UoSbCC1PJY8iCNiwTL3RWZEheQ/{0,1}/*,[0be174ee/48h/1h/0h/2h]tpubDEsePyLPkbxbrDiZSTTWdsviiNtiQjrvvzZnkLtG72QYLBygEsXePRsTdXi8DeMA7taCuuvoEBjUAfFrsNZeQJqfvG9fFoujYWbFPYUn7ux/{0,1}/*))#zw6cnrlk": ("2", "3"),
# nested segwit 2-of-3
"sh(wsh(sortedmulti(2,[73c5da0a/48h/1h/1h/0h/1h]tpubDFH9dgzveyD8yHQb8VrpG8FYAuwcLMHMje2CCcbBo1FpaGzYVtJeYYxcYgRqSTta5utUFts8nPPHs9C2bqoxrey5jia6Dwf9mpwrPq7YvcJ/{0,1}/*,[0be174ee/48h/1h/0h/1h]tpubDEsePyLPkbxbnj6XuKvWwdERHaKkikZxaGJ9sJqmM7okbZXgkNSFiGU6GX6qEes6kD8f9Z9FosYB9UEnBSgBEyEwwJhj4uUcFE1WE8VtKoh/{0,1}/*,[8d55ff0d/48h/1h/0h/1h]tpubDDxNVWk924RTT3vyGLHdSDoZ2JUVX7jUsPcwCQ9MrKHAtJrW5zECTF9rFHCvqu526E4PjHp61hBknts2c5aGexvX7hvCZ8TGPvQFdzxxy59/{0,1}/*)))#2ujlfp73": ("2", "3"),
# legacy p2sh 2-of-3
"sh(sortedmulti(2,[8d55ff0d/45h]tpubDANogJ2yfnizHwX7fSi5kUVzybyuPXDhgHB2TR9TUvkSLZFW73cRq4STKFDpx7qjJJiisyq82tbu4CeiYtmKEmT1xoCq9P8BPvXV31HUh6d/{0,1}/*,[0be174ee/45h]tpubDBkeVF2tDNT1Pz7L47iJeBB6RokU12LX6x4E6Ph8T89hmjQfB77q1AMyGwL8qpREVGq9sCJEbWwmnemwNTxnpxGn1di7BGy8jx9wEi5Vahu/{0,1}/*,[73c5da0a/45h]tpubDBKsGC1UqBDNvx9aivFmxZNgeZTUnmsCFGhWrqkLzucUCDePvbWWm3n8tAaAwMmxBG2ihdKCG9fzBdUnMxKx5PrkiqSZFi6Vkv6msUs9ddN/{0,1}/*))#p5t8sa8c": ("2", "3"),
}
for desc_str, (expected_threshold, expected_n) in vectors_descriptor_expected.items():
descriptor = Descriptor.from_string(desc_str)
threshold, n = embit_utils.get_multisig_policy(descriptor)
assert threshold == expected_threshold
assert n == expected_n
# Non-multisig descriptor should raise ValueError
with pytest.raises(ValueError):
embit_utils.get_multisig_policy(Descriptor.from_string(
"wpkh([73c5da0a/84h/1h/0h]tpubDC5FSnBiZDMmhiuCmWAYsLwgLYrrT9rAqvTySfuCCrgsWz8wxMXUS9Tb9iVMvcRbvFcAHGkMD5Kx8koh4GquNGNTfohfk7pgjhaPCdXpoba/{0,1}/*)#2aj6cvca"
))
def test_parse_derivation_path():
# Shouldn't care if input uses "'" or "h"
derivation_path = "m/84'/0'/0'/0/0"