mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 15:08:25 +00:00
Full l10n integration; Initial possible v0.8.5
This commit is contained in:
@@ -1,75 +0,0 @@
|
||||
# Babel Multilanguage Support
|
||||
|
||||
### "Wrapping" text for translation
|
||||
All you have to do in your code is wrap each piece of English text with the `gettext` shorthand `_()`:
|
||||
* Wrap python strings: `error="No device was selected."` becomes `error=_("No device was selected.")`
|
||||
* Use `.format()` to wrap strings with variable injections:
|
||||
```
|
||||
mystr = f"My dad's name is {dad.name} and my name is {self.name}."
|
||||
mystr = _("My dad's name is {} and my name is {}").format(dad.name, self.name)
|
||||
```
|
||||
|
||||
If there are a lot of variables to inject, placeholder names can be used:
|
||||
```
|
||||
mystr = _("My dad's name is {dad_name} and my name is {my_name}").format(dad_name=dad.name, my_name=self.name)
|
||||
```
|
||||
|
||||
|
||||
## Set up `babel`
|
||||
Install the additional dependency in your local dev environment
|
||||
```bash
|
||||
pip install -r babel/requirements-babel.txt
|
||||
```
|
||||
|
||||
Make sure that your local repo has fetched the `seedsigner-translations` submodule. It's configured to add it in src/seedsigner/resources.
|
||||
```bash
|
||||
# Need --remote in order to respect the target branch listed in .gitmodules
|
||||
git submodule update --remote
|
||||
```
|
||||
|
||||
|
||||
### Pre-configured `babel` commands
|
||||
The `setup.cfg` file in the project root specifies params for the various `babel` commands discussed below.
|
||||
|
||||
|
||||
### Rescanning for text that needs translations
|
||||
Re-generate the `messages.pot` file:
|
||||
|
||||
```bash
|
||||
python setup.py extract_messaages
|
||||
```
|
||||
|
||||
This will rescan all wrapped text, picking up new strings as well as updating existings strings that have been edited.
|
||||
|
||||
_TODO: Github Action to auto-generate messages.pot and fail a PR update if the PR has an out of date messages.pot._
|
||||
|
||||
|
||||
|
||||
### Making new text available to translators
|
||||
Upload the master `messages.pot` to Transifex. It will automatically update each language with the new or changed source strings.
|
||||
|
||||
_TODO: Look into Transifex options to automatically pull updates._
|
||||
|
||||
|
||||
|
||||
### Once new translations are complete
|
||||
The translation file for each language will need to be downloaded via Transifex's "Download for use" option (sends you a `messages.po` file for that language).
|
||||
|
||||
This updated `messages.po` will need to be added to the seedsigner-translations repo in l10n/`{TARGET_LOCALE}`/LC_MESSAGES.
|
||||
|
||||
|
||||
### Compile all the translations
|
||||
The `messages.po` files must be compiled into `*.mo` files:
|
||||
|
||||
```bash
|
||||
python compile_messages
|
||||
|
||||
# Or target a specific language code:
|
||||
python compile_messages -l es
|
||||
```
|
||||
|
||||
|
||||
## Keep the seedsigner-translations repo up to date
|
||||
The *.po files for each language and their compiled *.mo files should all be kept up to date in the seedsigner-translations repo.
|
||||
|
||||
_TODO: Github Actions automation to regenerate / verify that the *.mo files have been updated after *.po changes._
|
||||
@@ -1,72 +0,0 @@
|
||||
# i18n notes
|
||||
#
|
||||
# Format is "Pattern filename space-separated-list-of-line-numbers-with-pattern-prefixes" as of 20 November 2022
|
||||
#
|
||||
# kdmukai/intial_multilanguage branch already has plenty of work
|
||||
# * copied kdmukai's branch
|
||||
# * merged dev in into his branch.
|
||||
# * following his lead to change text literals and f-string substitutions.
|
||||
# * NOTE: TODO: add TranslatorNotes once I understand when they are needed!!!
|
||||
#
|
||||
# Common coding patterns noted: (source code lines below are prefixed w/[LFDECP) to identify pattern)
|
||||
#
|
||||
# * L) Literal text strings.
|
||||
#
|
||||
# * F) F-strings with placeholders.
|
||||
#
|
||||
# * V) variables with values set to a previously translated constant, ie _(display_name)
|
||||
#
|
||||
# * D) Default variables in baseclasses like "Button Label": is it necessary? user should never see these, else complain?
|
||||
#
|
||||
# * E) Exception messages: is it really necessary? only if frequent, else let user complain?
|
||||
#
|
||||
# * P) Proper names, partner logos, Coordinators... is it really of any concern at all?
|
||||
#
|
||||
# * print() debug messages to the C)onsole or /dev/null do not need translations? Not even included here.
|
||||
#
|
||||
#
|
||||
# While reading the notes below:
|
||||
# Each file and line-number are preceded by the type of i18n change needed, if truly 'needed'
|
||||
# A preceding # indicates there is nothing left to do, it's already i18n-ized.
|
||||
# A preceding ? indicates that I should give this another look, or a deeper look.
|
||||
# A preceding [DEP] might indicate nothing to do... if we really don't want to i18n these patterns.
|
||||
#
|
||||
|
||||
|
||||
E seedsigner/controller.py E117 E161 E168
|
||||
|
||||
ED seedsigner/gui/components.py E285 E324 E457 E498 E591 D1096 D1311
|
||||
E seedsigner/gui/keyboard.py E204 E207 E485 E587
|
||||
#L seedsigner/gui/renderer.py #L144
|
||||
|
||||
DE#LF seedsigner/gui/screens/screen.py D193 E524 #L756 #L771 #L885 #L888 #L900 #F1112
|
||||
#LF seedsigner/gui/screens/psbt_screens.py #L526 #L543? #L553 #L30 #L32 #L89 #L91 #L92 #L94 #F96 #F97 #F100 #L128 #L132 #L141 #L144 #L146 #F148 #L150 #L155 #L466 #L467 #L473 #L485 #L560 #F571 #FL667 #L677 #L689 #L703
|
||||
#L seedsigner/gui/screens/scan_screens.py #L22 #L138 #L139 #L140 #L156
|
||||
?#L seedsigner/gui/screens/settings_screens.py (_().replace() hmmm ?#L294) #L26 #L59 #L130 #L141 #L162 #L176 #L222 #L289
|
||||
#LF seedsigner/gui/screens/tools_screens.py #L41 #L72 #L99 #L119 #F150 #L161 #F167 #F177 #F189 #F193 #F200 #F241 #L274 #F311 #L352 #L354 #L369 #L385 #L393 #F402 #F410 #F418
|
||||
#L seedsigner/gui/screens/main_menu_screens.py #L12 #L24 #L29 #L39 #L44
|
||||
?#L seedsigner/gui/screens/seed_screens.py (why colon ?#L1033) #L411 #L424 #L555 #L561 #L571 #L587 #L593 #L597 #L606 #L616 #L625 #L638 #L1026 #L1121 #L1123 #L1131 #L1133 #L1147 #L1149 #L1226 #L1362 #L1419 #L1483 #L1500 #L1505 #L1518 #L1524 #L1532
|
||||
|
||||
E seedsigner/hardware/camera.py E36 E65
|
||||
E seedsigner/hardware/ST7789.py E154
|
||||
|
||||
E seedsigner/helpers/embit_utils.py E25 E35 E43 E45 E47 E91 E95
|
||||
E seedsigner/helpers/mnemonic_generation.py E22
|
||||
? seedsigner/helpers/ur2/DID-NOT-LOOK
|
||||
|
||||
E#LF seedsigner/views/psbt_views.py E30 #L36 #L37 #L38 #L55 #L94 #L166 #L167 #L168 #L185 #L186 #L187 #L253 #259 #L262 #L331 #L335 #L336 #L338 #L339 #L358 #L360 #L441 #F443 #L445 #FL447 #L451 #L453 #L478 #L530 #L532 #L533 #L534
|
||||
#L seedsigner/views/screensaver.py #L74
|
||||
E#LF seedsigner/views/seed_views.py (Why this one ?#E1464) #L1637 #L54 #L57 #L78 #L79 #L80 #L81 #L90 #F127 #L187 #L188 #L192 #L194 #L229 #L230 #L260 #L300 #L301 #L342 #L343 #L348 #F350 #L384 #L385 #L386 #L387 #L388 #L389 #L390 #L420 #L476 #L477 #L510 #L511 #L515 #L554 #L556 #L641 #L683 #L684 #L693 #L830 #L855 #L856 #F836 #L902 #L903 #F952 #L992 #L993 #L997 #L999 #F1000 #L1018 #L1020 #L1021 #L1022 #L1044 #L1045 #L1049 #L1050 #L1071 #L1120 #L1121 #L1213 #L1214 #L1218 #L1248 #L1257 #L1258 #L1259 #L1221 #L1268 #L1269 #L1270 #L1272 #L1280 #L1281 #L1282 #L1284 #L1353 #L1354 #L1391 #L1392 #L1393 #L1396 #L1406 #L1413 #L1510 #L1511 #F1639 #L1642 #L1652 #L1653 #L1682 #L1683 #L1684 #L1685
|
||||
#V seedsigner/views/settings_views.py #V26 #L20 #L21 #L36 #L39 #L46 #L54
|
||||
E#LF seedsigner/views/tools_views.py E605 E616 #L29 #L30 #L31 #L32 #L35 #L112 #L113 #L117 #L184 #L185 #L189 #L238 #L239 #L243 #L275 #L276 #L277 #L376 #L379 #L401 #L402 #L430 #L431 #L432 #L433 #L451 #L538 #L539 #L589 #F628 #FL631
|
||||
E#L seedsigner/views/view.py E66 #L124 #L125 #L126 #L127 #L142 #L145
|
||||
|
||||
E seedsigner/models/encode_qr.py E46 E104 E143 E146 E150 E153 E273 E356
|
||||
E seedsigner/models/psbt_parser.py E96 E226 E229 E232 E241 E245 E255 E269
|
||||
E seedsigner/models/seed.py E26 E42
|
||||
E seedsigner/models/seed_storage.py E68
|
||||
P#L seedsigner/models/settings_definition.py P37 P38 P39 P40 #V288 #L14 #L15 #L18 #L19 #L20 #L23 #L26 #L29 #L74 #L55 #L76 #L77 #L96 #L97 #L98 #L106 #L107 #L108 #L124 #L125 #L134 #L135 #L136 #L137 #L357 #L365 #L373 #L374 #L379 #L386 #L395 #L403 #L411 #L417 #L425 #L433 #L439 #L447 #L455 #L461 #L467 #L473
|
||||
E seedsigner/models/settings.py E112 E134 E150 E153 E162 E165
|
||||
E seedsigner/models/singleton.py E6 E24 E31
|
||||
E seedsigner/models/threads.py E23
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
# Localization (l10n) Tech Notes
|
||||
|
||||
## "Wrapping" text for translation
|
||||
All you have to do in your code is wrap each piece of English text with the `gettext` shorthand `_()`:
|
||||
* Wrap python strings: `error="No device was selected."` becomes `error=_("No device was selected.")`
|
||||
* Use `.format()` to wrap strings with variable injections:
|
||||
```python
|
||||
mystr = f"My dad's name is {dad.name} and my name is {self.name}."
|
||||
mystr = _("My dad's name is {} and my name is {}").format(dad.name, self.name)
|
||||
```
|
||||
|
||||
If there are a lot of variables to inject, placeholder names can be used:
|
||||
```python
|
||||
mystr = _("My dad's name is {dad_name} and my name is {my_name}").format(dad_name=dad.name, my_name=self.name)
|
||||
```
|
||||
* Use `ngettext` to dynamically handle singular vs plural forms based on an integer quantity:
|
||||
```python
|
||||
n = 1
|
||||
print(ngettext("apple", "apples", n))
|
||||
>> apple
|
||||
|
||||
n = 5
|
||||
print(ngettext("apple", "apples", n))
|
||||
>> apples
|
||||
```
|
||||
|
||||
Transifex will ask translators to provide the singular and plural forms.
|
||||
|
||||
Note: Some languages like Arabic have multiple plural forms (e.g. 2-3 is one form, > 3 is another).
|
||||
|
||||
|
||||
## Understanding `gettext` wrapping
|
||||
Note that this wrapping serves TWO purposes:
|
||||
1. Pre-translation: This is how we identify text that translators need to translate. Any strings wrapped with `_()` (or related calls like `ngettext`) will appear in translators' Transifex UI.
|
||||
2. Post-translation: It returns the locale-specific translation for that source string (defaults to the English string itself if no translation is available).
|
||||
|
||||
In general, most SeedSigner code uses the `_()` wrapping only to identify the strings that need translation.
|
||||
|
||||
The second step -- fetching the translated string -- mostly happens in the base `Screen` classes. They ask for the translation just before the text is drawn on screen.
|
||||
|
||||
Basic rules:
|
||||
* Wrap raw English strings so they'll be picked up for translation.
|
||||
* In general, don't go out of your way to pass already translated text to `Screen` classes.
|
||||
* The `Screen` itself should do most of the `_()` wrapping to translate its input attributes for final display.
|
||||
* Basic gui `Component` classes (e.g. `Button`, `TextArea`) should ideally NOT wrap any text.
|
||||
* Ensure that they are supplied with wrapped / already translated text.
|
||||
|
||||
Basic example:
|
||||
```python
|
||||
@dataclass
|
||||
class SomeClass:
|
||||
title: str = _("My Title")
|
||||
```
|
||||
|
||||
In this case `title` is wrapped in order to get "My Title" into the translators' list. Note that because `title` is a class-level attribute, its string value will be determined at import time and
|
||||
will NOT be re-evaluated when the class is eventually used. That means that the locale-specific translation will NOT be returned later:
|
||||
|
||||
```python
|
||||
from blah import SomeClass
|
||||
|
||||
print(SomeClass.title)
|
||||
>> My Title
|
||||
|
||||
# set locale to some other language and then...
|
||||
os.environ['LANGUAGE'] = 'es'
|
||||
my_instance = SomeClass()
|
||||
print(my_instance.title)
|
||||
>> My Title
|
||||
```
|
||||
|
||||
To get the translated value, we'd have to pass the attribute itself into `_()`. This feels redundant since the original string was already wrapped in the class definition, but we have to
|
||||
work around the realities of python imports, etc. This time the wrapping is saying, "Hey, take the value you're holding in that attribute/variable and see if it has an entry in our locale-specific
|
||||
translation library."
|
||||
```python
|
||||
os.environ['LANGUAGE'] = 'es'
|
||||
print(_(SomeClass.title))
|
||||
>> Mi Titulo
|
||||
|
||||
my_instance = SomeClass()
|
||||
print(_(my_instance.title))
|
||||
>> Mi Titulo
|
||||
```
|
||||
|
||||
|
||||
## Set up localization dependencies
|
||||
```bash
|
||||
pip install -r l10n/requirements-l10n.txt
|
||||
```
|
||||
|
||||
Make sure that your local repo has fetched the `seedsigner-translations` submodule. It's configured to add it in src/seedsigner/resources.
|
||||
```bash
|
||||
# Need --remote in order to respect the target branch listed in .gitmodules
|
||||
git submodule update --remote
|
||||
```
|
||||
|
||||
|
||||
### Pre-configured `babel` commands
|
||||
The `setup.cfg` file in the project root specifies params for the various `babel` commands discussed below.
|
||||
|
||||
|
||||
### Rescanning for text that needs translations
|
||||
Re-generate the `messages.pot` file:
|
||||
|
||||
```bash
|
||||
python setup.py extract_messaages
|
||||
```
|
||||
|
||||
This will rescan all wrapped text, picking up new strings as well as updating existings strings that have been edited.
|
||||
|
||||
_TODO: Github Action to auto-generate messages.pot and fail a PR update if the PR has an out of date messages.pot._
|
||||
|
||||
|
||||
### Making new text available to translators
|
||||
Upload the master `messages.pot` to Transifex. It will automatically update each language with the new or changed source strings.
|
||||
|
||||
_TODO: Look into Transifex options to automatically pull updates._
|
||||
|
||||
|
||||
### Once new translations are complete
|
||||
The translation file for each language will need to be downloaded via Transifex's "Download for use" option (sends you a `messages.po` file for that language).
|
||||
|
||||
This updated `messages.po` will need to be added to the seedsigner-translations repo in l10n/`{TARGET_LOCALE}`/LC_MESSAGES.
|
||||
|
||||
|
||||
### Compile all the translations
|
||||
The `messages.po` files must be compiled into `*.mo` files:
|
||||
|
||||
```bash
|
||||
python setup.py compile_catalog
|
||||
|
||||
# Or target a specific language code:
|
||||
python setup.py compile_catalog -l es
|
||||
```
|
||||
|
||||
### Unused babel commands
|
||||
Transifex eliminates the need for the `init_catalog` and `update_catalog` commands.
|
||||
|
||||
|
||||
## Keep the seedsigner-translations repo up to date
|
||||
The *.po files for each language and their compiled *.mo files should all be kept up to date in the seedsigner-translations repo.
|
||||
|
||||
_TODO: Github Actions automation to regenerate / verify that the *.mo files have been updated after *.po changes._
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: seedsigner 0.7.0\n"
|
||||
"Project-Id-Version: seedsigner 0.8.5\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2024-10-11 14:02-0500\n"
|
||||
"POT-Creation-Date: 2024-11-01 16:48-0500\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"
|
||||
@@ -32,6 +32,7 @@ msgid "btc"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/gui/components.py src/seedsigner/gui/screens/psbt_screens.py
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "sats"
|
||||
msgstr ""
|
||||
|
||||
@@ -113,6 +114,11 @@ msgstr ""
|
||||
msgid "fee"
|
||||
msgstr ""
|
||||
|
||||
#. Technical term, should probably NOT be translated in most languages
|
||||
#: src/seedsigner/gui/screens/psbt_screens.py
|
||||
msgid "OP_RETURN"
|
||||
msgstr ""
|
||||
|
||||
#. Label for a change output in the PSBT Overview flow diagram
|
||||
#. Used in a sentence describing the address type (change or receive)
|
||||
#: src/seedsigner/gui/screens/psbt_screens.py
|
||||
@@ -166,6 +172,11 @@ msgstr ""
|
||||
msgid "Address verified!"
|
||||
msgstr ""
|
||||
|
||||
#. Shown when displaying OP_RETURN as non-human-readable hexadecimal data
|
||||
#: src/seedsigner/gui/screens/psbt_screens.py
|
||||
msgid "raw hex data"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/gui/screens/psbt_screens.py
|
||||
msgid "Sign PSBT"
|
||||
msgstr ""
|
||||
@@ -175,11 +186,13 @@ msgid "Click to approve this transaction"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/gui/screens/scan_screens.py
|
||||
msgid "< back | "
|
||||
#: src/seedsigner/gui/screens/tools_screens.py
|
||||
msgid "back"
|
||||
msgstr ""
|
||||
|
||||
#. Inserts the percentage value of the animated QR scan progress
|
||||
#: src/seedsigner/gui/screens/scan_screens.py
|
||||
msgid "{}% Complete"
|
||||
msgid "{}%"
|
||||
msgstr ""
|
||||
|
||||
#. Increase QR code screen brightness
|
||||
@@ -235,6 +248,10 @@ msgstr ""
|
||||
msgid "Please wait about 30 seconds before disconnecting power."
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/gui/screens/screen.py
|
||||
msgid "Just Unplug It"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/gui/screens/screen.py
|
||||
msgid "It is safe to disconnect power at any time."
|
||||
msgstr ""
|
||||
@@ -310,6 +327,7 @@ msgid "Xpub"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/gui/screens/seed_screens.py
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "BIP-39 Passphrase"
|
||||
msgstr ""
|
||||
@@ -467,15 +485,27 @@ msgstr ""
|
||||
msgid "Settings QR"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/gui/screens/settings_screens.py
|
||||
msgid "Settings updated..."
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/gui/screens/settings_screens.py
|
||||
#: src/seedsigner/views/settings_views.py src/seedsigner/views/view.py
|
||||
msgid "Home"
|
||||
msgstr ""
|
||||
|
||||
#. Instructions when reviewing an image; "reshoot" must point left, "accept"
|
||||
#. must point right
|
||||
#: src/seedsigner/gui/screens/tools_screens.py
|
||||
msgid " < reshoot | accept > "
|
||||
msgid "click joystick"
|
||||
msgstr ""
|
||||
|
||||
#. A prompt to the user to either accept or reshoot the image
|
||||
#: src/seedsigner/gui/screens/tools_screens.py
|
||||
msgid "reshoot"
|
||||
msgstr ""
|
||||
|
||||
#. A prompt to the user to either accept or reshoot the image
|
||||
#: src/seedsigner/gui/screens/tools_screens.py
|
||||
msgid "accept"
|
||||
msgstr ""
|
||||
|
||||
#. current roll number vs total rolls (e.g. roll 7 of 50)
|
||||
@@ -510,7 +540,7 @@ msgstr ""
|
||||
msgid "Tails = 0"
|
||||
msgstr ""
|
||||
|
||||
#. A labeled presentation of a user-supplied value
|
||||
#. The additional entropy the user supplied (e.g. coin flips)
|
||||
#: src/seedsigner/gui/screens/tools_screens.py
|
||||
msgid "Your input: \"{}\""
|
||||
msgstr ""
|
||||
@@ -562,6 +592,18 @@ msgstr ""
|
||||
msgid "Required"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "BTC"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Threshold at 0.01"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "BTC | sats hybrid"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "0°"
|
||||
msgstr ""
|
||||
@@ -578,14 +620,17 @@ msgstr ""
|
||||
msgid "270°"
|
||||
msgstr ""
|
||||
|
||||
#. QR code density option: Low, Medium, High
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Low"
|
||||
msgstr ""
|
||||
|
||||
#. QR code density option: Low, Medium, High
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Medium"
|
||||
msgstr ""
|
||||
|
||||
#. QR code density option: Low, Medium, High
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "High"
|
||||
msgstr ""
|
||||
@@ -620,7 +665,11 @@ msgid "Native Segwit"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Nested Segwit (legacy)"
|
||||
msgid "Nested Segwit"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Legacy"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
@@ -631,6 +680,11 @@ msgstr ""
|
||||
msgid "Custom Derivation"
|
||||
msgstr ""
|
||||
|
||||
#. Terminology used by Electrum seeds; equivalent to bip39 passphrase
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Custom Extension"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
@@ -691,6 +745,10 @@ msgstr ""
|
||||
msgid "BIP-85 child seeds"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Native Segwit only"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/models/settings_definition.py
|
||||
msgid "Message signing"
|
||||
msgstr ""
|
||||
@@ -730,6 +788,17 @@ msgstr ""
|
||||
msgid "Enter 24-word seed"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/psbt_views.py src/seedsigner/views/seed_views.py
|
||||
#: src/seedsigner/views/tools_views.py
|
||||
msgid "Enter Electrum seed"
|
||||
msgstr ""
|
||||
|
||||
#. Inserts fingerprint w/"?" to indicate that this seed can't sign the current
|
||||
#. PSBT
|
||||
#: src/seedsigner/views/psbt_views.py
|
||||
msgid "{} (?)"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/psbt_views.py
|
||||
msgid "Select Signer"
|
||||
msgstr ""
|
||||
@@ -775,15 +844,19 @@ msgstr ""
|
||||
msgid "Next Recipient"
|
||||
msgstr ""
|
||||
|
||||
#. The amount you're receiving back from the transaction
|
||||
#: src/seedsigner/views/psbt_views.py
|
||||
msgid "Your Change"
|
||||
msgid "Skip Verification"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/psbt_views.py
|
||||
msgid "Verify Multisig Change"
|
||||
msgstr ""
|
||||
|
||||
#. The amount you're receiving back from the transaction
|
||||
#: src/seedsigner/views/psbt_views.py
|
||||
msgid "Your Change"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/psbt_views.py
|
||||
msgid "Self-Transfer"
|
||||
msgstr ""
|
||||
@@ -971,6 +1044,18 @@ msgstr ""
|
||||
msgid "Edit passphrase"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Discard passphrase"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Discard passphrase?"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Your current passphrase entry will be erased"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Keep Seed"
|
||||
msgstr ""
|
||||
@@ -984,6 +1069,14 @@ msgstr ""
|
||||
msgid "Discard Seed?"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Electrum warning"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Some features are disabled for Electrum seeds."
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Verify Addr"
|
||||
msgstr ""
|
||||
@@ -1157,10 +1250,6 @@ msgstr ""
|
||||
msgid "Sig type can't be auto-detected from this address. Please specify:"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Can't validate a single sig addr without specifying a seed"
|
||||
msgstr ""
|
||||
|
||||
#. Option when scanning for a matching address; skips ten addresses ahead
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Skip 10"
|
||||
@@ -1170,6 +1259,10 @@ msgstr ""
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Can't validate a single sig addr without specifying a seed"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "multisig"
|
||||
msgstr ""
|
||||
@@ -1202,15 +1295,15 @@ msgstr ""
|
||||
msgid "Return to PSBT"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Signing messages for custom derivation paths not supported"
|
||||
msgstr ""
|
||||
|
||||
#. Inserts mainnet/testnet/regtest and derivation path
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Current network setting ({}) doesn't match {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/seed_views.py
|
||||
msgid "Signing messages for custom derivation paths not supported"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/settings_views.py
|
||||
msgid "I/O test"
|
||||
msgstr ""
|
||||
@@ -1255,12 +1348,14 @@ msgstr ""
|
||||
msgid "Mnemonic Length?"
|
||||
msgstr ""
|
||||
|
||||
#. Inserts the number of dice rolls needed for a 12-word mnemonic
|
||||
#: src/seedsigner/views/tools_views.py
|
||||
msgid "12 words (50 rolls)"
|
||||
msgid "12 words ({} rolls)"
|
||||
msgstr ""
|
||||
|
||||
#. Inserts the number of dice rolls needed for a 24-word mnemonic
|
||||
#: src/seedsigner/views/tools_views.py
|
||||
msgid "24 words (99 rolls)"
|
||||
msgid "24 words ({} rolls)"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/tools_views.py
|
||||
@@ -1351,6 +1446,10 @@ msgstr ""
|
||||
msgid "Reset / Power"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/view.py
|
||||
msgid "This is still on our to-do list!"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/view.py
|
||||
msgid "Work In Progress"
|
||||
msgstr ""
|
||||
@@ -1359,10 +1458,6 @@ msgstr ""
|
||||
msgid "Not Yet Implemented"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/view.py
|
||||
msgid "This is still on our to-do list!"
|
||||
msgstr ""
|
||||
|
||||
#: src/seedsigner/views/view.py
|
||||
msgid "Back to Main Menu"
|
||||
msgstr ""
|
||||
+1
-1
@@ -13,7 +13,7 @@ description = "Build an offline, airgapped Bitcoin signing device for less than
|
||||
name = "seedsigner"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "0.8.0"
|
||||
version = "0.8.5"
|
||||
|
||||
[project.urls]
|
||||
"Bug Tracker" = "https://github.com/SeedSigner/seedsigner/issues"
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
{
|
||||
"settings_entries": [
|
||||
{
|
||||
"category": "system",
|
||||
"attr_name": "locale",
|
||||
"abbreviated_name": "lang",
|
||||
"display_name": "Language",
|
||||
"visibility": "general",
|
||||
"type": "select_1",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "\u010de\u0161tina",
|
||||
"value": "cs"
|
||||
},
|
||||
{
|
||||
"display_name": "English",
|
||||
"value": "en"
|
||||
},
|
||||
{
|
||||
"display_name": "Fran\u00e7ais",
|
||||
"value": "fr"
|
||||
},
|
||||
{
|
||||
"display_name": "Deutsch",
|
||||
"value": "de"
|
||||
},
|
||||
{
|
||||
"display_name": "Portugu\u00eas",
|
||||
"value": "pt"
|
||||
},
|
||||
{
|
||||
"display_name": "\u0440\u0443\u0441\u0441\u043a\u0438\u0439",
|
||||
"value": "ru"
|
||||
},
|
||||
{
|
||||
"display_name": "Espa\u00f1ol",
|
||||
"value": "es"
|
||||
}
|
||||
],
|
||||
"default_value": "en"
|
||||
},
|
||||
{
|
||||
"category": "system",
|
||||
"attr_name": "wordlist_language",
|
||||
"abbreviated_name": "wordlist_lang",
|
||||
"display_name": "Mnemonic language",
|
||||
"visibility": "hidden",
|
||||
"type": "select_1",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "English",
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"default_value": "en"
|
||||
},
|
||||
{
|
||||
"category": "system",
|
||||
"attr_name": "persistent_settings",
|
||||
"abbreviated_name": "persistent",
|
||||
"display_name": "Persistent settings",
|
||||
"visibility": "general",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": "Store Settings on SD card",
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "D"
|
||||
},
|
||||
{
|
||||
"category": "wallet",
|
||||
"attr_name": "coordinators",
|
||||
"abbreviated_name": "coords",
|
||||
"display_name": "Coordinator software",
|
||||
"visibility": "general",
|
||||
"type": "multiselect",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "BlueWallet",
|
||||
"value": "bw"
|
||||
},
|
||||
{
|
||||
"display_name": "Nunchuk",
|
||||
"value": "nun"
|
||||
},
|
||||
{
|
||||
"display_name": "Sparrow",
|
||||
"value": "spa"
|
||||
},
|
||||
{
|
||||
"display_name": "Specter Desktop",
|
||||
"value": "spd"
|
||||
},
|
||||
{
|
||||
"display_name": "Keeper",
|
||||
"value": "kpr"
|
||||
}
|
||||
],
|
||||
"default_value": [
|
||||
"bw",
|
||||
"nun",
|
||||
"spa",
|
||||
"spd"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "system",
|
||||
"attr_name": "denomination",
|
||||
"abbreviated_name": "denom",
|
||||
"display_name": "Denomination display",
|
||||
"visibility": "general",
|
||||
"type": "select_1",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "BTC",
|
||||
"value": "btc"
|
||||
},
|
||||
{
|
||||
"display_name": "sats",
|
||||
"value": "sats"
|
||||
},
|
||||
{
|
||||
"display_name": "Threshold at 0.01",
|
||||
"value": "thr"
|
||||
},
|
||||
{
|
||||
"display_name": "BTC | sats hybrid",
|
||||
"value": "hyb"
|
||||
}
|
||||
],
|
||||
"default_value": "thr"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "network",
|
||||
"abbreviated_name": null,
|
||||
"display_name": "Bitcoin network",
|
||||
"visibility": "advanced",
|
||||
"type": "select_1",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Mainnet",
|
||||
"value": "M"
|
||||
},
|
||||
{
|
||||
"display_name": "Testnet",
|
||||
"value": "T"
|
||||
},
|
||||
{
|
||||
"display_name": "Regtest",
|
||||
"value": "R"
|
||||
}
|
||||
],
|
||||
"default_value": "M"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "qr_density",
|
||||
"abbreviated_name": null,
|
||||
"display_name": "QR code density",
|
||||
"visibility": "advanced",
|
||||
"type": "select_1",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Low",
|
||||
"value": "L"
|
||||
},
|
||||
{
|
||||
"display_name": "Medium",
|
||||
"value": "M"
|
||||
},
|
||||
{
|
||||
"display_name": "High",
|
||||
"value": "H"
|
||||
}
|
||||
],
|
||||
"default_value": "M"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "xpub_export",
|
||||
"abbreviated_name": null,
|
||||
"display_name": "Xpub export",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "E"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "sig_types",
|
||||
"abbreviated_name": "sigs",
|
||||
"display_name": "Sig types",
|
||||
"visibility": "advanced",
|
||||
"type": "multiselect",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Single Sig",
|
||||
"value": "ss"
|
||||
},
|
||||
{
|
||||
"display_name": "Multisig",
|
||||
"value": "ms"
|
||||
}
|
||||
],
|
||||
"default_value": [
|
||||
"ss",
|
||||
"ms"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "script_types",
|
||||
"abbreviated_name": "scripts",
|
||||
"display_name": "Script types",
|
||||
"visibility": "advanced",
|
||||
"type": "multiselect",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Native Segwit",
|
||||
"value": "nat"
|
||||
},
|
||||
{
|
||||
"display_name": "Nested Segwit",
|
||||
"value": "nes"
|
||||
},
|
||||
{
|
||||
"display_name": "Legacy",
|
||||
"value": "leg"
|
||||
},
|
||||
{
|
||||
"display_name": "Taproot",
|
||||
"value": "tr"
|
||||
},
|
||||
{
|
||||
"display_name": "Custom Derivation",
|
||||
"value": "cus"
|
||||
}
|
||||
],
|
||||
"default_value": [
|
||||
"nat",
|
||||
"nes",
|
||||
"tr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "xpub_details",
|
||||
"abbreviated_name": null,
|
||||
"display_name": "Show xpub details",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "E"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "passphrase",
|
||||
"abbreviated_name": null,
|
||||
"display_name": "BIP-39 passphrase",
|
||||
"visibility": "advanced",
|
||||
"type": "select_1",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
},
|
||||
{
|
||||
"display_name": "Required",
|
||||
"value": "R"
|
||||
}
|
||||
],
|
||||
"default_value": "E"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "camera_rotation",
|
||||
"abbreviated_name": "camera",
|
||||
"display_name": "Camera rotation",
|
||||
"visibility": "advanced",
|
||||
"type": "select_1",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "0\u00b0",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"display_name": "90\u00b0",
|
||||
"value": 90
|
||||
},
|
||||
{
|
||||
"display_name": "180\u00b0",
|
||||
"value": 180
|
||||
},
|
||||
{
|
||||
"display_name": "270\u00b0",
|
||||
"value": 270
|
||||
}
|
||||
],
|
||||
"default_value": 180
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "compact_seedqr",
|
||||
"abbreviated_name": null,
|
||||
"display_name": "Compact SeedQR",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "E"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "bip85_child_seeds",
|
||||
"abbreviated_name": "bip85",
|
||||
"display_name": "BIP-85 child seeds",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "D"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "electrum_seeds",
|
||||
"abbreviated_name": "electrum",
|
||||
"display_name": "Electrum seeds",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": "Native Segwit only",
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "D"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "message_signing",
|
||||
"abbreviated_name": null,
|
||||
"display_name": "Message signing",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "D"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "privacy_warnings",
|
||||
"abbreviated_name": "priv_warn",
|
||||
"display_name": "Show privacy warnings",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "E"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "dire_warnings",
|
||||
"abbreviated_name": "dire_warn",
|
||||
"display_name": "Show dire warnings",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "E"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "qr_brightness_tips",
|
||||
"abbreviated_name": null,
|
||||
"display_name": "Show QR brightness tips",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "E"
|
||||
},
|
||||
{
|
||||
"category": "features",
|
||||
"attr_name": "partner_logos",
|
||||
"abbreviated_name": "partners",
|
||||
"display_name": "Show partner logos",
|
||||
"visibility": "advanced",
|
||||
"type": "enabled_disabled",
|
||||
"help_text": null,
|
||||
"selection_options": [
|
||||
{
|
||||
"display_name": "Enabled",
|
||||
"value": "E"
|
||||
},
|
||||
{
|
||||
"display_name": "Disabled",
|
||||
"value": "D"
|
||||
}
|
||||
],
|
||||
"default_value": "E"
|
||||
},
|
||||
{
|
||||
"category": "system",
|
||||
"attr_name": "qr_background_color",
|
||||
"abbreviated_name": "qr_brightness",
|
||||
"display_name": "QR background color",
|
||||
"visibility": "hidden",
|
||||
"type": "free_entry",
|
||||
"help_text": null,
|
||||
"selection_options": null,
|
||||
"default_value": 62
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,14 +3,8 @@ add_comments = TRANSLATOR_NOTE:
|
||||
strip_comments = yes
|
||||
add_location = file
|
||||
input_dirs = src
|
||||
output_file = babel/messages.pot
|
||||
output_file = l10n/messages.pot
|
||||
|
||||
[update_catalog]
|
||||
ignore_pot_creation_date = yes
|
||||
no_fuzzy_matching = yes
|
||||
no_wrap = yes
|
||||
input_file = babel/messages.pot
|
||||
output_dir = src/seedsigner/resources/seedsigner-translations/l10n
|
||||
|
||||
[compile_catalog]
|
||||
use_fuzzy = yes
|
||||
|
||||
@@ -101,7 +101,7 @@ class Controller(Singleton):
|
||||
rather than at the top in order avoid circular imports.
|
||||
"""
|
||||
|
||||
VERSION = "0.8.0"
|
||||
VERSION = "0.8.5"
|
||||
|
||||
# Declare class member vars with type hints to enable richer IDE support throughout
|
||||
# the code.
|
||||
|
||||
@@ -154,7 +154,8 @@ class PSBTOverviewScreen(ButtonListScreen):
|
||||
destination_column.append(_("fee"))
|
||||
|
||||
if self.has_op_return:
|
||||
destination_column.append("OP_RETURN")
|
||||
# TRANSLATOR_NOTE: Technical term, should probably NOT be translated in most languages
|
||||
destination_column.append(_("OP_RETURN"))
|
||||
|
||||
if self.num_change_outputs > 0:
|
||||
for i in range(0, self.num_change_outputs):
|
||||
@@ -731,8 +732,10 @@ class PSBTOpReturnScreen(ButtonListScreen):
|
||||
text += (decoded_str[i*chars_per_line:(i+1)*chars_per_line]) + "\n"
|
||||
text = text[:-1]
|
||||
|
||||
# TRANSLATOR_NOTE: Shown when displaying OP_RETURN as non-human-readable hexadecimal data
|
||||
hex_label = _("raw hex data")
|
||||
label = TextArea(
|
||||
text="raw hex data",
|
||||
text=hex_label,
|
||||
font_color=GUIConstants.LABEL_FONT_COLOR,
|
||||
font_size=GUIConstants.LABEL_FONT_SIZE,
|
||||
screen_y=self.top_nav.height,
|
||||
|
||||
@@ -57,7 +57,8 @@ class ScanScreen(BaseScreen):
|
||||
# Initialize the base class
|
||||
super().__post_init__()
|
||||
|
||||
self.instructions_text = _("< back | ") + self.instructions_text
|
||||
# TODO: Arrange this with UI elements rather than text
|
||||
self.instructions_text = "< " + _("back") + " | " + _(self.instructions_text)
|
||||
|
||||
self.camera = Camera.get_instance()
|
||||
self.camera.start_video_stream_mode(resolution=self.resolution, framerate=self.framerate, format="rgb")
|
||||
@@ -209,7 +210,6 @@ class ScanScreen(BaseScreen):
|
||||
draw.text(
|
||||
xy=(rectangle.width - GUIConstants.EDGE_PADDING, int(rectangle.height / 2)),
|
||||
text=text,
|
||||
# text=f"100%",
|
||||
fill=GUIConstants.BODY_FONT_COLOR,
|
||||
font=instructions_font,
|
||||
anchor="rm", # right-justified, middle
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import time
|
||||
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from gettext import gettext as _
|
||||
from PIL import Image, ImageDraw, ImageColor
|
||||
from typing import Any, List, Tuple
|
||||
@@ -206,7 +206,7 @@ class BaseTopNavScreen(BaseScreen):
|
||||
self.top_nav = TopNav(
|
||||
icon_name=self.top_nav_icon_name,
|
||||
icon_color=self.top_nav_icon_color,
|
||||
text=self.title,
|
||||
text=_(self.title), # Wrap here for just-in-time translations
|
||||
font_size=self.title_font_size,
|
||||
width=self.canvas_width,
|
||||
height=GUIConstants.TOP_NAV_HEIGHT,
|
||||
@@ -323,7 +323,7 @@ class ButtonListScreen(BaseTopNavScreen):
|
||||
(button_label, icon_name, icon_color, button_label_color, right_icon_name) = button_label
|
||||
|
||||
button_kwargs = dict(
|
||||
text=button_label,
|
||||
text=_(button_label), # Wrap here for just-in-time translations
|
||||
icon_name=icon_name,
|
||||
icon_color=icon_color if icon_color else GUIConstants.BUTTON_FONT_COLOR,
|
||||
is_icon_inline=True,
|
||||
@@ -565,7 +565,7 @@ class LargeButtonScreen(BaseTopNavScreen):
|
||||
button_start_x = GUIConstants.EDGE_PADDING + button_width + GUIConstants.COMPONENT_PADDING
|
||||
|
||||
button_args = {
|
||||
"text": button_label,
|
||||
"text": _(button_label), # Wrap here for just-in-time translations
|
||||
"screen_x": button_start_x,
|
||||
"screen_y": button_start_y,
|
||||
"width": button_width,
|
||||
@@ -855,24 +855,19 @@ class QRDisplayScreen(BaseScreen):
|
||||
|
||||
@dataclass
|
||||
class LargeIconStatusScreen(ButtonListScreen):
|
||||
title: str = None
|
||||
title: str = _("Success!")
|
||||
status_icon_name: str = SeedSignerIconConstants.SUCCESS
|
||||
status_icon_size: int = GUIConstants.ICON_PRIMARY_SCREEN_SIZE
|
||||
status_color: str = GUIConstants.SUCCESS_COLOR
|
||||
status_headline: str = None
|
||||
text: str = "" # The body text of the screen
|
||||
text_edge_padding: int = GUIConstants.EDGE_PADDING
|
||||
button_data: list = None
|
||||
button_data: list = field(default_factory=lambda: [_("OK")])
|
||||
allow_text_overflow: bool = False
|
||||
|
||||
|
||||
def __post_init__(self):
|
||||
if not self.title:
|
||||
self.title = _("Success!")
|
||||
|
||||
self.is_bottom_list: bool = True
|
||||
if not self.button_data:
|
||||
self.button_data = [_("OK")]
|
||||
self.is_bottom_list = True
|
||||
super().__post_init__()
|
||||
|
||||
self.status_icon = Icon(
|
||||
@@ -887,7 +882,7 @@ class LargeIconStatusScreen(ButtonListScreen):
|
||||
next_y = self.status_icon.screen_y + self.status_icon.height + int(GUIConstants.COMPONENT_PADDING/2)
|
||||
if self.status_headline:
|
||||
self.warning_headline_textarea = TextArea(
|
||||
text=self.status_headline,
|
||||
text=_(self.status_headline), # Wrap here for just-in-time translations
|
||||
width=self.canvas_width,
|
||||
screen_y=next_y,
|
||||
font_color=self.status_color,
|
||||
@@ -897,7 +892,7 @@ class LargeIconStatusScreen(ButtonListScreen):
|
||||
|
||||
self.components.append(TextArea(
|
||||
height=self.buttons[0].screen_y - next_y,
|
||||
text=self.text,
|
||||
text=_(self.text),
|
||||
width=self.canvas_width,
|
||||
edge_padding=self.text_edge_padding, # Don't render all the way up to the far left/right edges
|
||||
screen_y=next_y,
|
||||
@@ -983,36 +978,19 @@ class WarningEdgesMixin:
|
||||
|
||||
@dataclass
|
||||
class WarningScreen(WarningEdgesMixin, LargeIconStatusScreen):
|
||||
title: str = None
|
||||
title: str = _("Caution")
|
||||
status_icon_name: str = SeedSignerIconConstants.WARNING
|
||||
status_color: str = "yellow"
|
||||
status_headline: str = "Privacy Leak!" # The colored text under the alert icon
|
||||
|
||||
def __post_init__(self):
|
||||
if not self.title:
|
||||
self.title = _("Caution")
|
||||
|
||||
if not self.status_headline:
|
||||
self.status_headline = _("Privacy Leak!")
|
||||
|
||||
if not self.button_data:
|
||||
self.button_data = [_("I Understand")]
|
||||
|
||||
super().__post_init__()
|
||||
status_headline: str = _("Privacy Leak!") # The colored text under the alert icon
|
||||
button_data: list = field(default_factory=lambda: [_("I Understand")])
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class DireWarningScreen(WarningScreen):
|
||||
status_headline: str = "Classified Info!" # The colored text under the alert icon
|
||||
status_headline: str = _("Classified Info!") # The colored text under the alert icon
|
||||
status_color: str = GUIConstants.DIRE_WARNING_COLOR
|
||||
|
||||
def __post_init__(self):
|
||||
if not self.status_headline:
|
||||
self.status_headline = _("Classified Info!")
|
||||
|
||||
super().__post_init__()
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -1048,13 +1026,12 @@ class PowerOffScreen(BaseTopNavScreen):
|
||||
@dataclass
|
||||
class PowerOffNotRequiredScreen(BaseTopNavScreen):
|
||||
def __post_init__(self):
|
||||
self.title = "Just Unplug It"
|
||||
self.title = _("Just Unplug It")
|
||||
self.show_back_button = True
|
||||
super().__post_init__()
|
||||
|
||||
text = _("It is safe to disconnect power at any time.")
|
||||
self.components.append(TextArea(
|
||||
text=text,
|
||||
text=_("It is safe to disconnect power at any time."),
|
||||
screen_y=self.top_nav.height,
|
||||
height=self.canvas_height - self.top_nav.height,
|
||||
))
|
||||
|
||||
@@ -433,7 +433,6 @@ class SeedFinalizeScreen(ButtonListScreen):
|
||||
|
||||
@dataclass
|
||||
class SeedOptionsScreen(ButtonListScreen):
|
||||
# Customize defaults
|
||||
fingerprint: str = None
|
||||
has_passphrase: bool = False
|
||||
|
||||
|
||||
@@ -309,12 +309,11 @@ class DonateScreen(BaseTopNavScreen):
|
||||
@dataclass
|
||||
class SettingsQRConfirmationScreen(ButtonListScreen):
|
||||
config_name: str = None
|
||||
title: str = "Settings QR"
|
||||
status_message: str = "Settings updated..."
|
||||
title: str = _("Settings QR")
|
||||
status_message: str = _("Settings updated...")
|
||||
is_bottom_list: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
self.title = _("Settings QR")
|
||||
# Customize defaults
|
||||
self.button_data = [_("Home")]
|
||||
self.show_back_button = False
|
||||
@@ -323,7 +322,7 @@ class SettingsQRConfirmationScreen(ButtonListScreen):
|
||||
start_y = self.top_nav.height + 20
|
||||
if self.config_name:
|
||||
self.config_name_textarea = TextArea(
|
||||
text=f'"{self.config_name}"',
|
||||
text=f'"{self.config_name}"', # User-supplied string (from SettingsQR); don't wrap to translate
|
||||
is_text_centered=True,
|
||||
auto_line_break=True,
|
||||
screen_y=start_y
|
||||
@@ -332,7 +331,7 @@ class SettingsQRConfirmationScreen(ButtonListScreen):
|
||||
start_y = self.config_name_textarea.screen_y + 50
|
||||
|
||||
self.components.append(TextArea(
|
||||
text=self.status_message,
|
||||
text=_(self.status_message),
|
||||
is_text_centered=True,
|
||||
auto_line_break=True,
|
||||
screen_y=start_y
|
||||
|
||||
@@ -75,7 +75,7 @@ class ToolsImageEntropyLivePreviewScreen(BaseScreen):
|
||||
int(self.renderer.canvas_width/2),
|
||||
self.renderer.canvas_height - GUIConstants.EDGE_PADDING
|
||||
),
|
||||
text="< back | click joystick",
|
||||
text="< " + _("back") + " | " + _("click joystick"), # TODO: Render with UI elements instead of text
|
||||
fill=GUIConstants.BODY_FONT_COLOR,
|
||||
font=instructions_font,
|
||||
stroke_width=4,
|
||||
@@ -100,13 +100,18 @@ class ToolsImageEntropyFinalImageScreen(BaseScreen):
|
||||
instructions_font = Fonts.get_font(GUIConstants.get_body_font_name(), GUIConstants.get_button_font_size())
|
||||
|
||||
self.renderer.canvas.paste(self.final_image)
|
||||
|
||||
# TRANSLATOR_NOTE: A prompt to the user to either accept or reshoot the image
|
||||
reshoot = _("reshoot")
|
||||
|
||||
# TRANSLATOR_NOTE: A prompt to the user to either accept or reshoot the image
|
||||
accept = _("accept")
|
||||
self.renderer.draw.text(
|
||||
xy=(
|
||||
int(self.renderer.canvas_width/2),
|
||||
self.renderer.canvas_height - GUIConstants.EDGE_PADDING
|
||||
),
|
||||
# TRANSLATOR_NOTE: Instructions when reviewing an image; "reshoot" must point left, "accept" must point right
|
||||
text=_(" < reshoot | accept > "),
|
||||
text=" < " + reshoot + " | " + accept + " > ",
|
||||
fill=GUIConstants.BODY_FONT_COLOR,
|
||||
font=instructions_font,
|
||||
stroke_width=4,
|
||||
@@ -253,9 +258,10 @@ class ToolsCalcFinalWordScreen(ButtonListScreen):
|
||||
# significant n bits always rendered in same column)
|
||||
discard_selected_bits = "_" * (len(self.checksum_bits))
|
||||
|
||||
# TRANSLATOR_NOTE: The additional entropy the user supplied (e.g. coin flips)
|
||||
your_input = _('Your input: "{}"').format(selection_text)
|
||||
self.components.append(TextArea(
|
||||
# TRANSLATOR_NOTE: A labeled presentation of a user-supplied value
|
||||
text=_('Your input: "{}"').format(selection_text),
|
||||
text=your_input,
|
||||
screen_y=self.top_nav.height + GUIConstants.COMPONENT_PADDING - 2, # Nudge to last line doesn't get too close to "Next" button
|
||||
height_ignores_below_baseline=True, # Keep the next line (bits display) snugged up, regardless of text rendering below the baseline
|
||||
))
|
||||
|
||||
@@ -57,6 +57,7 @@ class SettingsConstants:
|
||||
LOCALE__PORTUGUESE = "pt"
|
||||
LOCALE__RUSSIAN = "ru"
|
||||
LOCALE__SPANISH = "es"
|
||||
# Do not wrap for translation; always present each language in its native form
|
||||
ALL_LOCALES = [
|
||||
# (LOCALE__ARABIC, "Arabic"),
|
||||
(LOCALE__CZECH, "čeština"),
|
||||
@@ -76,10 +77,10 @@ class SettingsConstants:
|
||||
BTC_DENOMINATION__THRESHOLD = "thr"
|
||||
BTC_DENOMINATION__BTCSATSHYBRID = "hyb"
|
||||
ALL_BTC_DENOMINATIONS = [
|
||||
(BTC_DENOMINATION__BTC, "BTC"),
|
||||
(BTC_DENOMINATION__SATS, "sats"),
|
||||
(BTC_DENOMINATION__THRESHOLD, "Threshold at 0.01"),
|
||||
(BTC_DENOMINATION__BTCSATSHYBRID, "BTC | sats hybrid"),
|
||||
(BTC_DENOMINATION__BTC, _("BTC")),
|
||||
(BTC_DENOMINATION__SATS, _("sats")),
|
||||
(BTC_DENOMINATION__THRESHOLD, _("Threshold at 0.01")),
|
||||
(BTC_DENOMINATION__BTCSATSHYBRID, _("BTC | sats hybrid")),
|
||||
]
|
||||
|
||||
CAMERA_ROTATION__0 = 0
|
||||
@@ -97,10 +98,16 @@ class SettingsConstants:
|
||||
DENSITY__LOW = "L"
|
||||
DENSITY__MEDIUM = "M"
|
||||
DENSITY__HIGH = "H"
|
||||
# TRANSLATOR_NOTE: QR code density option: Low, Medium, High
|
||||
density_low = _("Low")
|
||||
# TRANSLATOR_NOTE: QR code density option: Low, Medium, High
|
||||
density_medium = _("Medium")
|
||||
# TRANSLATOR_NOTE: QR code density option: Low, Medium, High
|
||||
density_high = _("High")
|
||||
ALL_DENSITIES = [
|
||||
(DENSITY__LOW, _("Low")),
|
||||
(DENSITY__MEDIUM, _("Medium")),
|
||||
(DENSITY__HIGH, _("High")),
|
||||
(DENSITY__LOW, density_low),
|
||||
(DENSITY__MEDIUM, density_medium),
|
||||
(DENSITY__HIGH, density_high),
|
||||
]
|
||||
|
||||
# Seed-related constants
|
||||
@@ -115,6 +122,7 @@ class SettingsConstants:
|
||||
|
||||
@classmethod
|
||||
def map_network_to_embit(cls, network) -> str:
|
||||
# Note these are `embit` constants; do not wrap for translation
|
||||
if network == SettingsConstants.MAINNET:
|
||||
return "main"
|
||||
elif network == SettingsConstants.TESTNET:
|
||||
@@ -138,11 +146,11 @@ class SettingsConstants:
|
||||
TAPROOT = "tr"
|
||||
CUSTOM_DERIVATION = "cus"
|
||||
ALL_SCRIPT_TYPES = [
|
||||
(NATIVE_SEGWIT, "Native Segwit"),
|
||||
(NESTED_SEGWIT, "Nested Segwit"),
|
||||
(LEGACY_P2PKH, "Legacy"),
|
||||
(TAPROOT, "Taproot"),
|
||||
(CUSTOM_DERIVATION, "Custom Derivation"),
|
||||
(NATIVE_SEGWIT, _("Native Segwit")),
|
||||
(NESTED_SEGWIT, _("Nested Segwit")),
|
||||
(LEGACY_P2PKH, _("Legacy")),
|
||||
(TAPROOT, _("Taproot")),
|
||||
(CUSTOM_DERIVATION, _("Custom Derivation")),
|
||||
]
|
||||
|
||||
WORDLIST_LANGUAGE__ENGLISH = "en"
|
||||
@@ -166,6 +174,7 @@ class SettingsConstants:
|
||||
|
||||
|
||||
# Individual SettingsEntry attr_names
|
||||
# Note: attr_names are internal constants; do not wrap for translation
|
||||
SETTING__LOCALE = "locale"
|
||||
SETTING__WORDLIST_LANGUAGE = "wordlist_language"
|
||||
SETTING__PERSISTENT_SETTINGS = "persistent_settings"
|
||||
@@ -228,8 +237,10 @@ class SettingsConstants:
|
||||
ELECTRUM_PBKDF2_ROUNDS=2048
|
||||
|
||||
# Label strings
|
||||
LABEL__BIP39_PASSPHRASE = "BIP-39 Passphrase"
|
||||
LABEL__CUSTOM_EXTENSION = "Custom Extension" # Terminology used by Electrum seeds
|
||||
LABEL__BIP39_PASSPHRASE = _("BIP-39 Passphrase")
|
||||
# TRANSLATOR_NOTE: Terminology used by Electrum seeds; equivalent to bip39 passphrase
|
||||
custom_extension = _("Custom Extension")
|
||||
LABEL__CUSTOM_EXTENSION = custom_extension
|
||||
|
||||
|
||||
|
||||
@@ -368,8 +379,8 @@ class SettingsDefinition:
|
||||
|
||||
SettingsEntry(category=SettingsConstants.CATEGORY__SYSTEM,
|
||||
attr_name=SettingsConstants.SETTING__LOCALE,
|
||||
display_name=_("Language"),
|
||||
abbreviated_name="lang",
|
||||
display_name=_("Language"),
|
||||
type=SettingsConstants.TYPE__SELECT_1,
|
||||
selection_options=SettingsConstants.ALL_LOCALES,
|
||||
default_value=SettingsConstants.LOCALE__ENGLISH),
|
||||
@@ -494,7 +505,7 @@ class SettingsDefinition:
|
||||
attr_name=SettingsConstants.SETTING__ELECTRUM_SEEDS,
|
||||
abbreviated_name="electrum",
|
||||
display_name="Electrum seeds",
|
||||
help_text="Native Segwit only",
|
||||
help_text=_("Native Segwit only"),
|
||||
visibility=SettingsConstants.VISIBILITY__ADVANCED,
|
||||
default_value=SettingsConstants.OPTION__DISABLED),
|
||||
|
||||
|
||||
Submodule src/seedsigner/resources/seedsigner-translations updated: 7b4f05a4cf...23bb8f6533
@@ -15,11 +15,10 @@ from seedsigner.views.view import BackStackView, MainMenuView, NotYetImplemented
|
||||
|
||||
|
||||
class PSBTSelectSeedView(View):
|
||||
# TODO: Class-level attrs vs dynamic gettext calls
|
||||
SCAN_SEED = ("Scan a seed", SeedSignerIconConstants.QRCODE)
|
||||
TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_ELECTRUM = ("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
|
||||
TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
|
||||
|
||||
def run(self):
|
||||
@@ -29,9 +28,6 @@ class PSBTSelectSeedView(View):
|
||||
# Shouldn't be able to get here
|
||||
raise Exception("No PSBT currently loaded")
|
||||
|
||||
self.SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
|
||||
self.TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
self.TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
if self.controller.psbt_seed:
|
||||
if PSBTParser.has_matching_input_fingerprint(psbt=self.controller.psbt, seed=self.controller.psbt_seed, network=self.settings.get_value(SettingsConstants.SETTING__NETWORK)):
|
||||
# skip the seed prompt if a seed was previous selected and has matching input fingerprint
|
||||
@@ -43,7 +39,8 @@ class PSBTSelectSeedView(View):
|
||||
button_str = seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
|
||||
if not PSBTParser.has_matching_input_fingerprint(psbt=self.controller.psbt, seed=seed, network=self.settings.get_value(SettingsConstants.SETTING__NETWORK)):
|
||||
# Doesn't look like this seed can sign the current PSBT
|
||||
button_str += " (?)"
|
||||
# TRANSLATOR_NOTE: Inserts fingerprint w/"?" to indicate that this seed can't sign the current PSBT
|
||||
button_str = _("{} (?)").format(button_str)
|
||||
|
||||
button_data.append((button_str, SeedSignerIconConstants.FINGERPRINT))
|
||||
|
||||
@@ -303,9 +300,9 @@ class PSBTAddressDetailsView(View):
|
||||
|
||||
|
||||
class PSBTChangeDetailsView(View):
|
||||
NEXT = "Next"
|
||||
SKIP_VERIFICATION = "Skip Verification"
|
||||
VERIFY_MULTISIG = "Verify Multisig Change"
|
||||
NEXT = _("Next")
|
||||
SKIP_VERIFICATION = _("Skip Verification")
|
||||
VERIFY_MULTISIG = _("Verify Multisig Change")
|
||||
|
||||
|
||||
def __init__(self, change_address_num):
|
||||
@@ -347,12 +344,9 @@ class PSBTChangeDetailsView(View):
|
||||
is_change_derivation_path = int(derivation_path.split("/")[-2]) == 1
|
||||
derivation_path_addr_index = int(derivation_path.split("/")[-1])
|
||||
|
||||
self.NEXT = _("Next")
|
||||
|
||||
if is_change_derivation_path:
|
||||
# TRANSLATOR_NOTE: The amount you're receiving back from the transaction
|
||||
title = _("Your Change")
|
||||
self.VERIFY_MULTISIG = _("Verify Multisig Change")
|
||||
else:
|
||||
title = _("Self-Transfer")
|
||||
self.VERIFY_MULTISIG = _("Verify Multisig Addr")
|
||||
@@ -569,11 +563,10 @@ class PSBTSignedQRDisplayView(View):
|
||||
|
||||
|
||||
class PSBTSigningErrorView(View):
|
||||
SELECT_DIFF_SEED = "Select Diff Seed"
|
||||
SELECT_DIFF_SEED = _("Select Diff Seed")
|
||||
|
||||
def run(self):
|
||||
psbt_parser: PSBTParser = self.controller.psbt_parser
|
||||
self.SELECT_DIFF_SEED = _("Select Diff Seed")
|
||||
if not psbt_parser:
|
||||
# Should not be able to get here
|
||||
return Destination(MainMenuView)
|
||||
|
||||
@@ -62,10 +62,11 @@ class ScanView(View):
|
||||
# current flow.
|
||||
# Report QR types in more human-readable text (e.g. QRType
|
||||
# `seed__compactseedqr` as "seed: compactseedqr").
|
||||
# TODO: cleanup l10n presentation
|
||||
return Destination(ErrorView, view_args=dict(
|
||||
title="Error",
|
||||
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",
|
||||
next_destination=Destination(BackStackView, skip_current_view=True),
|
||||
))
|
||||
|
||||
@@ -78,10 +78,10 @@ class SeedSelectSeedView(View):
|
||||
* `flow`: indicates which user flow is in progress during seed selection (e.g.
|
||||
verify single sig addr or sign message).
|
||||
"""
|
||||
SCAN_SEED = ("Scan a seed", SeedSignerIconConstants.QRCODE)
|
||||
TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_ELECTRUM = ("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
|
||||
TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
|
||||
|
||||
def __init__(self, flow: str = Controller.FLOW__VERIFY_SINGLESIG_ADDR):
|
||||
@@ -90,9 +90,6 @@ class SeedSelectSeedView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
self.SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
|
||||
self.TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
self.TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
seeds = self.controller.storage.seeds
|
||||
|
||||
if self.flow == Controller.FLOW__VERIFY_SINGLESIG_ADDR:
|
||||
@@ -172,17 +169,13 @@ class SeedSelectSeedView(View):
|
||||
Loading seeds, passphrases, etc
|
||||
****************************************************************************"""
|
||||
class LoadSeedView(View):
|
||||
SEED_QR = ("Scan a SeedQR", SeedSignerIconConstants.QRCODE)
|
||||
TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_ELECTRUM = ("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
CREATE = (" Create a seed", SeedSignerIconConstants.PLUS)
|
||||
SEED_QR = (_("Scan a SeedQR"), SeedSignerIconConstants.QRCODE)
|
||||
TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
CREATE = (_(" Create a seed"), SeedSignerIconConstants.PLUS)
|
||||
|
||||
def run(self):
|
||||
self.SEED_QR = (_("Scan a SeedQR"), SeedSignerIconConstants.QRCODE)
|
||||
self.TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
self.TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
self.CREATE = (_("Create a seed"), SeedSignerIconConstants.PLUS)
|
||||
button_data = [
|
||||
self.SEED_QR,
|
||||
self.TYPE_12WORD,
|
||||
@@ -284,8 +277,8 @@ class SeedMnemonicEntryView(View):
|
||||
|
||||
|
||||
class SeedMnemonicInvalidView(View):
|
||||
EDIT = "Review & Edit"
|
||||
DISCARD = ("Discard", None, None, "red")
|
||||
EDIT = _("Review & Edit")
|
||||
DISCARD = (_("Discard"), None, None, "red")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -293,8 +286,6 @@ class SeedMnemonicInvalidView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
self.EDIT = _("Review & Edit")
|
||||
self.DISCARD = (_("Discard"), None, None, "red")
|
||||
button_data = [self.EDIT, self.DISCARD]
|
||||
selected_menu_num = self.run_screen(
|
||||
WarningScreen,
|
||||
@@ -315,7 +306,8 @@ class SeedMnemonicInvalidView(View):
|
||||
|
||||
|
||||
class SeedFinalizeView(View):
|
||||
FINALIZE = "Done"
|
||||
FINALIZE = _("Done")
|
||||
PASSPHRASE = _("BIP-39 Passphrase")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -337,9 +329,6 @@ class SeedFinalizeView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
self.FINALIZE = _("Done")
|
||||
self.PASSPHRASE = _("BIP-39 Passphrase")
|
||||
|
||||
button_data = [self.FINALIZE]
|
||||
passphrase_button = self.seed.passphrase_label
|
||||
if self.settings.get_value(SettingsConstants.SETTING__PASSPHRASE) != SettingsConstants.OPTION__DISABLED:
|
||||
@@ -391,8 +380,8 @@ class SeedAddPassphraseView(View):
|
||||
|
||||
|
||||
class SeedAddPassphraseExitDialogView(View):
|
||||
EDIT = "Edit passphrase"
|
||||
DISCARD = ("Discard passphrase", None, None, "red")
|
||||
EDIT = _("Edit passphrase")
|
||||
DISCARD = (_("Discard passphrase"), None, None, "red")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -404,9 +393,9 @@ class SeedAddPassphraseExitDialogView(View):
|
||||
|
||||
selected_menu_num = self.run_screen(
|
||||
WarningScreen,
|
||||
title="Discard passphrase?",
|
||||
title=_("Discard passphrase?"),
|
||||
status_headline=None,
|
||||
text=f"Your current passphrase entry will be erased",
|
||||
text=_("Your current passphrase entry will be erased"),
|
||||
show_back_button=False,
|
||||
button_data=button_data,
|
||||
)
|
||||
@@ -424,8 +413,8 @@ class SeedReviewPassphraseView(View):
|
||||
"""
|
||||
Display the completed passphrase back to the user.
|
||||
"""
|
||||
EDIT = "Edit passphrase"
|
||||
DONE = "Done"
|
||||
EDIT = _("Edit passphrase")
|
||||
DONE = _("Done")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -433,9 +422,6 @@ class SeedReviewPassphraseView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
self.EDIT = _("Edit passphrase")
|
||||
self.DONE = _("Done")
|
||||
|
||||
# Get the before/after fingerprints
|
||||
network = self.settings.get_value(SettingsConstants.SETTING__NETWORK)
|
||||
passphrase = self.seed.passphrase
|
||||
@@ -467,8 +453,8 @@ class SeedReviewPassphraseView(View):
|
||||
|
||||
|
||||
class SeedDiscardView(View):
|
||||
KEEP = "Keep Seed"
|
||||
DISCARD = ("Discard", None, None, "red")
|
||||
KEEP = _("Keep Seed")
|
||||
DISCARD = (_("Discard"), None, None, "red")
|
||||
|
||||
def __init__(self, seed_num: int = None):
|
||||
super().__init__()
|
||||
@@ -480,9 +466,6 @@ class SeedDiscardView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
self.KEEP = _("Keep Seed")
|
||||
self.DISCARD = (_("Discard"), None, None, "red")
|
||||
|
||||
button_data = [self.KEEP, self.DISCARD]
|
||||
|
||||
fingerprint = self.seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
|
||||
@@ -522,9 +505,9 @@ class SeedElectrumMnemonicStartView(View):
|
||||
def run(self):
|
||||
self.run_screen(
|
||||
WarningScreen,
|
||||
title="Electrum warning",
|
||||
title=_("Electrum warning"),
|
||||
status_headline=None,
|
||||
text=f"Some features are disabled for Electrum seeds.",
|
||||
text=_("Some features are disabled for Electrum seeds."),
|
||||
show_back_button=False,
|
||||
)
|
||||
|
||||
@@ -538,14 +521,14 @@ class SeedElectrumMnemonicStartView(View):
|
||||
Views for actions on individual seeds:
|
||||
****************************************************************************"""
|
||||
class SeedOptionsView(View):
|
||||
SCAN_PSBT = ("Scan PSBT", SeedSignerIconConstants.QRCODE)
|
||||
VERIFY_ADDRESS = "Verify Addr"
|
||||
EXPORT_XPUB = "Export Xpub"
|
||||
EXPLORER = "Address Explorer"
|
||||
SIGN_MESSAGE = "Sign Message"
|
||||
BACKUP = ("Backup Seed", None, None, None, SeedSignerIconConstants.CHEVRON_RIGHT)
|
||||
BIP85_CHILD_SEED = "BIP-85 Child Seed"
|
||||
DISCARD = ("Discard Seed", None, None, "red")
|
||||
SCAN_PSBT = (_("Scan PSBT"), SeedSignerIconConstants.QRCODE)
|
||||
VERIFY_ADDRESS = _("Verify Addr")
|
||||
EXPORT_XPUB = _("Export Xpub")
|
||||
EXPLORER = _("Address Explorer")
|
||||
SIGN_MESSAGE = _("Sign Message")
|
||||
BACKUP = (_("Backup Seed"), None, None, None, SeedSignerIconConstants.CHEVRON_RIGHT)
|
||||
BIP85_CHILD_SEED = _("BIP-85 Child Seed")
|
||||
DISCARD = (_("Discard Seed"), None, None, "red")
|
||||
|
||||
|
||||
def __init__(self, seed_num: int):
|
||||
@@ -557,13 +540,6 @@ class SeedOptionsView(View):
|
||||
def run(self):
|
||||
from seedsigner.views.psbt_views import PSBTOverviewView
|
||||
|
||||
self.SCAN_PSBT = (_("Scan PSBT"), SeedSignerIconConstants.QRCODE)
|
||||
self.VERIFY_ADDRESS = _("Verify Addr")
|
||||
self.EXPORT_XPUB = _("Export Xpub")
|
||||
self.EXPLORER = _("Address Explorer")
|
||||
self.BACKUP = (_("Backup Seed"), None, None, None, SeedSignerIconConstants.CHEVRON_RIGHT)
|
||||
self.BIP85_CHILD_SEED = _("BIP-85 Child Seed")
|
||||
self.DISCARD = (_("Discard Seed"), None, None, "red")
|
||||
if self.controller.unverified_address:
|
||||
if self.controller.resume_main_flow == Controller.FLOW__VERIFY_SINGLESIG_ADDR:
|
||||
# Jump straight back into the single sig addr verification flow
|
||||
@@ -655,8 +631,8 @@ class SeedOptionsView(View):
|
||||
|
||||
|
||||
class SeedBackupView(View):
|
||||
VIEW_WORDS = "View Seed Words"
|
||||
EXPORT_SEEDQR = "Export as SeedQR"
|
||||
VIEW_WORDS = _("View Seed Words")
|
||||
EXPORT_SEEDQR = _("Export as SeedQR")
|
||||
|
||||
def __init__(self, seed_num):
|
||||
super().__init__()
|
||||
@@ -665,8 +641,6 @@ class SeedBackupView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
self.VIEW_WORDS = _("View Seed Words")
|
||||
self.EXPORT_SEEDQR = _("Export as SeedQR")
|
||||
button_data = [self.VIEW_WORDS]
|
||||
|
||||
if self.seed.seedqr_supported:
|
||||
@@ -694,8 +668,8 @@ class SeedBackupView(View):
|
||||
Export Xpub flow
|
||||
****************************************************************************"""
|
||||
class SeedExportXpubSigTypeView(View):
|
||||
SINGLE_SIG = "Single Sig"
|
||||
MULTISIG = "Multisig"
|
||||
SINGLE_SIG = _("Single Sig")
|
||||
MULTISIG = _("Multisig")
|
||||
|
||||
def __init__(self, seed_num: int):
|
||||
super().__init__()
|
||||
@@ -707,8 +681,6 @@ class SeedExportXpubSigTypeView(View):
|
||||
# Nothing to select; skip this screen
|
||||
return Destination(SeedExportXpubScriptTypeView, view_args={"seed_num": self.seed_num, "sig_type": self.settings.get_value(SettingsConstants.SETTING__SIG_TYPES)[0]}, skip_current_view=True)
|
||||
|
||||
self.SINGLE_SIG = _("Single Sig")
|
||||
self.MULTISIG = _("Multisig")
|
||||
button_data=[self.SINGLE_SIG, self.MULTISIG]
|
||||
|
||||
selected_menu_num = self.run_screen(
|
||||
@@ -765,7 +737,7 @@ class SeedExportXpubScriptTypeView(View):
|
||||
button_data = []
|
||||
for script_type, display_name in SettingsConstants.ALL_SCRIPT_TYPES:
|
||||
if script_type in self.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES):
|
||||
button_data.append((script_type, _(display_name)))
|
||||
button_data.append((script_type, display_name))
|
||||
|
||||
selected_menu_num = self.run_screen(
|
||||
ButtonListScreen,
|
||||
@@ -1071,6 +1043,9 @@ class SeedWordsWarningView(View):
|
||||
|
||||
|
||||
class SeedWordsView(View):
|
||||
NEXT = _("Next")
|
||||
DONE = _("Done")
|
||||
|
||||
def __init__(self, seed_num: int, bip85_data: dict = None, page_index: int = 0):
|
||||
super().__init__()
|
||||
self.seed_num = seed_num
|
||||
@@ -1083,9 +1058,6 @@ class SeedWordsView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
NEXT = _("Next")
|
||||
DONE = _("Done")
|
||||
|
||||
# Slice the mnemonic to our current 4-word section
|
||||
words_per_page = 4 # TODO: eventually make this configurable for bigger screens?
|
||||
|
||||
@@ -1101,9 +1073,9 @@ class SeedWordsView(View):
|
||||
button_data = []
|
||||
num_pages = int(len(mnemonic)/words_per_page)
|
||||
if self.page_index < num_pages - 1 or self.seed_num is None:
|
||||
button_data.append(NEXT)
|
||||
button_data.append(self.NEXT)
|
||||
else:
|
||||
button_data.append(DONE)
|
||||
button_data.append(self.DONE)
|
||||
|
||||
selected_menu_num = seed_screens.SeedWordsScreen(
|
||||
title=f"{title}: {self.page_index+1}/{num_pages}",
|
||||
@@ -1116,7 +1088,7 @@ class SeedWordsView(View):
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
if button_data[selected_menu_num] == NEXT:
|
||||
if button_data[selected_menu_num] == self.NEXT:
|
||||
if self.seed_num is None and self.page_index == num_pages - 1:
|
||||
return Destination(
|
||||
SeedWordsBackupTestPromptView,
|
||||
@@ -1128,7 +1100,7 @@ class SeedWordsView(View):
|
||||
view_args=dict(seed_num=self.seed_num, page_index=self.page_index + 1, bip85_data=self.bip85_data)
|
||||
)
|
||||
|
||||
elif button_data[selected_menu_num] == DONE:
|
||||
elif button_data[selected_menu_num] == self.DONE:
|
||||
# Must clear history to avoid BACK button returning to private info
|
||||
return Destination(
|
||||
SeedWordsBackupTestPromptView,
|
||||
@@ -1148,6 +1120,10 @@ class SeedBIP85ApplicationModeView(View):
|
||||
* WIF (HDSEED)
|
||||
* XPRV (BIP32)
|
||||
"""
|
||||
# TODO: Future enhancement to display WIF (HD-SEED) and XPRV (Bip32)?
|
||||
WORDS_12 = _("12 Words")
|
||||
WORDS_24 = _("24 Words")
|
||||
|
||||
def __init__(self, seed_num: int):
|
||||
super().__init__()
|
||||
self.seed_num = seed_num
|
||||
@@ -1156,11 +1132,7 @@ class SeedBIP85ApplicationModeView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
# TODO: Future enhancement to display WIF (HD-SEED) and XPRV (Bip32)?
|
||||
WORDS_12 = _("12 Words")
|
||||
WORDS_24 = _("24 Words")
|
||||
|
||||
button_data = [WORDS_12, WORDS_24]
|
||||
button_data = [self.WORDS_12, self.WORDS_24]
|
||||
|
||||
selected_menu_num = ButtonListScreen(
|
||||
title=_("BIP-85 Num Words"),
|
||||
@@ -1170,9 +1142,9 @@ class SeedBIP85ApplicationModeView(View):
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
if button_data[selected_menu_num] == WORDS_12:
|
||||
if button_data[selected_menu_num] == self.WORDS_12:
|
||||
self.num_words = 12
|
||||
elif button_data[selected_menu_num] == WORDS_24:
|
||||
elif button_data[selected_menu_num] == self.WORDS_24:
|
||||
self.num_words = 24
|
||||
|
||||
return Destination(
|
||||
@@ -1247,6 +1219,9 @@ class SeedBIP85InvalidChildIndexView(View):
|
||||
Seed Words Backup Test
|
||||
****************************************************************************"""
|
||||
class SeedWordsBackupTestPromptView(View):
|
||||
VERIFY = _("Verify")
|
||||
SKIP = _("Skip")
|
||||
|
||||
def __init__(self, seed_num: int, bip85_data: dict = None):
|
||||
super().__init__()
|
||||
self.seed_num = seed_num
|
||||
@@ -1254,20 +1229,18 @@ class SeedWordsBackupTestPromptView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
VERIFY = _("Verify")
|
||||
SKIP = _("Skip")
|
||||
button_data = [VERIFY, SKIP]
|
||||
button_data = [self.VERIFY, self.SKIP]
|
||||
selected_menu_num = seed_screens.SeedWordsBackupTestPromptScreen(
|
||||
button_data=button_data,
|
||||
).display()
|
||||
|
||||
if button_data[selected_menu_num] == VERIFY:
|
||||
if button_data[selected_menu_num] == self.VERIFY:
|
||||
return Destination(
|
||||
SeedWordsBackupTestView,
|
||||
view_args=dict(seed_num=self.seed_num, bip85_data=self.bip85_data),
|
||||
)
|
||||
|
||||
elif button_data[selected_menu_num] == SKIP:
|
||||
elif button_data[selected_menu_num] == self.SKIP:
|
||||
if self.seed_num is not None:
|
||||
return Destination(SeedOptionsView, view_args=dict(seed_num=self.seed_num))
|
||||
else:
|
||||
@@ -1352,6 +1325,9 @@ class SeedWordsBackupTestView(View):
|
||||
|
||||
|
||||
class SeedWordsBackupTestMistakeView(View):
|
||||
REVIEW = _("Review Seed Words")
|
||||
RETRY = _("Try Again")
|
||||
|
||||
def __init__(self, seed_num: int, bip85_data: dict = None, cur_index: int = None, wrong_word: str = None, confirmed_list: List[bool] = None):
|
||||
super().__init__()
|
||||
self.seed_num = seed_num
|
||||
@@ -1362,9 +1338,7 @@ class SeedWordsBackupTestMistakeView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
REVIEW = _("Review Seed Words")
|
||||
RETRY = _("Try Again")
|
||||
button_data = [REVIEW, RETRY]
|
||||
button_data = [self.REVIEW, self.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)
|
||||
@@ -1380,13 +1354,13 @@ class SeedWordsBackupTestMistakeView(View):
|
||||
text=text,
|
||||
).display()
|
||||
|
||||
if button_data[selected_menu_num] == REVIEW:
|
||||
if button_data[selected_menu_num] == self.REVIEW:
|
||||
return Destination(
|
||||
SeedWordsView,
|
||||
view_args=dict(seed_num=self.seed_num, bip85_data=self.bip85_data),
|
||||
)
|
||||
|
||||
elif button_data[selected_menu_num] == RETRY:
|
||||
elif button_data[selected_menu_num] == self.RETRY:
|
||||
return Destination(
|
||||
SeedWordsBackupTestView,
|
||||
view_args=dict(
|
||||
@@ -1598,6 +1572,9 @@ class SeedTranscribeSeedQRZoomedInView(View):
|
||||
|
||||
|
||||
class SeedTranscribeSeedQRConfirmQRPromptView(View):
|
||||
SCAN = (_("Confirm SeedQR"), SeedSignerIconConstants.QRCODE)
|
||||
DONE = _("Done")
|
||||
|
||||
def __init__(self, seed_num: int):
|
||||
super().__init__()
|
||||
self.seed_num = seed_num
|
||||
@@ -1605,9 +1582,7 @@ class SeedTranscribeSeedQRConfirmQRPromptView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
SCAN = (_("Confirm SeedQR"), SeedSignerIconConstants.QRCODE)
|
||||
DONE = _("Done")
|
||||
button_data = [SCAN, DONE]
|
||||
button_data = [self.SCAN, self.DONE]
|
||||
|
||||
selected_menu_option = seed_screens.SeedTranscribeSeedQRConfirmQRPromptScreen(
|
||||
title=_("Confirm SeedQR?"),
|
||||
@@ -1617,10 +1592,10 @@ class SeedTranscribeSeedQRConfirmQRPromptView(View):
|
||||
if selected_menu_option == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
elif button_data[selected_menu_option] == SCAN:
|
||||
elif button_data[selected_menu_option] == self.SCAN:
|
||||
return Destination(SeedTranscribeSeedQRConfirmScanView, view_args={"seed_num": self.seed_num})
|
||||
|
||||
elif button_data[selected_menu_option] == DONE:
|
||||
elif button_data[selected_menu_option] == self.DONE:
|
||||
return Destination(SeedOptionsView, view_args={"seed_num": self.seed_num}, clear_history=True)
|
||||
|
||||
|
||||
@@ -1739,13 +1714,8 @@ class AddressVerificationStartView(View):
|
||||
|
||||
|
||||
class AddressVerificationSigTypeView(View):
|
||||
SINGLE_SIG = "Single Sig"
|
||||
MULTISIG = "Multisig"
|
||||
|
||||
def __post_init__(self):
|
||||
self.SINGLE_SIG = _("Single Sig")
|
||||
self.MULTISIG = _("Multisig")
|
||||
return super().__post_init__()
|
||||
SINGLE_SIG = _("Single Sig")
|
||||
MULTISIG = _("Multisig")
|
||||
|
||||
def run(self):
|
||||
button_data = [self.SINGLE_SIG, self.MULTISIG]
|
||||
@@ -1796,6 +1766,10 @@ class SeedAddressVerificationView(View):
|
||||
Performs single sig verification on `seed_num` if specified, otherwise assumes
|
||||
multisig.
|
||||
"""
|
||||
# TRANSLATOR_NOTE: Option when scanning for a matching address; skips ten addresses ahead
|
||||
SKIP_10 = _("Skip 10")
|
||||
CANCEL = _("Cancel")
|
||||
|
||||
def __init__(self, seed_num: int = None):
|
||||
super().__init__()
|
||||
self.seed_num = seed_num
|
||||
@@ -1849,10 +1823,7 @@ class SeedAddressVerificationView(View):
|
||||
# Start brute-force calculations from the zero-th index
|
||||
self.addr_verification_thread.start()
|
||||
|
||||
# TRANSLATOR_NOTE: Option when scanning for a matching address; skips ten addresses ahead
|
||||
SKIP_10 = _("Skip 10")
|
||||
CANCEL = _("Cancel")
|
||||
button_data = [SKIP_10, CANCEL]
|
||||
button_data = [self.SKIP_10, self.CANCEL]
|
||||
|
||||
script_type_settings_entry = SettingsDefinition.get_settings_entry(SettingsConstants.SETTING__SCRIPT_TYPES)
|
||||
script_type_display = script_type_settings_entry.get_selection_option_display_name_by_value(self.script_type)
|
||||
@@ -1894,10 +1865,10 @@ class SeedAddressVerificationView(View):
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
if button_data[selected_menu_num] == SKIP_10:
|
||||
if button_data[selected_menu_num] == self.SKIP_10:
|
||||
self.threadsafe_counter.increment(10)
|
||||
|
||||
elif button_data[selected_menu_num] == CANCEL:
|
||||
elif button_data[selected_menu_num] == self.CANCEL:
|
||||
break
|
||||
|
||||
if self.verified_index.cur_count is not None:
|
||||
@@ -2013,20 +1984,11 @@ class AddressVerificationSuccessView(View):
|
||||
|
||||
|
||||
class LoadMultisigWalletDescriptorView(View):
|
||||
SCAN = ("Scan Descriptor", SeedSignerIconConstants.QRCODE)
|
||||
CANCEL = "Cancel"
|
||||
|
||||
def __post_init__(self):
|
||||
self.SCAN = (_("Scan Descriptor"), SeedSignerIconConstants.QRCODE)
|
||||
self.CANCEL = _("Cancel")
|
||||
return super().__post_init__()
|
||||
|
||||
SCAN = (_("Scan Descriptor"), SeedSignerIconConstants.QRCODE)
|
||||
CANCEL = _("Cancel")
|
||||
|
||||
def run(self):
|
||||
button_data = [
|
||||
self.SCAN,
|
||||
self.CANCEL
|
||||
]
|
||||
button_data = [self.SCAN, self.CANCEL]
|
||||
selected_menu_num = self.run_screen(
|
||||
seed_screens.LoadMultisigWalletDescriptorScreen,
|
||||
button_data=button_data,
|
||||
@@ -2046,18 +2008,10 @@ class LoadMultisigWalletDescriptorView(View):
|
||||
|
||||
|
||||
class MultisigWalletDescriptorView(View):
|
||||
RETURN = "Return to PSBT"
|
||||
VERIFY_ADDR = "Verify Addr"
|
||||
ADDRESS_EXPLORER = "Address Explorer"
|
||||
OK = "OK"
|
||||
|
||||
def __post_init__(self):
|
||||
self.RETURN = _("Return to PSBT")
|
||||
self.VERIFY = _("Verify Addr")
|
||||
self.EXPLORER = _("Address Explorer")
|
||||
self.OK = _("OK")
|
||||
super().__post_init__()
|
||||
|
||||
RETURN = _("Return to PSBT")
|
||||
VERIFY_ADDR = _("Verify Addr")
|
||||
ADDRESS_EXPLORER = _("Address Explorer")
|
||||
OK = _("OK")
|
||||
|
||||
def run(self):
|
||||
descriptor = self.controller.multisig_wallet_descriptor
|
||||
|
||||
@@ -16,8 +16,8 @@ from .view import View, Destination, BackStackView, MainMenuView
|
||||
|
||||
|
||||
class SettingsMenuView(View):
|
||||
IO_TEST = "I/O test"
|
||||
DONATE = "Donate"
|
||||
IO_TEST = _("I/O test")
|
||||
DONATE = _("Donate")
|
||||
|
||||
def __init__(self, visibility: str = SettingsConstants.VISIBILITY__GENERAL, selected_attr: str = None, initial_scroll: int = 0):
|
||||
super().__init__()
|
||||
@@ -29,13 +29,10 @@ class SettingsMenuView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
self.IO_TEST = _("I/O test")
|
||||
self.DONATE = _("Donate")
|
||||
|
||||
settings_entries = SettingsDefinition.get_settings_entries(
|
||||
visibility=self.visibility
|
||||
)
|
||||
button_data=[_(e.display_name) for e in settings_entries]
|
||||
button_data=[e.display_name for e in settings_entries]
|
||||
|
||||
selected_button = 0
|
||||
if self.selected_attr:
|
||||
@@ -121,7 +118,7 @@ class SettingsEntryUpdateSelectionView(View):
|
||||
value, display_name = value
|
||||
else:
|
||||
display_name = value
|
||||
button_data.append(_(display_name))
|
||||
button_data.append(display_name)
|
||||
|
||||
if (type(initial_value) == list and value in initial_value) or value == initial_value:
|
||||
checked_buttons.append(i)
|
||||
|
||||
@@ -27,20 +27,11 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ToolsMenuView(View):
|
||||
IMAGE = ("New seed", FontAwesomeIconConstants.CAMERA)
|
||||
DICE = ("New seed", FontAwesomeIconConstants.DICE)
|
||||
KEYBOARD = ("Calc 12th/24th word", FontAwesomeIconConstants.KEYBOARD)
|
||||
ADDRESS_EXPLORER = "Address Explorer"
|
||||
VERIFY_ADDRESS = "Verify address"
|
||||
|
||||
def __post_init__(self):
|
||||
self.IMAGE = (_("New seed"), FontAwesomeIconConstants.CAMERA)
|
||||
self.DICE = (_("New seed"), FontAwesomeIconConstants.DICE)
|
||||
self.KEYBOARD = (_("Calc 12th/24th word"), FontAwesomeIconConstants.KEYBOARD)
|
||||
self.ADDRESS_EXPLORER = _("Address Explorer")
|
||||
self.ADDRESS = _("Verify Address")
|
||||
super().__post_init__()
|
||||
|
||||
IMAGE = (_("New seed"), FontAwesomeIconConstants.CAMERA)
|
||||
DICE = (_("New seed"), FontAwesomeIconConstants.DICE)
|
||||
KEYBOARD = (_("Calc 12th/24th word"), FontAwesomeIconConstants.KEYBOARD)
|
||||
ADDRESS_EXPLORER = _("Address Explorer")
|
||||
VERIFY_ADDRESS = _("Verify Address")
|
||||
|
||||
def run(self):
|
||||
button_data = [self.IMAGE, self.DICE, self.KEYBOARD, self.ADDRESS_EXPLORER, self.VERIFY_ADDRESS]
|
||||
@@ -126,10 +117,11 @@ class ToolsImageEntropyFinalImageView(View):
|
||||
|
||||
|
||||
class ToolsImageEntropyMnemonicLengthView(View):
|
||||
TWELVE_WORDS = _("12 words")
|
||||
TWENTYFOUR_WORDS = _("24 words")
|
||||
|
||||
def run(self):
|
||||
TWELVE_WORDS = _("12 words")
|
||||
TWENTYFOUR_WORDS = _("24 words")
|
||||
button_data = [TWELVE_WORDS, TWENTYFOUR_WORDS]
|
||||
button_data = [self.TWELVE_WORDS, self.TWENTYFOUR_WORDS]
|
||||
|
||||
selected_menu_num = ButtonListScreen(
|
||||
title=_("Mnemonic Length?"),
|
||||
@@ -139,7 +131,7 @@ class ToolsImageEntropyMnemonicLengthView(View):
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
if button_data[selected_menu_num] == TWELVE_WORDS:
|
||||
if button_data[selected_menu_num] == self.TWELVE_WORDS:
|
||||
mnemonic_length = 12
|
||||
else:
|
||||
mnemonic_length = 24
|
||||
@@ -198,14 +190,15 @@ class ToolsImageEntropyMnemonicLengthView(View):
|
||||
Dice rolls Views
|
||||
****************************************************************************"""
|
||||
class ToolsDiceEntropyMnemonicLengthView(View):
|
||||
def run(self):
|
||||
# TRANSLATOR_NOTE: Inserts the number of dice rolls needed for a 12-word mnemonic
|
||||
TWELVE = _("12 words ({} rolls)").format(mnemonic_generation.DICE__NUM_ROLLS__12WORD)
|
||||
# TRANSLATOR_NOTE: Inserts the number of dice rolls needed for a 12-word mnemonic
|
||||
TWELVE = _("12 words ({} rolls)").format(mnemonic_generation.DICE__NUM_ROLLS__12WORD)
|
||||
|
||||
# TRANSLATOR_NOTE: Inserts the number of dice rolls needed for a 24-word mnemonic
|
||||
TWENTY_FOUR = _("24 words ({} rolls)").format(mnemonic_generation.DICE__NUM_ROLLS__24WORD)
|
||||
|
||||
button_data = [TWELVE, TWENTY_FOUR]
|
||||
# TRANSLATOR_NOTE: Inserts the number of dice rolls needed for a 24-word mnemonic
|
||||
TWENTY_FOUR = _("24 words ({} rolls)").format(mnemonic_generation.DICE__NUM_ROLLS__24WORD)
|
||||
|
||||
|
||||
def run(self):
|
||||
button_data = [self.TWELVE, self.TWENTY_FOUR]
|
||||
selected_menu_num = ButtonListScreen(
|
||||
title=_("Mnemonic Length"),
|
||||
is_bottom_list=True,
|
||||
@@ -216,10 +209,10 @@ class ToolsDiceEntropyMnemonicLengthView(View):
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
elif button_data[selected_menu_num] == TWELVE:
|
||||
elif button_data[selected_menu_num] == self.TWELVE:
|
||||
return Destination(ToolsDiceEntropyEntryView, view_args=dict(total_rolls=mnemonic_generation.DICE__NUM_ROLLS__12WORD))
|
||||
|
||||
elif button_data[selected_menu_num] == TWENTY_FOUR:
|
||||
elif button_data[selected_menu_num] == self.TWENTY_FOUR:
|
||||
return Destination(ToolsDiceEntropyEntryView, view_args=dict(total_rolls=mnemonic_generation.DICE__NUM_ROLLS__24WORD))
|
||||
|
||||
|
||||
@@ -253,13 +246,10 @@ class ToolsDiceEntropyEntryView(View):
|
||||
Calc final word Views
|
||||
****************************************************************************"""
|
||||
class ToolsCalcFinalWordNumWordsView(View):
|
||||
TWELVE = "12 words"
|
||||
TWENTY_FOUR = "24 words"
|
||||
TWELVE = _("12 words")
|
||||
TWENTY_FOUR = _("24 words")
|
||||
|
||||
def run(self):
|
||||
self.TWELVE = _("12 words")
|
||||
self.TWENTY_FOUR = _("24 words")
|
||||
|
||||
button_data = [self.TWELVE, self.TWENTY_FOUR]
|
||||
|
||||
selected_menu_num = self.run_screen(
|
||||
@@ -288,6 +278,15 @@ class ToolsCalcFinalWordNumWordsView(View):
|
||||
|
||||
|
||||
class ToolsCalcFinalWordFinalizePromptView(View):
|
||||
# TRANSLATOR_NOTE: Label to gather entropy through coin tosses
|
||||
COIN_FLIPS = _("Coin flip entropy")
|
||||
|
||||
# TRANSLATOR_NOTE: Label to gather entropy through user specified BIP-39 word
|
||||
SELECT_WORD = _("Word selection entropy")
|
||||
|
||||
# TRANSLATOR_NOTE: Label to allow user to default entropy as all-zeros
|
||||
ZEROS = _("Finalize with zeros")
|
||||
|
||||
def run(self):
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_length = len(mnemonic)
|
||||
@@ -296,14 +295,7 @@ class ToolsCalcFinalWordFinalizePromptView(View):
|
||||
else:
|
||||
num_entropy_bits = 3
|
||||
|
||||
# TRANSLATOR_NOTE: Label to gather entropy through coin tosses
|
||||
COIN_FLIPS = _("Coin flip entropy")
|
||||
# TRANSLATOR_NOTE: Label to gather entropy through user specified BIP-39 word
|
||||
SELECT_WORD = _("Word selection entropy")
|
||||
# TRANSLATOR_NOTE: Label to allow user to default entropy as all-zeros
|
||||
ZEROS = _("Finalize with zeros")
|
||||
|
||||
button_data = [COIN_FLIPS, SELECT_WORD, ZEROS]
|
||||
button_data = [self.COIN_FLIPS, self.SELECT_WORD, self.ZEROS]
|
||||
selected_menu_num = ToolsCalcFinalWordFinalizePromptScreen(
|
||||
mnemonic_length=mnemonic_length,
|
||||
num_entropy_bits=num_entropy_bits,
|
||||
@@ -313,15 +305,15 @@ class ToolsCalcFinalWordFinalizePromptView(View):
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
elif button_data[selected_menu_num] == COIN_FLIPS:
|
||||
elif button_data[selected_menu_num] == self.COIN_FLIPS:
|
||||
return Destination(ToolsCalcFinalWordCoinFlipsView)
|
||||
|
||||
elif button_data[selected_menu_num] == SELECT_WORD:
|
||||
elif button_data[selected_menu_num] == self.SELECT_WORD:
|
||||
# Clear the final word slot, just in case we're returning via BACK button
|
||||
self.controller.storage.update_pending_mnemonic(None, mnemonic_length - 1)
|
||||
return Destination(SeedMnemonicEntryView, view_args=dict(is_calc_final_word=True, cur_word_index=mnemonic_length - 1))
|
||||
|
||||
elif button_data[selected_menu_num] == ZEROS:
|
||||
elif button_data[selected_menu_num] == self.ZEROS:
|
||||
# User skipped the option to select a final word to provide last bits of
|
||||
# entropy. We'll insert all zeros and piggy-back on the coin flip attr
|
||||
wordlist_language_code = self.settings.get_value(SettingsConstants.SETTING__WORDLIST_LANGUAGE)
|
||||
@@ -352,6 +344,8 @@ class ToolsCalcFinalWordCoinFlipsView(View):
|
||||
|
||||
|
||||
class ToolsCalcFinalWordShowFinalWordView(View):
|
||||
NEXT = _("Next")
|
||||
|
||||
def __init__(self, coin_flips: str = None):
|
||||
super().__init__()
|
||||
# Construct the actual final word. The user's selected_final_word
|
||||
@@ -404,9 +398,7 @@ class ToolsCalcFinalWordShowFinalWordView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
# l10n_note already exists.
|
||||
NEXT = _("Next")
|
||||
button_data = [NEXT]
|
||||
button_data = [self.NEXT]
|
||||
selected_menu_num = self.run_screen(
|
||||
ToolsCalcFinalWordScreen,
|
||||
# TRANSLATOR_NOTE: label to calculate the last word of a BIP-39 mnemonic seed phrase
|
||||
@@ -421,20 +413,21 @@ class ToolsCalcFinalWordShowFinalWordView(View):
|
||||
if selected_menu_num == RET_CODE__BACK_BUTTON:
|
||||
return Destination(BackStackView)
|
||||
|
||||
elif button_data[selected_menu_num] == NEXT:
|
||||
elif button_data[selected_menu_num] == self.NEXT:
|
||||
return Destination(ToolsCalcFinalWordDoneView)
|
||||
|
||||
|
||||
|
||||
class ToolsCalcFinalWordDoneView(View):
|
||||
LOAD = _("Load seed")
|
||||
DISCARD = (_("Discard"), None, None, "red")
|
||||
|
||||
def run(self):
|
||||
mnemonic = self.controller.storage.pending_mnemonic
|
||||
mnemonic_word_length = len(mnemonic)
|
||||
final_word = mnemonic[-1]
|
||||
|
||||
LOAD = _("Load seed")
|
||||
DISCARD = (_("Discard"), None, None, "red")
|
||||
button_data = [LOAD, DISCARD]
|
||||
button_data = [self.LOAD, self.DISCARD]
|
||||
|
||||
selected_menu_num = ToolsCalcFinalWordDoneScreen(
|
||||
final_word=final_word,
|
||||
@@ -448,30 +441,26 @@ class ToolsCalcFinalWordDoneView(View):
|
||||
|
||||
self.controller.storage.convert_pending_mnemonic_to_pending_seed()
|
||||
|
||||
if button_data[selected_menu_num] == LOAD:
|
||||
if button_data[selected_menu_num] == self.LOAD:
|
||||
return Destination(SeedFinalizeView)
|
||||
|
||||
elif button_data[selected_menu_num] == DISCARD:
|
||||
elif button_data[selected_menu_num] == self.DISCARD:
|
||||
return Destination(SeedDiscardView)
|
||||
|
||||
|
||||
|
||||
"""****************************************************************************
|
||||
Address Explorer Views
|
||||
****************************************************************************"""
|
||||
class ToolsAddressExplorerSelectSourceView(View):
|
||||
SCAN_SEED = ("Scan a seed", SeedSignerIconConstants.QRCODE)
|
||||
SCAN_DESCRIPTOR = ("Scan wallet descriptor", SeedSignerIconConstants.QRCODE)
|
||||
TYPE_12WORD = ("Enter 12-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = ("Enter 24-word seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_ELECTRUM = ("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
|
||||
|
||||
SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
|
||||
SCAN_DESCRIPTOR = (_("Scan wallet descriptor"), SeedSignerIconConstants.QRCODE)
|
||||
TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
|
||||
def run(self):
|
||||
seeds = self.controller.storage.seeds
|
||||
self.SCAN_SEED = (_("Scan a seed"), SeedSignerIconConstants.QRCODE)
|
||||
self.SCAN_DESCRIPTOR = (_("Scan wallet descriptor"), SeedSignerIconConstants.QRCODE)
|
||||
self.TYPE_12WORD = (_("Enter 12-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
self.TYPE_24WORD = (_("Enter 24-word seed"), FontAwesomeIconConstants.KEYBOARD)
|
||||
button_data = []
|
||||
for seed in seeds:
|
||||
button_str = seed.get_fingerprint(self.settings.get_value(SettingsConstants.SETTING__NETWORK))
|
||||
@@ -529,8 +518,11 @@ class ToolsAddressExplorerSelectSourceView(View):
|
||||
|
||||
|
||||
class ToolsAddressExplorerAddressTypeView(View):
|
||||
RECEIVE = "Receive Addresses"
|
||||
CHANGE = "Change Addresses"
|
||||
# TRANSLATOR_NOTE: label for addresses where others send us incoming payments
|
||||
RECEIVE = _("Receive Addresses")
|
||||
|
||||
# TRANSLATOR_NOTE: label for addresses that collect the change from our own outgoing payments
|
||||
CHANGE = _("Change Addresses")
|
||||
|
||||
|
||||
def __init__(self, seed_num: int = None, script_type: str = None, custom_derivation: str = None):
|
||||
@@ -584,11 +576,6 @@ class ToolsAddressExplorerAddressTypeView(View):
|
||||
def run(self):
|
||||
data = self.controller.address_explorer_data
|
||||
|
||||
# TRANSLATOR_NOTE: label for addresses where others send us incoming payments
|
||||
self.RECEIVE = _("Receive Addresses")
|
||||
# TRANSLATOR_NOTE: label for addresses that collect the change from our own outgoing payments
|
||||
self.CHANGE = _("Change Addresses")
|
||||
|
||||
wallet_descriptor_display_name = None
|
||||
if "wallet_descriptor" in data:
|
||||
wallet_descriptor_display_name = data["wallet_descriptor"].brief_policy.replace(" (sorted)", "")
|
||||
|
||||
@@ -182,17 +182,14 @@ class Destination:
|
||||
#
|
||||
#########################################################################################
|
||||
class MainMenuView(View):
|
||||
SCAN = ("Scan", SeedSignerIconConstants.SCAN)
|
||||
SEEDS = ("Seeds", SeedSignerIconConstants.SEEDS)
|
||||
TOOLS = ("Tools", SeedSignerIconConstants.TOOLS)
|
||||
SETTINGS = ("Settings", SeedSignerIconConstants.SETTINGS)
|
||||
SCAN = (_("Scan"), SeedSignerIconConstants.SCAN)
|
||||
SEEDS = (_("Seeds"), SeedSignerIconConstants.SEEDS)
|
||||
TOOLS = (_("Tools"), SeedSignerIconConstants.TOOLS)
|
||||
SETTINGS = (_("Settings"), SeedSignerIconConstants.SETTINGS)
|
||||
|
||||
def run(self):
|
||||
self.SCAN = (_("Scan"), SeedSignerIconConstants.SCAN)
|
||||
self.SEEDS = (_("Seeds"), SeedSignerIconConstants.SEEDS)
|
||||
self.TOOLS = (_("Tools"), SeedSignerIconConstants.TOOLS)
|
||||
self.SETTINGS = (_("Settings"), SeedSignerIconConstants.SETTINGS)
|
||||
from seedsigner.gui.screens.screen import MainMenuScreen
|
||||
|
||||
button_data = [self.SCAN, self.SEEDS, self.TOOLS, self.SETTINGS]
|
||||
selected_menu_num = self.run_screen(
|
||||
MainMenuScreen,
|
||||
@@ -222,12 +219,10 @@ class MainMenuView(View):
|
||||
|
||||
|
||||
class PowerOptionsView(View):
|
||||
RESET = ("Restart", SeedSignerIconConstants.RESTART)
|
||||
POWER_OFF = ("Power Off", SeedSignerIconConstants.POWER)
|
||||
RESET = (_("Restart"), SeedSignerIconConstants.RESTART)
|
||||
POWER_OFF = (_("Power Off"), SeedSignerIconConstants.POWER)
|
||||
|
||||
def run(self):
|
||||
self.RESET = (_("Restart"), SeedSignerIconConstants.RESTART)
|
||||
self.POWER_OFF = (_("Power Off"), SeedSignerIconConstants.POWER)
|
||||
button_data = [self.RESET, self.POWER_OFF]
|
||||
selected_menu_num = self.run_screen(
|
||||
LargeButtonScreen,
|
||||
@@ -321,7 +316,7 @@ class NotYetImplementedView(View):
|
||||
|
||||
@dataclass
|
||||
class ErrorView(View):
|
||||
title: str = "Error"
|
||||
title: str = _("Error")
|
||||
show_back_button: bool = True
|
||||
status_headline: str = None
|
||||
text: str = None
|
||||
@@ -330,7 +325,6 @@ class ErrorView(View):
|
||||
|
||||
|
||||
def run(self):
|
||||
self.title = _("Error")
|
||||
self.run_screen(
|
||||
WarningScreen,
|
||||
title=self.title,
|
||||
@@ -346,16 +340,14 @@ class ErrorView(View):
|
||||
|
||||
@dataclass
|
||||
class NetworkMismatchErrorView(ErrorView):
|
||||
title: str = "Network Mismatch"
|
||||
title: str = _("Network Mismatch")
|
||||
show_back_button: bool = False
|
||||
button_text: str = "Change Setting"
|
||||
button_text: str = _("Change Setting")
|
||||
next_destination: Destination = None
|
||||
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
self.title = _("Network Mismatch")
|
||||
self.button_text = _("Change Setting")
|
||||
if not self.text:
|
||||
# TRANSLATOR_NOTE: Inserts mainnet/testnet/regtest
|
||||
self.text = _("Current network setting ({}) doesn't match current action.").format(
|
||||
@@ -377,6 +369,8 @@ class UnhandledExceptionView(View):
|
||||
self.run_screen(
|
||||
DireWarningScreen,
|
||||
title=_("System Error"),
|
||||
status_headline=self.error[0],
|
||||
text=self.error[1] + "\n" + self.error[2],
|
||||
allow_text_overflow=True, # Fit what we can, let the rest go off the edges
|
||||
)
|
||||
|
||||
|
||||
@@ -342,8 +342,8 @@ def test_generate_screenshots(target_locale):
|
||||
time.sleep(0.1) #jdlcdl
|
||||
|
||||
|
||||
# Parse the main `babel/messages.pot` for overall stats
|
||||
messages_source_path = os.path.join(pathlib.Path(__file__).parent.resolve().parent.resolve().parent.resolve(), "babel", "messages.pot")
|
||||
# Parse the main `l10n/messages.pot` for overall stats
|
||||
messages_source_path = os.path.join(pathlib.Path(__file__).parent.resolve().parent.resolve().parent.resolve(), "l10n", "messages.pot")
|
||||
with open(messages_source_path, 'r') as messages_source_file:
|
||||
num_source_messages = messages_source_file.read().count("msgid \"") - 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user