mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-22 14:28:07 +00:00
Merge pull request #447 from trezor/tsusanka/utils-strings
Introduce format_plural
This commit is contained in:
commit
cf73d8e499
@ -7,9 +7,9 @@ from trezor.messages import (
|
|||||||
BinanceTransferMsg,
|
BinanceTransferMsg,
|
||||||
ButtonRequestType,
|
ButtonRequestType,
|
||||||
)
|
)
|
||||||
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.scroll import Paginated
|
from trezor.ui.scroll import Paginated
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import format_amount
|
|
||||||
|
|
||||||
from . import helpers
|
from . import helpers
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
|
|
||||||
from trezor import ui
|
from trezor import ui
|
||||||
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.scroll import Paginated
|
from trezor.ui.scroll import Paginated
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import chunks, format_amount
|
from trezor.utils import chunks
|
||||||
|
|
||||||
from apps.common.confirm import confirm, hold_to_confirm
|
from apps.common.confirm import confirm, hold_to_confirm
|
||||||
|
|
||||||
|
@ -2,8 +2,9 @@ from ubinascii import hexlify
|
|||||||
|
|
||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import chunks, format_amount
|
from trezor.utils import chunks
|
||||||
|
|
||||||
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
||||||
from apps.common.layout import split_address
|
from apps.common.layout import split_address
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import chunks, format_amount
|
from trezor.utils import chunks
|
||||||
|
|
||||||
from .helpers import get_vote_tx_text
|
from .helpers import get_vote_tx_text
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import storage
|
|||||||
import storage.device
|
import storage.device
|
||||||
import storage.recovery
|
import storage.recovery
|
||||||
import storage.recovery_shares
|
import storage.recovery_shares
|
||||||
from trezor import utils, wire, workflow
|
from trezor import strings, utils, wire, workflow
|
||||||
from trezor.crypto import slip39
|
from trezor.crypto import slip39
|
||||||
from trezor.crypto.hashlib import sha256
|
from trezor.crypto.hashlib import sha256
|
||||||
from trezor.errors import MnemonicError
|
from trezor.errors import MnemonicError
|
||||||
@ -213,10 +213,7 @@ async def _request_share_next_screen(ctx: wire.GenericContext) -> None:
|
|||||||
ctx, content, "Enter", _show_remaining_groups_and_shares
|
ctx, content, "Enter", _show_remaining_groups_and_shares
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if remaining[0] == 1:
|
text = strings.format_plural("{count} more {plural}", remaining[0], "share")
|
||||||
text = "1 more share"
|
|
||||||
else:
|
|
||||||
text = "%d more shares" % remaining[0]
|
|
||||||
content = layout.RecoveryHomescreen(text, "needed to enter")
|
content = layout.RecoveryHomescreen(text, "needed to enter")
|
||||||
await layout.homescreen_dialog(ctx, content, "Enter share")
|
await layout.homescreen_dialog(ctx, content, "Enter share")
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import storage.recovery
|
import storage.recovery
|
||||||
from trezor import ui, wire
|
from trezor import strings, ui, wire
|
||||||
from trezor.crypto.slip39 import MAX_SHARE_COUNT
|
from trezor.crypto.slip39 import MAX_SHARE_COUNT
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
from trezor.messages.ButtonAck import ButtonAck
|
from trezor.messages.ButtonAck import ButtonAck
|
||||||
@ -99,10 +99,11 @@ async def show_remaining_shares(
|
|||||||
for remaining, group in groups:
|
for remaining, group in groups:
|
||||||
if 0 < remaining < MAX_SHARE_COUNT:
|
if 0 < remaining < MAX_SHARE_COUNT:
|
||||||
text = Text("Remaining Shares")
|
text = Text("Remaining Shares")
|
||||||
if remaining > 1:
|
text.bold(
|
||||||
text.bold("%s more shares starting" % remaining)
|
strings.format_plural(
|
||||||
else:
|
"{count} more {plural} starting", remaining, "share"
|
||||||
text.bold("%s more share starting" % remaining)
|
)
|
||||||
|
)
|
||||||
for word in group:
|
for word in group:
|
||||||
text.normal(word)
|
text.normal(word)
|
||||||
pages.append(text)
|
pages.append(text)
|
||||||
@ -111,10 +112,11 @@ async def show_remaining_shares(
|
|||||||
):
|
):
|
||||||
text = Text("Remaining Shares")
|
text = Text("Remaining Shares")
|
||||||
groups_remaining = group_threshold - shares_remaining.count(0)
|
groups_remaining = group_threshold - shares_remaining.count(0)
|
||||||
if groups_remaining > 1:
|
text.bold(
|
||||||
text.bold("%s more groups starting" % groups_remaining)
|
strings.format_plural(
|
||||||
elif groups_remaining > 0:
|
"{count} more {plural} starting", groups_remaining, "group"
|
||||||
text.bold("%s more group starting" % groups_remaining)
|
)
|
||||||
|
)
|
||||||
for word in group:
|
for word in group:
|
||||||
text.normal(word)
|
text.normal(word)
|
||||||
pages.append(text)
|
pages.append(text)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from trezor import ui, utils
|
from trezor import strings, ui, utils
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
from trezor.messages.ButtonAck import ButtonAck
|
from trezor.messages.ButtonAck import ButtonAck
|
||||||
from trezor.messages.ButtonRequest import ButtonRequest
|
from trezor.messages.ButtonRequest import ButtonRequest
|
||||||
@ -59,7 +59,7 @@ def paginate_lines(lines, lines_per_page=5):
|
|||||||
|
|
||||||
|
|
||||||
def format_amount(value):
|
def format_amount(value):
|
||||||
return "%s XMR" % utils.format_amount(value, 12)
|
return "%s XMR" % strings.format_amount(value, 12)
|
||||||
|
|
||||||
|
|
||||||
def split_address(address):
|
def split_address(address):
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import format_amount
|
|
||||||
|
|
||||||
from .helpers import NEM_MAX_DIVISIBILITY
|
from .helpers import NEM_MAX_DIVISIBILITY
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ from trezor.messages import (
|
|||||||
NEMTransactionCommon,
|
NEMTransactionCommon,
|
||||||
NEMTransfer,
|
NEMTransfer,
|
||||||
)
|
)
|
||||||
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import format_amount
|
|
||||||
|
|
||||||
from ..helpers import (
|
from ..helpers import (
|
||||||
NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE,
|
NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import format_amount
|
|
||||||
|
|
||||||
from . import helpers
|
from . import helpers
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from trezor import ui, utils
|
from trezor import strings, ui, utils
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ def format_amount(amount: int, ticker=True) -> str:
|
|||||||
t = ""
|
t = ""
|
||||||
if ticker:
|
if ticker:
|
||||||
t = " XLM"
|
t = " XLM"
|
||||||
return utils.format_amount(amount, consts.AMOUNT_DECIMALS) + t
|
return strings.format_amount(amount, consts.AMOUNT_DECIMALS) + t
|
||||||
|
|
||||||
|
|
||||||
def split(text):
|
def split(text):
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.messages import ButtonRequestType
|
from trezor.messages import ButtonRequestType
|
||||||
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.scroll import Paginated
|
from trezor.ui.scroll import Paginated
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from trezor.utils import chunks, format_amount
|
from trezor.utils import chunks
|
||||||
|
|
||||||
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
from apps.common.confirm import require_confirm, require_hold_to_confirm
|
||||||
from apps.tezos.helpers import TEZOS_AMOUNT_DECIMALS
|
from apps.tezos.helpers import TEZOS_AMOUNT_DECIMALS
|
||||||
|
@ -3,7 +3,8 @@ from ubinascii import hexlify
|
|||||||
|
|
||||||
from trezor import ui
|
from trezor import ui
|
||||||
from trezor.messages import ButtonRequestType, OutputScriptType
|
from trezor.messages import ButtonRequestType, OutputScriptType
|
||||||
from trezor.utils import chunks, format_amount
|
from trezor.strings import format_amount
|
||||||
|
from trezor.utils import chunks
|
||||||
|
|
||||||
_LOCKTIME_TIMESTAMP_MIN_VALUE = const(500000000)
|
_LOCKTIME_TIMESTAMP_MIN_VALUE = const(500000000)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from ustruct import unpack
|
from ustruct import unpack
|
||||||
|
|
||||||
from trezor.utils import format_amount
|
from trezor.strings import format_amount
|
||||||
|
|
||||||
currencies = {
|
currencies = {
|
||||||
1: ("OMNI", True),
|
1: ("OMNI", True),
|
||||||
|
47
core/src/trezor/strings.py
Normal file
47
core/src/trezor/strings.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
def format_amount(amount: int, decimals: int) -> str:
|
||||||
|
if amount < 0:
|
||||||
|
amount = -amount
|
||||||
|
sign = "-"
|
||||||
|
else:
|
||||||
|
sign = ""
|
||||||
|
d = pow(10, decimals)
|
||||||
|
s = (
|
||||||
|
("%s%d.%0*d" % (sign, amount // d, decimals, amount % d))
|
||||||
|
.rstrip("0")
|
||||||
|
.rstrip(".")
|
||||||
|
)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def format_ordinal(number: int) -> str:
|
||||||
|
return str(number) + {1: "st", 2: "nd", 3: "rd"}.get(
|
||||||
|
4 if 10 <= number % 100 < 20 else number % 10, "th"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def format_plural(string: str, count: int, plural: str) -> str:
|
||||||
|
"""
|
||||||
|
Adds plural form to a string based on `count`.
|
||||||
|
!! Does not work with irregular words !!
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> format_plural("We need {count} more {plural}", 3, "share")
|
||||||
|
'We need 3 more shares'
|
||||||
|
>>> format_plural("We need {count} more {plural}", 1, "share")
|
||||||
|
'We need 1 more share'
|
||||||
|
>>> format_plural("{count} {plural}", 4, "candy")
|
||||||
|
'4 candies'
|
||||||
|
"""
|
||||||
|
if not all(s in string for s in ("{count}", "{plural}")):
|
||||||
|
# string needs to have {count} and {plural} inside
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
if count == 0 or count > 1:
|
||||||
|
if plural[-1] == "y":
|
||||||
|
plural = plural[:-1] + "ies"
|
||||||
|
elif plural[-1] in "hsxz":
|
||||||
|
plural = plural + "es"
|
||||||
|
else:
|
||||||
|
plural = plural + "s"
|
||||||
|
|
||||||
|
return string.format(count=count, plural=plural)
|
@ -70,27 +70,6 @@ def chunks(items: Chunkable, size: int) -> Iterator[Chunkable]:
|
|||||||
yield items[i : i + size]
|
yield items[i : i + size]
|
||||||
|
|
||||||
|
|
||||||
def format_amount(amount: int, decimals: int) -> str:
|
|
||||||
if amount < 0:
|
|
||||||
amount = -amount
|
|
||||||
sign = "-"
|
|
||||||
else:
|
|
||||||
sign = ""
|
|
||||||
d = pow(10, decimals)
|
|
||||||
s = (
|
|
||||||
("%s%d.%0*d" % (sign, amount // d, decimals, amount % d))
|
|
||||||
.rstrip("0")
|
|
||||||
.rstrip(".")
|
|
||||||
)
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def format_ordinal(number: int) -> str:
|
|
||||||
return str(number) + {1: "st", 2: "nd", 3: "rd"}.get(
|
|
||||||
4 if 10 <= number % 100 < 20 else number % 10, "th"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
|
|
||||||
class HashContext(Protocol):
|
class HashContext(Protocol):
|
||||||
|
37
core/tests/test_trezor.strings.py
Normal file
37
core/tests/test_trezor.strings.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from common import *
|
||||||
|
|
||||||
|
from trezor import strings
|
||||||
|
|
||||||
|
|
||||||
|
class TestStrings(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_format_amount(self):
|
||||||
|
VECTORS = [
|
||||||
|
(123456, 3, "123.456"),
|
||||||
|
(4242, 7, "0.0004242"),
|
||||||
|
(-123456, 3, "-123.456"),
|
||||||
|
(-4242, 7, "-0.0004242"),
|
||||||
|
]
|
||||||
|
for v in VECTORS:
|
||||||
|
self.assertEqual(strings.format_amount(v[0], v[1]), v[2])
|
||||||
|
|
||||||
|
def test_format_plural(self):
|
||||||
|
VECTORS = [
|
||||||
|
("We need {count} more {plural}", 3, "share", "We need 3 more shares"),
|
||||||
|
("We need {count} more {plural}", 1, "share", "We need 1 more share"),
|
||||||
|
("We need {count} more {plural}", 1, "candy", "We need 1 more candy"),
|
||||||
|
("We need {count} more {plural}", 7, "candy", "We need 7 more candies"),
|
||||||
|
("We need {count} more {plural}", 1, "hash", "We need 1 more hash"),
|
||||||
|
("We need {count} more {plural}", 2, "hash", "We need 2 more hashes"),
|
||||||
|
("We need {count} more {plural}", 1, "fuzz", "We need 1 more fuzz"),
|
||||||
|
("We need {count} more {plural}", 2, "fuzz", "We need 2 more fuzzes"),
|
||||||
|
]
|
||||||
|
for v in VECTORS:
|
||||||
|
self.assertEqual(strings.format_plural(v[0], v[1], v[2]), v[3])
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
strings.format_plural("Hello", 1, "share")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -13,16 +13,6 @@ class TestUtils(unittest.TestCase):
|
|||||||
self.assertEqual(c[i].stop, 100 if (i == 14) else (i + 1) * 7)
|
self.assertEqual(c[i].stop, 100 if (i == 14) else (i + 1) * 7)
|
||||||
self.assertEqual(c[i].step, 1)
|
self.assertEqual(c[i].step, 1)
|
||||||
|
|
||||||
def test_format_amount(self):
|
|
||||||
VECTORS = [
|
|
||||||
(123456, 3, "123.456"),
|
|
||||||
(4242, 7, "0.0004242"),
|
|
||||||
(-123456, 3, "-123.456"),
|
|
||||||
(-4242, 7, "-0.0004242"),
|
|
||||||
]
|
|
||||||
for v in VECTORS:
|
|
||||||
self.assertEqual(utils.format_amount(v[0], v[1]), v[2])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user