diff --git a/Makefile b/Makefile index fe99a3b7d..8f5cb4163 100644 --- a/Makefile +++ b/Makefile @@ -144,6 +144,6 @@ vendorheader: ## generate vendor header vendorheader_check: ## check that vendor header is up to date ./core/embed/vendorheader/generate.sh --quiet --check -gen: mocks icons templates protobuf ci_docs vendorheader solana_templates ## regenerate auto-generated files from sources +gen: templates mocks icons protobuf ci_docs vendorheader solana ## regenerate auto-generated files from sources -gen_check: mocks_check icons_check templates_check protobuf_check ci_docs_check vendorheader_check solana_templates_check ## check validity of auto-generated files +gen_check: templates_check mocks_check icons_check protobuf_check ci_docs_check vendorheader_check solana_templates_check ## check validity of auto-generated files diff --git a/ci/shell.nix b/ci/shell.nix index 5e438b381..434aad5ed 100644 --- a/ci/shell.nix +++ b/ci/shell.nix @@ -97,6 +97,7 @@ stdenvNoCC.mkDerivation ({ bash bloaty # for binsize check + crowdin-cli # for translations curl # for connect tests editorconfig-checker gcc-arm-embedded diff --git a/ci/test.yml b/ci/test.yml index ca7d1afd2..4d8a5ac3e 100644 --- a/ci/test.yml +++ b/ci/test.yml @@ -82,6 +82,70 @@ core device test: reports: junit: tests/junit.xml +core device test czech: + stage: test + <<: *gitlab_caching + needs: + - core unix frozen debug build + variables: + TREZOR_PROFILING: 1 # so that we get coverage data + MULTICORE: 4 # more could interfere with other jobs + TEST_LANG: "cs" # czech + script: + - $NIX_SHELL --run "poetry run make -C core test_emu_ui_multicore | ts -s" + after_script: + - mv core/src/.coverage.* core # there will be more coverage files (one per core) + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json + - tests/ui_tests/fixtures.results.json + - tests/junit.xml + - tests/trezor.log + - core/.coverage.* + when: always + expire_in: 1 week + reports: + junit: tests/junit.xml + +core device test french: + stage: test + <<: *gitlab_caching + needs: + - core unix frozen debug build + variables: + TREZOR_PROFILING: 1 # so that we get coverage data + MULTICORE: 4 # more could interfere with other jobs + TEST_LANG: "fr" # french + script: + - $NIX_SHELL --run "poetry run make -C core test_emu_ui_multicore | ts -s" + after_script: + - mv core/src/.coverage.* core # there will be more coverage files (one per core) + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json + - tests/ui_tests/fixtures.results.json + - tests/junit.xml + - tests/trezor.log + - core/.coverage.* + when: always + expire_in: 1 week + reports: + junit: tests/junit.xml + core device R test: stage: test <<: *gitlab_caching @@ -114,6 +178,72 @@ core device R test: reports: junit: tests/junit.xml +core device R test czech: + stage: test + <<: *gitlab_caching + needs: + - core unix frozen R debug build + variables: + TREZOR_PROFILING: "1" + TREZOR_MODEL: "R" + MULTICORE: 4 # more could interfere with other jobs + TEST_LANG: "cs" # czech + script: + - $NIX_SHELL --run "poetry run make -C core test_emu_ui_multicore | ts -s" + after_script: + - mv core/src/.coverage.* core # there will be more coverage files (one per core) + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json + - tests/ui_tests/fixtures.results.json + - tests/junit.xml + - tests/trezor.log + - core/.coverage.* + when: always + expire_in: 1 week + reports: + junit: tests/junit.xml + +core device R test french: + stage: test + <<: *gitlab_caching + needs: + - core unix frozen R debug build + variables: + TREZOR_PROFILING: "1" + TREZOR_MODEL: "R" + MULTICORE: 4 # more could interfere with other jobs + TEST_LANG: "fr" # french + script: + - $NIX_SHELL --run "poetry run make -C core test_emu_ui_multicore | ts -s" + after_script: + - mv core/src/.coverage.* core # there will be more coverage files (one per core) + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json + - tests/ui_tests/fixtures.results.json + - tests/junit.xml + - tests/trezor.log + - core/.coverage.* + when: always + expire_in: 1 week + reports: + junit: tests/junit.xml + core device asan test: stage: test <<: *gitlab_caching @@ -327,6 +457,68 @@ core click test: expire_in: 1 week when: always +core click test czech: + stage: test + <<: *gitlab_caching + needs: + - core unix frozen debug build + variables: + TREZOR_PROFILING: 1 + TEST_LANG: "cs" # czech + script: + - $NIX_SHELL --run "poetry run make -C core test_emu_click_ui | ts -s" + after_script: + - mv core/src/.coverage core/.coverage.test_click + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json + - tests/ui_tests/fixtures.results.json + - tests/trezor.log + - tests/junit.xml + - core/.coverage.* + reports: + junit: tests/junit.xml + expire_in: 1 week + when: always + +core click test french: + stage: test + <<: *gitlab_caching + needs: + - core unix frozen debug build + variables: + TREZOR_PROFILING: 1 + TEST_LANG: "fr" # french + script: + - $NIX_SHELL --run "poetry run make -C core test_emu_click_ui | ts -s" + after_script: + - mv core/src/.coverage core/.coverage.test_click + - mv tests/ui_tests/reports/test/ test_ui_report + - $NIX_SHELL --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json + - tests/ui_tests/fixtures.results.json + - tests/trezor.log + - tests/junit.xml + - core/.coverage.* + reports: + junit: tests/junit.xml + expire_in: 1 week + when: always + # Click tests. # See [docs/tests/click-tests](../tests/click-tests.md) for more info. core click R test: @@ -358,6 +550,66 @@ core click R test: expire_in: 1 week when: always +core click R test czech: + stage: test + <<: *gitlab_caching + needs: + - core unix frozen R debug build + variables: + TREZOR_PROFILING: 1 + TEST_LANG: "cs" # czech + script: + - nix-shell --run "poetry run make -C core test_emu_click_ui | ts -s" + after_script: + - mv core/src/.coverage core/.coverage.test_click + - mv tests/ui_tests/reports/test/ test_ui_report + - nix-shell --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json + - tests/ui_tests/fixtures.results.json + - tests/trezor.log + - tests/junit.xml + reports: + junit: tests/junit.xml + expire_in: 1 week + when: always + +core click R test french: + stage: test + <<: *gitlab_caching + needs: + - core unix frozen R debug build + variables: + TREZOR_PROFILING: 1 + TEST_LANG: "fr" # french + script: + - nix-shell --run "poetry run make -C core test_emu_click_ui | ts -s" + after_script: + - mv core/src/.coverage core/.coverage.test_click + - mv tests/ui_tests/reports/test/ test_ui_report + - nix-shell --run "poetry run python ci/prepare_ui_artifacts.py | ts -s" + - diff -u tests/ui_tests/fixtures.json tests/ui_tests/fixtures.suggestion.json + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - ci/ui_test_records/ + - test_ui_report + - tests/ui_tests/screens/ + - tests/ui_tests/fixtures.suggestion.json + - tests/ui_tests/fixtures.results.json + - tests/trezor.log + - tests/junit.xml + reports: + junit: tests/junit.xml + expire_in: 1 week + when: always + core click asan test: stage: test <<: *gitlab_caching diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index 2394f2efc..1a14d7036 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -161,7 +161,7 @@ message EndSession { } /** - * Request: change language and/or label of the device + * Request: change some property of the device, e.g. label or homescreen * @start * @next Success * @next Failure @@ -180,6 +180,37 @@ message ApplySettings { optional bool hide_passphrase_from_host = 11; // do not show passphrase coming from host } +/** + * Request: change the device language via translation data + * Does not send the translation data itself, as they are too large for one message. + * Device will request the translation data in chunks. + * @start + * @next TranslationDataRequest + * @next Failure + */ +message ChangeLanguage { + required uint32 data_length = 1; // byte length of the whole translation blob (set to 0 for default language) +} + +/** + * Response: Device asks for more data from transaction payload. + * @end + * @next TranslationDataAck + */ + message TranslationDataRequest { + required uint32 data_length = 1; // Number of bytes being requested (<= 1024) + required uint32 data_offset = 2; // Offset of the first byte being requested +} + +/** + * Request: Translation payload data. + * @next TranslationDataRequest + * @next Success + */ +message TranslationDataAck { + required bytes data_chunk = 1; // Bytes from translation payload (<= 1024 bytes) +} + /** * Request: set flags of the device * @start diff --git a/common/protob/messages.proto b/common/protob/messages.proto index e54e7ed6a..97203f8db 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -124,6 +124,9 @@ enum MessageType { MessageType_UnlockBootloader = 96 [(bitcoin_only) = true, (wire_in) = true]; MessageType_AuthenticateDevice = 97 [(bitcoin_only) = true, (wire_out) = true]; MessageType_AuthenticityProof = 98 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_ChangeLanguage = 990 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_TranslationDataRequest = 991 [(bitcoin_only) = true, (wire_out) = true]; + MessageType_TranslationDataAck = 992 [(bitcoin_only) = true, (wire_in) = true]; MessageType_SetU2FCounter = 63 [(wire_in) = true]; MessageType_GetNextU2FCounter = 80 [(wire_in) = true]; diff --git a/common/tools/cointool.py b/common/tools/cointool.py index 8eb8fc1e7..2f4a770eb 100755 --- a/common/tools/cointool.py +++ b/common/tools/cointool.py @@ -116,6 +116,10 @@ def ascii_filter(s: str) -> str: return re.sub("[^ -\x7e]", "_", s) +def utf8_str_filter(s: str) -> str: + return '"' + repr(s)[1:-1] + '"' + + def make_support_filter( support_info: SupportInfo, ) -> Callable[[str, Coins], Iterator[Coin]]: @@ -126,6 +130,7 @@ def make_support_filter( MAKO_FILTERS = { + "utf8_str": utf8_str_filter, "c_str": c_str_filter, "ascii": ascii_filter, "black_repr": black_repr_filter, diff --git a/core/Makefile b/core/Makefile index a2742ea37..764e481d2 100644 --- a/core/Makefile +++ b/core/Makefile @@ -29,6 +29,7 @@ TREZOR_MEMPERF ?= 0 ADDRESS_SANITIZER ?= 0 CMAKELISTS ?= 0 PYTEST_TIMEOUT ?= 400 +TEST_LANG ?= "en" # OpenOCD interface default. Alternative: ftdi/olimex-arm-usb-tiny-h OPENOCD_INTERFACE ?= stlink @@ -95,11 +96,12 @@ test_rust: ## run rs unit tests -- --test-threads=1 --nocapture test_emu: ## run selected device tests from python-trezor - $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS) + $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS) --lang=$(TEST_LANG) test_emu_multicore: ## run device tests using multiple cores $(PYTEST) -n $(MULTICORE) $(TESTPATH)/device_tests $(TESTOPTS) --timeout $(PYTEST_TIMEOUT) \ - --control-emulators --model=core --random-order-seed=$(shell echo $$RANDOM) + --control-emulators --model=core --random-order-seed=$(shell echo $$RANDOM) \ + --lang=$(TEST_LANG) test_emu_monero: ## run selected monero device tests from monero-agent cd tests ; $(EMU_TEST) ./run_tests_device_emu_monero.sh $(TESTOPTS) @@ -113,31 +115,33 @@ test_emu_fido2: ## run fido2 device tests $(EMU_TEST) --slip0014 $(PYTEST) --maxfail=5 --sim tests/standard/ --vendor trezor $(TESTOPTS) test_emu_click: ## run click tests - $(EMU_TEST) $(PYTEST) $(TESTPATH)/click_tests $(TESTOPTS) + $(EMU_TEST) $(PYTEST) $(TESTPATH)/click_tests $(TESTOPTS) --lang=$(TEST_LANG) test_emu_click_ui: ## run click tests with UI testing $(EMU_TEST) $(PYTEST) $(TESTPATH)/click_tests $(TESTOPTS) \ - --ui=test --ui-check-missing --do-master-diff + --ui=test --ui-check-missing --do-master-diff --lang=$(TEST_LANG) test_emu_persistence: ## run persistence tests - $(PYTEST) $(TESTPATH)/persistence_tests $(TESTOPTS) + $(PYTEST) $(TESTPATH)/persistence_tests $(TESTOPTS) --lang=$(TEST_LANG) test_emu_persistence_ui: ## run persistence tests with UI testing $(PYTEST) $(TESTPATH)/persistence_tests $(TESTOPTS) \ - --ui=test --ui-check-missing --do-master-diff + --ui=test --ui-check-missing --do-master-diff --lang=$(TEST_LANG) test_emu_ui: ## run ui integration tests $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS) \ - --ui=test --ui-check-missing --record-text-layout --do-master-diff + --ui=test --ui-check-missing --record-text-layout --do-master-diff \ + --lang=$(TEST_LANG) test_emu_ui_multicore: ## run ui integration tests using multiple cores $(PYTEST) -n $(MULTICORE) $(TESTPATH)/device_tests $(TESTOPTS) --timeout $(PYTEST_TIMEOUT) \ --ui=test --ui-check-missing --record-text-layout --do-master-diff \ - --control-emulators --model=core --random-order-seed=$(shell echo $$RANDOM) + --control-emulators --model=core --random-order-seed=$(shell echo $$RANDOM) \ + --lang=$(TEST_LANG) test_emu_ui_record: ## record and hash screens for ui integration tests $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS) \ - --ui=record --ui-check-missing --do-master-diff + --ui=record --ui-check-missing --do-master-diff --lang=$(TEST_LANG) test_emu_ui_record_multicore: ## quickly record all screens make test_emu_ui_multicore || echo "All errors are recorded in fixtures.json" diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 168d47865..541ba7f1d 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -202,6 +202,7 @@ SOURCE_MOD += [ CPPDEFINES_MOD += [ 'TREZOR_UI2', + 'TRANSLATIONS', ] if TREZOR_MODEL not in ('1', ): @@ -713,6 +714,7 @@ def cargo_build(): if BITCOIN_ONLY == '1': features.append('bitcoin_only') features.append('ui') + features.append('translations') if PYOPT == '0': features.append('ui_debug') diff --git a/core/SConscript.unix b/core/SConscript.unix index a1dda73a9..85caf8aaa 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -217,6 +217,7 @@ elif TREZOR_MODEL in ('R', ): CPPDEFINES_MOD += [ 'TREZOR_UI2', + 'TRANSLATIONS', ] if TREZOR_MODEL not in ('1', ): CPPDEFINES_MOD += [ @@ -370,6 +371,7 @@ SOURCE_MICROPYTHON = [ ] SOURCE_UNIX = [ + 'embed/trezorhal/unix/translations.c', 'embed/trezorhal/unix/common.c', 'embed/trezorhal/unix/display-unix.c', 'embed/trezorhal/unix/flash.c', @@ -791,6 +793,7 @@ def cargo_build(): if BITCOIN_ONLY == '1': features.append('bitcoin_only') features.append('ui') + features.append('translations') if PYOPT == '0': features.append('debug') if DMA2D: diff --git a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c index 1078cd923..02bcbe8d5 100644 --- a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -17,6 +17,7 @@ * along with this program. If not, see . */ +#include #include #include "py/mphal.h" @@ -26,10 +27,12 @@ #if MICROPY_PY_TREZORCONFIG #include "embed/extmod/trezorobj.h" +#include "embed/trezorhal/translations.h" #include "common.h" #include "memzero.h" #include "storage.h" +#include "translations.h" static secbool wrapped_ui_wait_callback(uint32_t wait, uint32_t progress, const char *message) { @@ -311,6 +314,52 @@ STATIC mp_obj_t mod_trezorconfig_set(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_set_obj, 3, 4, mod_trezorconfig_set); +/// def translations_set(blob: bytes, offset: int) -> None: +/// """ +/// Save translations data at the certain offset. +/// """ +STATIC mp_obj_t mod_trezorconfig_translations_set(mp_obj_t blob, + mp_obj_t offset) { + mp_buffer_info_t blob_b = {0}; + blob_b.buf = NULL; + if (blob != mp_const_none) { + mp_get_buffer_raise(blob, &blob_b, MP_BUFFER_READ); + } + if (blob_b.len > translations_area_bytesize()) { + mp_raise_msg(&mp_type_RuntimeError, "Translations data blob too big"); + } + uint32_t off = trezor_obj_get_uint(offset); + if (off + blob_b.len > translations_area_bytesize()) { + mp_raise_msg(&mp_type_RuntimeError, "Translations data cannot fit flash"); + } + translations_write(blob_b.buf, off, blob_b.len); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorconfig_translations_set_obj, + mod_trezorconfig_translations_set); + +/// def translations_wipe() -> None: +/// """ +/// Wipe all the translations data before writing a new one +/// """ +STATIC mp_obj_t mod_trezorconfig_translations_wipe(void) { + translations_erase(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_translations_wipe_obj, + mod_trezorconfig_translations_wipe); + +/// def translations_max_bytesize() -> int: +/// """ +/// How much is the maximum bytesize of translations data +/// """ +STATIC mp_obj_t mod_trezorconfig_translations_max_bytesize(void) { + uint32_t count = translations_area_bytesize(); + return mp_obj_new_int_from_uint(count); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_translations_max_bytesize_obj, + mod_trezorconfig_translations_max_bytesize); + /// def delete( /// app: int, key: int, public: bool = False, writable_locked: bool = False /// ) -> bool: @@ -431,6 +480,12 @@ STATIC const mp_rom_map_elem_t mp_module_trezorconfig_globals_table[] = { MP_ROM_PTR(&mod_trezorconfig_change_wipe_code_obj)}, {MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&mod_trezorconfig_get_obj)}, {MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&mod_trezorconfig_set_obj)}, + {MP_ROM_QSTR(MP_QSTR_translations_set), + MP_ROM_PTR(&mod_trezorconfig_translations_set_obj)}, + {MP_ROM_QSTR(MP_QSTR_translations_wipe), + MP_ROM_PTR(&mod_trezorconfig_translations_wipe_obj)}, + {MP_ROM_QSTR(MP_QSTR_translations_max_bytesize), + MP_ROM_PTR(&mod_trezorconfig_translations_max_bytesize_obj)}, {MP_ROM_QSTR(MP_QSTR_delete), MP_ROM_PTR(&mod_trezorconfig_delete_obj)}, {MP_ROM_QSTR(MP_QSTR_set_counter), MP_ROM_PTR(&mod_trezorconfig_set_counter_obj)}, diff --git a/core/embed/extmod/rustmods.c b/core/embed/extmod/rustmods.c index 438c383f1..2673bceb5 100644 --- a/core/embed/extmod/rustmods.c +++ b/core/embed/extmod/rustmods.c @@ -18,6 +18,7 @@ */ #include "librust.h" +#include "librust_fonts.h" #include "py/runtime.h" #if MICROPY_PY_TREZORUI2 @@ -27,3 +28,7 @@ MP_REGISTER_MODULE(MP_QSTR_trezorui2, mp_module_trezorui2); #if MICROPY_PY_TREZORPROTO MP_REGISTER_MODULE(MP_QSTR_trezorproto, mp_module_trezorproto); #endif + +#if MICROPY_PY_TREZORTRANSLATE +MP_REGISTER_MODULE(MP_QSTR_trezortranslate, mp_module_trezortranslate); +#endif diff --git a/core/embed/firmware/mpconfigport.h b/core/embed/firmware/mpconfigport.h index 56a1c056b..b80f257c6 100644 --- a/core/embed/firmware/mpconfigport.h +++ b/core/embed/firmware/mpconfigport.h @@ -163,6 +163,7 @@ #define MICROPY_PY_TREZORUI (1) #define MICROPY_PY_TREZORUTILS (1) #define MICROPY_PY_TREZORPROTO (1) +#define MICROPY_PY_TREZORTRANSLATE (1) #define MICROPY_PY_TREZORUI2 (1) #ifdef SYSTEM_VIEW diff --git a/core/embed/lib/fonts/font_pixeloperator_bold_8.c b/core/embed/lib/fonts/font_pixeloperator_bold_8.c index 57590fc0e..5bdd5e403 100644 --- a/core/embed/lib/fonts/font_pixeloperator_bold_8.c +++ b/core/embed/lib/fonts/font_pixeloperator_bold_8.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_PixelOperator_Bold_8_glyph_125[] = { 5, 7, 6, 0, 7, 225, 140, 51, 27, 128 }; /* ~ */ static const uint8_t Font_PixelOperator_Bold_8_glyph_126[] = { 7, 2, 8, 0, 7, 119, 184 }; -const uint8_t Font_PixelOperator_Bold_8_glyph_nonprintable[] = { 6, 7, 7, 0, 7, 132, 207, 57, 207, 252, 255 }; +/* ? */ const uint8_t Font_PixelOperator_Bold_8_glyph_nonprintable[] = { 6, 7, 7, 0, 7, 132, 207, 57, 207, 252, 255 }; const uint8_t * const Font_PixelOperator_Bold_8[126 + 1 - 32] = { Font_PixelOperator_Bold_8_glyph_32, diff --git a/core/embed/lib/fonts/font_pixeloperator_regular_8.c b/core/embed/lib/fonts/font_pixeloperator_regular_8.c index 955928f18..ba7a412dd 100644 --- a/core/embed/lib/fonts/font_pixeloperator_regular_8.c +++ b/core/embed/lib/fonts/font_pixeloperator_regular_8.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_PixelOperator_Regular_8_glyph_125[] = { 4, 7, 6, 0, 7, 194, 33, 34, 192 }; /* ~ */ static const uint8_t Font_PixelOperator_Regular_8_glyph_126[] = { 6, 2, 7, 0, 7, 102, 96 }; -const uint8_t Font_PixelOperator_Regular_8_glyph_nonprintable[] = { 5, 7, 6, 0, 7, 139, 189, 221, 255, 127 }; +/* ? */ const uint8_t Font_PixelOperator_Regular_8_glyph_nonprintable[] = { 5, 7, 6, 0, 7, 139, 189, 221, 255, 127 }; const uint8_t * const Font_PixelOperator_Regular_8[126 + 1 - 32] = { Font_PixelOperator_Regular_8_glyph_32, diff --git a/core/embed/lib/fonts/font_pixeloperatormono_regular_8.c b/core/embed/lib/fonts/font_pixeloperatormono_regular_8.c index 41459a8fc..ab12ebffd 100644 --- a/core/embed/lib/fonts/font_pixeloperatormono_regular_8.c +++ b/core/embed/lib/fonts/font_pixeloperatormono_regular_8.c @@ -105,7 +105,7 @@ /* } */ static const uint8_t Font_PixelOperatorMono_Regular_8_glyph_125[] = { 4, 7, 7, 0, 7, 194, 33, 34, 192 }; /* ~ */ static const uint8_t Font_PixelOperatorMono_Regular_8_glyph_126[] = { 6, 2, 7, 0, 7, 102, 96 }; -const uint8_t Font_PixelOperatorMono_Regular_8_glyph_nonprintable[] = { 5, 7, 7, 0, 7, 139, 189, 221, 255, 127 }; +/* ? */ const uint8_t Font_PixelOperatorMono_Regular_8_glyph_nonprintable[] = { 5, 7, 7, 0, 7, 139, 189, 221, 255, 127 }; const uint8_t * const Font_PixelOperatorMono_Regular_8[126 + 1 - 32] = { Font_PixelOperatorMono_Regular_8_glyph_32, diff --git a/core/embed/lib/fonts/font_roboto_bold_20.c b/core/embed/lib/fonts/font_roboto_bold_20.c index c2f97d7eb..9f3729ce5 100644 --- a/core/embed/lib/fonts/font_roboto_bold_20.c +++ b/core/embed/lib/fonts/font_roboto_bold_20.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_Roboto_Bold_20_glyph_125[] = { 7, 20, 7, 0, 16, 75, 48, 0, 8, 255, 64, 0, 11, 254, 0, 0, 79, 243, 0, 2, 255, 80, 0, 47, 246, 0, 2, 255, 96, 0, 31, 247, 0, 0, 207, 210, 0, 2, 223, 242, 0, 45, 255, 32, 12, 253, 32, 1, 255, 112, 0, 47, 246, 0, 2, 255, 96, 0, 47, 245, 0, 4, 255, 48, 0, 191, 224, 0, 143, 244, 0, 4, 179, 0, 0 }; /* ~ */ static const uint8_t Font_Roboto_Bold_20_glyph_126[] = { 11, 5, 13, 1, 8, 6, 239, 195, 0, 42, 149, 255, 255, 246, 9, 252, 207, 198, 207, 255, 255, 109, 227, 0, 143, 255, 160, 0, 0, 0, 20, 32, 0 }; -const uint8_t Font_Roboto_Bold_20_glyph_nonprintable[] = { 10, 14, 10, 0, 14, 255, 130, 1, 58, 255, 244, 0, 0, 0, 127, 176, 1, 152, 0, 15, 128, 10, 255, 80, 12, 255, 255, 255, 64, 13, 255, 255, 250, 0, 63, 255, 255, 160, 1, 223, 255, 254, 0, 29, 255, 255, 248, 0, 191, 255, 255, 246, 1, 255, 255, 255, 255, 255, 255, 255, 255, 249, 20, 255, 255, 255, 242, 0, 207, 255, 255, 248, 4, 255, 255 }; +/* ? */ const uint8_t Font_Roboto_Bold_20_glyph_nonprintable[] = { 10, 14, 10, 0, 14, 255, 130, 1, 58, 255, 244, 0, 0, 0, 127, 176, 1, 152, 0, 15, 128, 10, 255, 80, 12, 255, 255, 255, 64, 13, 255, 255, 250, 0, 63, 255, 255, 160, 1, 223, 255, 254, 0, 29, 255, 255, 248, 0, 191, 255, 255, 246, 1, 255, 255, 255, 255, 255, 255, 255, 255, 249, 20, 255, 255, 255, 242, 0, 207, 255, 255, 248, 4, 255, 255 }; const uint8_t * const Font_Roboto_Bold_20[126 + 1 - 32] = { Font_Roboto_Bold_20_glyph_32, diff --git a/core/embed/lib/fonts/font_roboto_regular_20.c b/core/embed/lib/fonts/font_roboto_regular_20.c index 74a2fa9ff..ded02f87a 100644 --- a/core/embed/lib/fonts/font_roboto_regular_20.c +++ b/core/embed/lib/fonts/font_roboto_regular_20.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_Roboto_Regular_20_glyph_125[] = { 7, 20, 7, 0, 16, 137, 16, 0, 4, 237, 16, 0, 3, 249, 0, 0, 14, 224, 0, 0, 207, 0, 0, 12, 240, 0, 0, 207, 0, 0, 10, 242, 0, 0, 79, 177, 0, 0, 127, 241, 0, 29, 248, 0, 8, 245, 0, 0, 191, 16, 0, 12, 240, 0, 0, 207, 0, 0, 12, 240, 0, 0, 237, 0, 0, 95, 128, 0, 94, 209, 0, 8, 145, 0, 0 }; /* ~ */ static const uint8_t Font_Roboto_Regular_20_glyph_126[] = { 12, 4, 14, 1, 8, 4, 223, 214, 0, 2, 179, 63, 218, 239, 178, 26, 241, 159, 16, 9, 255, 255, 112, 35, 0, 0, 55, 131, 0 }; -const uint8_t Font_Roboto_Regular_20_glyph_nonprintable[] = { 9, 14, 9, 0, 14, 255, 147, 1, 93, 255, 96, 21, 64, 30, 224, 47, 255, 128, 125, 122, 255, 252, 5, 255, 255, 255, 160, 143, 255, 255, 225, 14, 255, 255, 226, 10, 255, 255, 243, 10, 255, 255, 251, 6, 255, 255, 255, 128, 175, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 10, 255, 255, 255, 144, 175, 255 }; +/* ? */ const uint8_t Font_Roboto_Regular_20_glyph_nonprintable[] = { 9, 14, 9, 0, 14, 255, 147, 1, 93, 255, 96, 21, 64, 30, 224, 47, 255, 128, 125, 122, 255, 252, 5, 255, 255, 255, 160, 143, 255, 255, 225, 14, 255, 255, 226, 10, 255, 255, 243, 10, 255, 255, 251, 6, 255, 255, 255, 128, 175, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 249, 10, 255, 255, 255, 144, 175, 255 }; const uint8_t * const Font_Roboto_Regular_20[126 + 1 - 32] = { Font_Roboto_Regular_20_glyph_32, diff --git a/core/embed/lib/fonts/font_robotomono_medium_20.c b/core/embed/lib/fonts/font_robotomono_medium_20.c index cd7837992..685e0e85f 100644 --- a/core/embed/lib/fonts/font_robotomono_medium_20.c +++ b/core/embed/lib/fonts/font_robotomono_medium_20.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_RobotoMono_Medium_20_glyph_125[] = { 7, 20, 12, 3, 16, 87, 16, 0, 9, 254, 16, 0, 7, 250, 0, 0, 31, 240, 0, 0, 255, 32, 0, 15, 243, 0, 0, 255, 48, 0, 14, 244, 0, 0, 143, 193, 0, 0, 159, 251, 0, 9, 255, 160, 8, 252, 16, 0, 239, 64, 0, 15, 243, 0, 0, 255, 48, 0, 15, 242, 0, 2, 255, 0, 0, 159, 144, 0, 175, 209, 0, 4, 96, 0, 0 }; /* ~ */ static const uint8_t Font_RobotoMono_Medium_20_glyph_126[] = { 12, 5, 12, 0, 8, 4, 222, 196, 0, 2, 97, 47, 255, 255, 128, 9, 243, 143, 96, 94, 254, 223, 224, 89, 0, 2, 191, 254, 48, 0, 0, 0, 1, 32, 0 }; -const uint8_t Font_RobotoMono_Medium_20_glyph_nonprintable[] = { 10, 16, 12, 1, 15, 255, 252, 153, 191, 255, 254, 48, 0, 1, 175, 242, 0, 102, 32, 13, 192, 11, 255, 224, 8, 234, 175, 255, 242, 7, 255, 255, 255, 208, 10, 255, 255, 255, 48, 47, 255, 255, 244, 1, 223, 255, 255, 80, 29, 255, 255, 255, 0, 191, 255, 255, 255, 34, 239, 255, 255, 255, 255, 255, 255, 255, 255, 153, 255, 255, 255, 250, 0, 175, 255, 255, 252, 0, 223, 255, 255, 255, 255, 255, 255 }; +/* ? */ const uint8_t Font_RobotoMono_Medium_20_glyph_nonprintable[] = { 10, 16, 12, 1, 15, 255, 252, 153, 191, 255, 254, 48, 0, 1, 175, 242, 0, 102, 32, 13, 192, 11, 255, 224, 8, 234, 175, 255, 242, 7, 255, 255, 255, 208, 10, 255, 255, 255, 48, 47, 255, 255, 244, 1, 223, 255, 255, 80, 29, 255, 255, 255, 0, 191, 255, 255, 255, 34, 239, 255, 255, 255, 255, 255, 255, 255, 255, 153, 255, 255, 255, 250, 0, 175, 255, 255, 252, 0, 223, 255, 255, 255, 255, 255, 255 }; const uint8_t * const Font_RobotoMono_Medium_20[126 + 1 - 32] = { Font_RobotoMono_Medium_20_glyph_32, diff --git a/core/embed/lib/fonts/font_tthoves_bold_17.c b/core/embed/lib/fonts/font_tthoves_bold_17.c index cf04c0a59..dd91e700b 100644 --- a/core/embed/lib/fonts/font_tthoves_bold_17.c +++ b/core/embed/lib/fonts/font_tthoves_bold_17.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_TTHoves_Bold_17_glyph_125[] = { 7, 17, 7, 0, 13, 20, 48, 0, 6, 255, 209, 0, 111, 255, 160, 0, 30, 253, 0, 0, 191, 208, 0, 11, 253, 0, 0, 191, 224, 0, 8, 255, 192, 0, 8, 255, 0, 2, 223, 240, 0, 159, 247, 0, 11, 254, 0, 0, 191, 208, 0, 11, 253, 0, 21, 255, 192, 6, 255, 248, 0, 111, 233, 0, 0 }; /* ~ */ static const uint8_t Font_TTHoves_Bold_17_glyph_126[] = { 10, 4, 10, 0, 7, 0, 38, 32, 0, 0, 8, 255, 249, 111, 248, 47, 255, 255, 255, 244, 95, 243, 75, 254, 112 }; -const uint8_t Font_TTHoves_Bold_17_glyph_nonprintable[] = { 9, 12, 9, 0, 12, 255, 130, 2, 110, 255, 48, 0, 0, 30, 160, 2, 114, 0, 138, 85, 223, 192, 6, 255, 255, 245, 0, 159, 255, 210, 0, 111, 255, 245, 0, 175, 255, 255, 32, 79, 255, 255, 254, 238, 255, 255, 255, 51, 111, 255, 255, 240, 3, 255, 255, 255, 0, 63, 255 }; +/* ? */ const uint8_t Font_TTHoves_Bold_17_glyph_nonprintable[] = { 9, 12, 9, 0, 12, 255, 130, 2, 110, 255, 48, 0, 0, 30, 160, 2, 114, 0, 138, 85, 223, 192, 6, 255, 255, 245, 0, 159, 255, 210, 0, 111, 255, 245, 0, 175, 255, 255, 32, 79, 255, 255, 254, 238, 255, 255, 255, 51, 111, 255, 255, 240, 3, 255, 255, 255, 0, 63, 255 }; const uint8_t * const Font_TTHoves_Bold_17[126 + 1 - 32] = { Font_TTHoves_Bold_17_glyph_32, diff --git a/core/embed/lib/fonts/font_tthoves_demibold_21.c b/core/embed/lib/fonts/font_tthoves_demibold_21.c index cb0256ec8..023cb2e7e 100644 --- a/core/embed/lib/fonts/font_tthoves_demibold_21.c +++ b/core/embed/lib/fonts/font_tthoves_demibold_21.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_TTHoves_DemiBold_21_glyph_125[] = { 7, 20, 8, 0, 16, 22, 82, 0, 4, 255, 248, 0, 79, 255, 242, 0, 8, 255, 64, 0, 79, 245, 0, 4, 255, 80, 0, 79, 245, 0, 4, 255, 80, 0, 47, 250, 16, 0, 175, 252, 0, 0, 159, 192, 0, 223, 250, 0, 63, 248, 0, 4, 255, 80, 0, 79, 245, 0, 4, 255, 80, 0, 95, 245, 1, 108, 255, 64, 79, 255, 224, 4, 255, 195, 0 }; /* ~ */ static const uint8_t Font_TTHoves_DemiBold_21_glyph_126[] = { 12, 5, 12, 0, 8, 0, 125, 233, 16, 9, 148, 7, 255, 255, 211, 95, 246, 14, 255, 239, 255, 255, 242, 31, 248, 4, 239, 255, 144, 4, 82, 0, 23, 132, 0 }; -const uint8_t Font_TTHoves_DemiBold_21_glyph_nonprintable[] = { 11, 15, 11, 0, 15, 255, 213, 32, 38, 239, 255, 160, 0, 0, 0, 207, 225, 0, 70, 32, 1, 251, 0, 95, 255, 48, 12, 217, 157, 255, 247, 0, 175, 255, 255, 254, 16, 13, 255, 255, 249, 0, 5, 255, 255, 249, 0, 8, 255, 255, 255, 32, 11, 255, 255, 255, 240, 4, 255, 255, 255, 255, 85, 159, 255, 255, 255, 255, 255, 255, 255, 255, 254, 17, 95, 255, 255, 255, 224, 4, 255, 255, 255, 254, 0, 79, 255, 255 }; +/* ? */ const uint8_t Font_TTHoves_DemiBold_21_glyph_nonprintable[] = { 11, 15, 11, 0, 15, 255, 213, 32, 38, 239, 255, 160, 0, 0, 0, 207, 225, 0, 70, 32, 1, 251, 0, 95, 255, 48, 12, 217, 157, 255, 247, 0, 175, 255, 255, 254, 16, 13, 255, 255, 249, 0, 5, 255, 255, 249, 0, 8, 255, 255, 255, 32, 11, 255, 255, 255, 240, 4, 255, 255, 255, 255, 85, 159, 255, 255, 255, 255, 255, 255, 255, 255, 254, 17, 95, 255, 255, 255, 224, 4, 255, 255, 255, 254, 0, 79, 255, 255 }; const uint8_t * const Font_TTHoves_DemiBold_21[126 + 1 - 32] = { Font_TTHoves_DemiBold_21_glyph_32, diff --git a/core/embed/lib/fonts/font_tthoves_regular_21.c b/core/embed/lib/fonts/font_tthoves_regular_21.c index a0b0bff66..20c188411 100644 --- a/core/embed/lib/fonts/font_tthoves_regular_21.c +++ b/core/embed/lib/fonts/font_tthoves_regular_21.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_TTHoves_Regular_21_glyph_125[] = { 6, 20, 7, 0, 16, 21, 48, 0, 79, 248, 0, 0, 175, 0, 0, 127, 16, 0, 127, 16, 0, 127, 16, 0, 127, 16, 0, 127, 16, 0, 111, 48, 0, 29, 212, 0, 3, 231, 0, 63, 145, 0, 127, 16, 0, 127, 16, 0, 127, 16, 0, 127, 16, 0, 127, 16, 0, 127, 16, 22, 222, 0, 79, 212, 0 }; /* ~ */ static const uint8_t Font_TTHoves_Regular_21_glyph_126[] = { 11, 4, 12, 0, 8, 0, 22, 81, 0, 3, 80, 79, 255, 227, 0, 220, 12, 211, 61, 249, 191, 112, 232, 0, 9, 238, 128 }; -const uint8_t Font_TTHoves_Regular_21_glyph_nonprintable[] = { 10, 15, 11, 0, 15, 255, 196, 17, 58, 255, 249, 0, 69, 16, 111, 224, 46, 255, 244, 11, 160, 175, 255, 254, 6, 254, 255, 255, 255, 4, 255, 255, 255, 252, 6, 255, 255, 255, 226, 12, 255, 255, 250, 16, 191, 255, 255, 160, 61, 255, 255, 255, 64, 239, 255, 255, 255, 49, 255, 255, 255, 255, 119, 255, 255, 255, 255, 255, 255, 255, 255, 255, 50, 255, 255, 255, 255, 16, 255, 255 }; +/* ? */ const uint8_t Font_TTHoves_Regular_21_glyph_nonprintable[] = { 10, 15, 11, 0, 15, 255, 196, 17, 58, 255, 249, 0, 69, 16, 111, 224, 46, 255, 244, 11, 160, 175, 255, 254, 6, 254, 255, 255, 255, 4, 255, 255, 255, 252, 6, 255, 255, 255, 226, 12, 255, 255, 250, 16, 191, 255, 255, 160, 61, 255, 255, 255, 64, 239, 255, 255, 255, 49, 255, 255, 255, 255, 119, 255, 255, 255, 255, 255, 255, 255, 255, 255, 50, 255, 255, 255, 255, 16, 255, 255 }; const uint8_t * const Font_TTHoves_Regular_21[126 + 1 - 32] = { Font_TTHoves_Regular_21_glyph_32, diff --git a/core/embed/lib/fonts/font_unifont_bold_16.c b/core/embed/lib/fonts/font_unifont_bold_16.c index ad4097cae..975620b90 100644 --- a/core/embed/lib/fonts/font_unifont_bold_16.c +++ b/core/embed/lib/fonts/font_unifont_bold_16.c @@ -102,7 +102,7 @@ /* } */ static const uint8_t Font_Unifont_Bold_16_glyph_125[] = { 5, 13, 7, 0, 11, 225, 140, 198, 24, 102, 99, 12, 110, 0 }; /* ~ */ static const uint8_t Font_Unifont_Bold_16_glyph_126[] = { 7, 3, 8, 0, 11, 99, 118, 48 }; -const uint8_t Font_Unifont_Bold_16_glyph_nonprintable[] = { 7, 10, 8, 0, 10, 130, 112, 231, 207, 60, 249, 255, 231, 207 }; +/* ? */ const uint8_t Font_Unifont_Bold_16_glyph_nonprintable[] = { 6, 10, 7, 0, 10, 5, 199, 60, 231, 60, 255, 207, 63 }; const uint8_t * const Font_Unifont_Bold_16[126 + 1 - 32] = { Font_Unifont_Bold_16_glyph_32, diff --git a/core/embed/lib/fonts/font_unifont_regular_16.c b/core/embed/lib/fonts/font_unifont_regular_16.c index 93eaadc25..bb0607f1b 100644 --- a/core/embed/lib/fonts/font_unifont_regular_16.c +++ b/core/embed/lib/fonts/font_unifont_regular_16.c @@ -104,9 +104,9 @@ /* { */ static const uint8_t Font_Unifont_Regular_16_glyph_123[] = { 4, 13, 7, 1, 11, 52, 66, 36, 132, 34, 68, 48 }; /* | */ static const uint8_t Font_Unifont_Regular_16_glyph_124[] = { 1, 14, 7, 3, 12, 255, 252 }; /* } */ static const uint8_t Font_Unifont_Regular_16_glyph_125[] = { 4, 13, 7, 1, 11, 194, 36, 66, 18, 68, 34, 192 }; -/* ~ */ static const uint8_t Font_Unifont_Regular_16_glyph_126[] = { 7, 3, 8, 0, 11, 99, 38, 48 }; // < --- advanced changed from 7 to 8 +/* ~ */ static const uint8_t Font_Unifont_Regular_16_glyph_126[] = { 7, 3, 7, 0, 11, 99, 38, 48 }; -const uint8_t Font_Unifont_Regular_16_glyph_nonprintable[] = { 6, 10, 7, 0, 10, 133, 231, 190, 247, 190, 255, 239, 191 }; +/* ? */ const uint8_t Font_Unifont_Regular_16_glyph_nonprintable[] = { 6, 10, 7, 0, 10, 133, 231, 190, 247, 190, 255, 239, 191 }; const uint8_t * const Font_Unifont_Regular_16[126 + 1 - 32] = { Font_Unifont_Regular_16_glyph_32, diff --git a/core/embed/lib/fonts/fonts.c b/core/embed/lib/fonts/fonts.c index daf3f11cd..eaf4db350 100644 --- a/core/embed/lib/fonts/fonts.c +++ b/core/embed/lib/fonts/fonts.c @@ -18,19 +18,27 @@ */ #include "fonts.h" +#include +#include +#ifdef TRANSLATIONS +#include "librust_fonts.h" +#endif -static uint8_t convert_char(const uint8_t c) { - static char last_was_utf8 = 0; +// TODO: make it return uint32_t (needs logic to assemble at most 4 bytes +// together) +static uint16_t convert_char_utf8(const uint8_t c) { + // Considering only two-byte UTF-8 characters currently + static uint8_t first_utf8_byte = 0; // non-printable ASCII character if (c < ' ') { - last_was_utf8 = 0; + first_utf8_byte = 0; return 0x7F; } // regular ASCII character if (c < 0x80) { - last_was_utf8 = 0; + first_utf8_byte = 0; return c; } @@ -38,13 +46,13 @@ static uint8_t convert_char(const uint8_t c) { // bytes 11xxxxxx are first bytes of UTF-8 characters if (c >= 0xC0) { - last_was_utf8 = 1; - return 0x7F; + first_utf8_byte = c; + return 0; // not print this } - if (last_was_utf8) { - // bytes 10xxxxxx can be successive UTF-8 characters ... - return 0; // skip glyph + if (first_utf8_byte) { + // encountered a successive UTF-8 character ... + return ((uint16_t)first_utf8_byte << 8) | c; } else { // ... or they are just non-printable ASCII characters return 0x7F; @@ -130,31 +138,46 @@ int font_baseline(int font) { } const uint8_t *font_get_glyph(int font, uint8_t c) { - c = convert_char(c); - if (!c) return 0; + uint16_t c_2bytes = convert_char_utf8(c); + bool is_printable = c_2bytes != 0x7F; + if (!c_2bytes) return 0; + +#ifdef TRANSLATIONS + // found UTF8 character + // it is not hardcoded in firmware fonts, it must be extracted from the + // embedded blob + if (c_2bytes > 0xFF) { + PointerData glyph_data = get_utf8_glyph(c_2bytes, font); + if (glyph_data.ptr != NULL) { + return glyph_data.ptr; + } else { + is_printable = false; + } + } +#endif // printable ASCII character - if (c >= ' ' && c < 0x7F) { + if (is_printable && c_2bytes >= ' ' && c_2bytes <= 126) { switch (font) { #ifdef TREZOR_FONT_NORMAL_ENABLE case FONT_NORMAL: - return FONT_NORMAL_DATA[c - ' ']; + return FONT_NORMAL_DATA[c_2bytes - ' ']; #endif #ifdef TREZOR_FONT_DEMIBOLD_ENABLE case FONT_DEMIBOLD: - return FONT_DEMIBOLD_DATA[c - ' ']; + return FONT_DEMIBOLD_DATA[c_2bytes - ' ']; #endif #ifdef TREZOR_FONT_BOLD_ENABLE case FONT_BOLD: - return FONT_BOLD_DATA[c - ' ']; + return FONT_BOLD_DATA[c_2bytes - ' ']; #endif #ifdef TREZOR_FONT_MONO_ENABLE case FONT_MONO: - return FONT_MONO_DATA[c - ' ']; + return FONT_MONO_DATA[c_2bytes - ' ']; #endif #ifdef TREZOR_FONT_BIG_ENABLE case FONT_BIG: - return FONT_BIG_DATA[c - ' ']; + return FONT_BIG_DATA[c_2bytes - ' ']; #endif } return 0; @@ -180,6 +203,10 @@ const uint8_t *font_get_glyph(int font, uint8_t c) { #ifdef TREZOR_FONT_MONO_ENABLE case FONT_MONO: return NONPRINTABLE_GLYPH(FONT_MONO_DATA); +#endif +#ifdef TREZOR_FONT_BIG_ENABLE + case FONT_BIG: + return NONPRINTABLE_GLYPH(FONT_BIG_DATA); #endif } return 0; diff --git a/core/embed/models/layout_common.h b/core/embed/models/layout_common.h index 57f12ff34..898338eeb 100644 --- a/core/embed/models/layout_common.h +++ b/core/embed/models/layout_common.h @@ -15,6 +15,7 @@ extern const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT]; extern const flash_area_t BOARDLOADER_AREA; extern const flash_area_t SECRET_AREA; +extern const flash_area_t TRANSLATIONS_AREA; extern const flash_area_t BOOTLOADER_AREA; extern const flash_area_t FIRMWARE_AREA; extern const flash_area_t WIPE_AREA; diff --git a/core/embed/models/model_T2B1_layout.c b/core/embed/models/model_T2B1_layout.c index 478a89aa9..adaf2f3d7 100644 --- a/core/embed/models/model_T2B1_layout.c +++ b/core/embed/models/model_T2B1_layout.c @@ -38,6 +38,15 @@ const flash_area_t SECRET_AREA = { }, }; +const flash_area_t TRANSLATIONS_AREA = { + .num_subareas = 1, + .subarea[0] = + { + .first_sector = 13, + .num_sectors = 2, + }, +}; + const flash_area_t BOOTLOADER_AREA = { .num_subareas = 1, .subarea[0] = @@ -62,7 +71,7 @@ const flash_area_t FIRMWARE_AREA = { }; const flash_area_t WIPE_AREA = { - .num_subareas = 4, + .num_subareas = 3, .subarea[0] = { .first_sector = 4, @@ -74,12 +83,6 @@ const flash_area_t WIPE_AREA = { .num_sectors = 6, }, .subarea[2] = - { - .first_sector = 13, - .num_sectors = 2, // sector 15 skipped due to bootloader MPU - // settings, sector 12 is secret - }, - .subarea[3] = { .first_sector = 16, .num_sectors = 8, diff --git a/core/embed/models/model_T2T1_layout.c b/core/embed/models/model_T2T1_layout.c index b845ffa56..65932add5 100644 --- a/core/embed/models/model_T2T1_layout.c +++ b/core/embed/models/model_T2T1_layout.c @@ -29,6 +29,16 @@ const flash_area_t BOARDLOADER_AREA = { }, }; +const flash_area_t TRANSLATIONS_AREA = { + .num_subareas = 1, + .subarea[0] = + { + // TODO: can we use the 4th sector? + .first_sector = 12, + .num_sectors = 3, + }, +}; + const flash_area_t BOOTLOADER_AREA = { .num_subareas = 1, .subarea[0] = diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml index 4e7ca7bdc..d23cedbe2 100644 --- a/core/embed/rust/Cargo.toml +++ b/core/embed/rust/Cargo.toml @@ -32,6 +32,7 @@ rgb_led = [] backlight = [] usb = [] optiga = [] +translations = [] test = [ "button", "cc", @@ -43,7 +44,8 @@ test = [ "dma2d", "touch", "backlight", - "optiga" + "optiga", + "translations", ] [lib] diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index ecbcad264..dc92da132 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -289,6 +289,7 @@ fn generate_trezorhal_bindings() { .allowlist_function("storage_delete") .allowlist_function("storage_set_counter") .allowlist_function("storage_next_counter") + .allowlist_function("translations_read") // display .allowlist_function("display_clear") .allowlist_function("display_offset") diff --git a/core/embed/rust/librust.h b/core/embed/rust/librust.h index 15b06589b..590840067 100644 --- a/core/embed/rust/librust.h +++ b/core/embed/rust/librust.h @@ -9,6 +9,7 @@ mp_obj_t protobuf_debug_msg_def_type(); extern mp_obj_module_t mp_module_trezorproto; extern mp_obj_module_t mp_module_trezorui2; +extern mp_obj_module_t mp_module_trezortranslate; #ifdef TREZOR_EMULATOR mp_obj_t ui_debug_layout_type(); diff --git a/core/embed/rust/librust_fonts.h b/core/embed/rust/librust_fonts.h new file mode 100644 index 000000000..d21cc14b5 --- /dev/null +++ b/core/embed/rust/librust_fonts.h @@ -0,0 +1,6 @@ +typedef struct { + const uint8_t* ptr; + uint32_t len; +} PointerData; + +PointerData get_utf8_glyph(uint16_t char_code, int font); diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 50116a2f5..2ad77f547 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -13,6 +13,7 @@ static void _librust_qstrs(void) { MP_QSTR_MESSAGE_WIRE_TYPE; MP_QSTR_Msg; MP_QSTR_MsgDef; + MP_QSTR_TR; MP_QSTR___dict__; MP_QSTR___name__; MP_QSTR_account; @@ -89,6 +90,7 @@ static void _librust_qstrs(void) { MP_QSTR_is_type_of; MP_QSTR_items; MP_QSTR_label; + MP_QSTR_language_name; MP_QSTR_lines; MP_QSTR_max_count; MP_QSTR_max_feerate; diff --git a/core/embed/rust/src/storage/mod.rs b/core/embed/rust/src/storage/mod.rs index 0bb147b0e..96eab3df7 100644 --- a/core/embed/rust/src/storage/mod.rs +++ b/core/embed/rust/src/storage/mod.rs @@ -35,6 +35,7 @@ const SD_SALT_AUTH_KEY: u16 = FLAG_PUBLIC | APP_DEVICE | 0x0012; const INITIALIZED: u16 = FLAG_PUBLIC | APP_DEVICE | 0x0013; const SAFETY_CHECK_LEVEL: u16 = APP_DEVICE | 0x0014; const EXPERIMENTAL_FEATURES: u16 = APP_DEVICE | 0x0015; +const HIDE_PASSPHRASE_FROM_HOST: u16 = APP_DEVICE | 0x0016; pub fn get_avatar_len() -> StorageResult { get_length(HOMESCREEN) diff --git a/core/embed/rust/src/trezorhal/mod.rs b/core/embed/rust/src/trezorhal/mod.rs index 86fe0a76b..1aea7bb35 100644 --- a/core/embed/rust/src/trezorhal/mod.rs +++ b/core/embed/rust/src/trezorhal/mod.rs @@ -14,6 +14,8 @@ pub mod random; pub mod rgb_led; pub mod slip39; pub mod storage; +#[cfg(feature = "translations")] +pub mod translations; pub mod usb; pub mod uzlib; pub mod wordlist; diff --git a/core/embed/rust/src/trezorhal/translations.rs b/core/embed/rust/src/trezorhal/translations.rs new file mode 100644 index 000000000..6f854b5a5 --- /dev/null +++ b/core/embed/rust/src/trezorhal/translations.rs @@ -0,0 +1,27 @@ +use super::ffi; + +#[repr(C)] +pub struct PointerData { + pub ptr: *const u8, + pub len: u32, +} + +pub fn get_translations_blob() -> &'static [u8] { + let mut len: u32 = 0; + let ptr = unsafe { ffi::translations_read(&mut len, 0) }; + if ptr.is_null() { + fatal_error!("Translations read failed", ""); + } + unsafe { core::slice::from_raw_parts(ptr, len as usize) } +} + +pub fn get_pointer_with_offset(offset: u16, len: u16) -> PointerData { + let ptr = unsafe { ffi::translations_read(&mut 0, offset.into()) }; + if ptr.is_null() { + fatal_error!("Translations read failed", ""); + } + PointerData { + ptr, + len: len.into(), + } +} diff --git a/core/embed/rust/src/ui/display/font.rs b/core/embed/rust/src/ui/display/font.rs index 52866a008..0e4eaf866 100644 --- a/core/embed/rust/src/ui/display/font.rs +++ b/core/embed/rust/src/ui/display/font.rs @@ -144,6 +144,36 @@ impl Font { display::text_width(text, self.into()) } + /// Supports UTF8 characters + fn get_glyph_from_char(self, c: char) -> Option { + let mut buffer = [0; 4]; + let bytes = c.encode_utf8(&mut buffer); + for byte in bytes.bytes() { + if let Some(glyph) = self.get_glyph(byte) { + return Some(glyph); + } + } + None + } + + /// Supports UTF8 characters + fn get_first_glyph_from_text(self, text: &str) -> Option { + if let Some(c) = text.chars().next() { + self.get_glyph_from_char(c) + } else { + None + } + } + + /// Supports UTF8 characters + fn get_last_glyph_from_text(self, text: &str) -> Option { + if let Some(c) = text.chars().next_back() { + self.get_glyph_from_char(c) + } else { + None + } + } + /// Width of the text that is visible. /// Not including the spaces before the first and after the last character. pub fn visible_text_width(self, text: &str) -> i16 { @@ -152,14 +182,20 @@ impl Font { return 0; } - let first_char = unwrap!(text.chars().next()); - let first_char_glyph = unwrap!(self.get_glyph(first_char as u8)); + let first_char_bearing = if let Some(glyph) = self.get_first_glyph_from_text(text) { + glyph.bearing_x + } else { + 0 + }; - let last_char = unwrap!(text.chars().last()); - let last_char_glyph = unwrap!(self.get_glyph(last_char as u8)); + let last_char_bearing = if let Some(glyph) = self.get_last_glyph_from_text(text) { + glyph.right_side_bearing() + } else { + 0 + }; // Strip leftmost and rightmost spaces/bearings/margins. - self.text_width(text) - first_char_glyph.bearing_x - last_char_glyph.right_side_bearing() + self.text_width(text) - first_char_bearing - last_char_bearing } /// Returning the x-bearing (offset) of the first character. @@ -169,9 +205,11 @@ impl Font { return 0; } - let first_char = unwrap!(text.chars().next()); - let first_char_glyph = unwrap!(self.get_glyph(first_char as u8)); - first_char_glyph.bearing_x + if let Some(glyph) = self.get_first_glyph_from_text(text) { + glyph.bearing_x + } else { + 0 + } } pub fn char_width(self, ch: char) -> i16 { diff --git a/core/embed/rust/src/ui/mod.rs b/core/embed/rust/src/ui/mod.rs index 0b57e0306..4277fd05d 100644 --- a/core/embed/rust/src/ui/mod.rs +++ b/core/embed/rust/src/ui/mod.rs @@ -9,6 +9,8 @@ pub mod event; pub mod geometry; pub mod lerp; pub mod screens; +#[cfg(feature = "translations")] +pub mod translations; #[macro_use] pub mod util; diff --git a/core/embed/rust/src/ui/model_tr/bootloader/menu.rs b/core/embed/rust/src/ui/model_tr/bootloader/menu.rs index 73afbfc29..4fb185215 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/menu.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/menu.rs @@ -13,7 +13,7 @@ use crate::{ use super::{ super::{ - component::{Choice, ChoiceFactory, ChoicePage}, + component::{ButtonLayout, Choice, ChoiceFactory, ChoicePage}, theme::bootloader::{BLD_BG, BLD_FG, ICON_EXIT, ICON_REDO, ICON_TRASH}, }, ReturnToC, @@ -70,6 +70,10 @@ impl Choice<&'static str> for MenuChoice { BLD_BG, ); } + + fn btn_layout(&self) -> ButtonLayout<&'static str> { + ButtonLayout::arrow_armed_arrow("SELECT") + } } #[cfg(feature = "ui_debug")] diff --git a/core/embed/rust/src/ui/model_tr/component/address_details.rs b/core/embed/rust/src/ui/model_tr/component/address_details.rs index 60e992d56..ae74185a4 100644 --- a/core/embed/rust/src/ui/model_tr/component/address_details.rs +++ b/core/embed/rust/src/ui/model_tr/component/address_details.rs @@ -9,6 +9,7 @@ use crate::{ Child, Component, Event, EventCtx, Pad, Paginate, Qr, }, geometry::Rect, + translations::tr, }, }; @@ -48,11 +49,17 @@ where let details_view = { let mut para = ParagraphVecShort::new(); if let Some(account) = account { - para.add(Paragraph::new(&theme::TEXT_BOLD, "Account:".into())); + para.add(Paragraph::new( + &theme::TEXT_BOLD, + tr("address_details__account").into(), + )); para.add(Paragraph::new(&theme::TEXT_MONO, account)); } if let Some(path) = path { - para.add(Paragraph::new(&theme::TEXT_BOLD, "Derivation path:".into())); + para.add(Paragraph::new( + &theme::TEXT_BOLD, + tr("address_details__derivation_path").into(), + )); para.add(Paragraph::new(&theme::TEXT_MONO, path)); } Paragraphs::new(para) @@ -123,7 +130,7 @@ where } else { let left = Some(ButtonDetails::left_arrow_icon()); let middle = if self.is_xpub_page() && self.subpages_in_current_page() > 1 { - Some(ButtonDetails::armed_text("SHOW ALL".into())) + Some(ButtonDetails::armed_text(tr("buttons__show_all").into())) } else { None }; diff --git a/core/embed/rust/src/ui/model_tr/component/button.rs b/core/embed/rust/src/ui/model_tr/component/button.rs index da53990bf..31d281519 100644 --- a/core/embed/rust/src/ui/model_tr/component/button.rs +++ b/core/embed/rust/src/ui/model_tr/component/button.rs @@ -513,12 +513,7 @@ where Self::new(None, None, None) } - /// Default button layout for all three buttons - icons. - pub fn default_three_icons() -> Self { - Self::arrow_armed_arrow("SELECT".into()) - } - - /// Special middle text for default icon layout. + /// Arrows at sides, armed text in the middle. pub fn arrow_armed_arrow(text: T) -> Self { Self::new( Some(ButtonDetails::left_arrow_icon()), @@ -934,9 +929,6 @@ impl ButtonActions { // DEBUG-ONLY SECTION BELOW -#[cfg(feature = "ui_debug")] -use crate::strutil::ShortString; - #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Button { fn trace(&self, t: &mut dyn crate::trace::Tracer) { @@ -969,19 +961,3 @@ impl crate::trace::Trace for ButtonDetails { } } } - -#[cfg(feature = "ui_debug")] -impl ButtonAction { - /// Describing the action as a string. Debug-only. - pub fn string(&self) -> ShortString { - match self { - ButtonAction::NextPage => "Next".into(), - ButtonAction::PrevPage => "Prev".into(), - ButtonAction::FirstPage => "First".into(), - ButtonAction::LastPage => "Last".into(), - ButtonAction::Cancel => "Cancel".into(), - ButtonAction::Confirm => "Confirm".into(), - ButtonAction::Info => "Info".into(), - } - } -} diff --git a/core/embed/rust/src/ui/model_tr/component/coinjoin_progress.rs b/core/embed/rust/src/ui/model_tr/component/coinjoin_progress.rs index eb67ee5ed..c66f1002e 100644 --- a/core/embed/rust/src/ui/model_tr/component/coinjoin_progress.rs +++ b/core/embed/rust/src/ui/model_tr/component/coinjoin_progress.rs @@ -10,14 +10,13 @@ use crate::{ }, display::{self, Font}, geometry::{Alignment, Insets, Rect}, + translations::tr, util::animation_disabled, }, }; use super::theme; -const HEADER: &str = "COINJOIN IN PROGRESS"; -const FOOTER: &str = "Do not disconnect your Trezor!"; const FOOTER_TEXT_MARGIN: i16 = 8; const LOADER_OFFSET: i16 = -15; const LOADER_SPEED: u16 = 10; @@ -89,7 +88,7 @@ where if self.indeterminate { text_multiline( self.area, - HEADER, + tr("coinjoin__title_progress"), Font::BOLD, theme::FG, theme::BG, @@ -114,7 +113,7 @@ where // BOTTOM let top_rest = text_multiline_bottom( self.area, - FOOTER, + tr("coinjoin__do_not_disconnect"), Font::BOLD, theme::FG, theme::BG, @@ -140,8 +139,8 @@ where { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("CoinJoinProgress"); - t.string("header", HEADER); + t.string("header", tr("coinjoin__title_progress")); t.string("text", self.text.as_ref()); - t.string("footer", FOOTER); + t.string("footer", tr("coinjoin__do_not_disconnect")); } } diff --git a/core/embed/rust/src/ui/model_tr/component/homescreen.rs b/core/embed/rust/src/ui/model_tr/component/homescreen.rs index bac39440e..e51ab0e53 100644 --- a/core/embed/rust/src/ui/model_tr/component/homescreen.rs +++ b/core/embed/rust/src/ui/model_tr/component/homescreen.rs @@ -8,6 +8,7 @@ use crate::{ event::USBEvent, geometry::{Alignment2D, Insets, Offset, Point, Rect}, layout::util::get_user_custom_image, + translations::tr, }, }; @@ -82,7 +83,11 @@ where if !usb_configured() { self.fill_notification_background(); // TODO: fill warning icons here as well? - display_center(baseline, &"NO USB CONNECTION", NOTIFICATION_FONT); + display_center( + baseline, + &tr("homescreen__title_no_usb_connection"), + NOTIFICATION_FONT, + ); } else if let Some((notification, _level)) = &self.notification { self.fill_notification_background(); display_center(baseline, ¬ification.as_ref(), NOTIFICATION_FONT); @@ -192,9 +197,9 @@ where // so that even middle-click triggers the event. let invisible_btn_layout = ButtonLayout::arrow_armed_arrow("".into()); let instruction_str = if bootscreen { - "Click to Connect" + tr("homescreen__click_to_connect") } else { - "Click to Unlock" + tr("homescreen__click_to_unlock") }; Lockscreen { label: Child::new(Label::centered(label, theme::TEXT_BIG)), @@ -264,7 +269,7 @@ where T: StringType + Clone, { pub fn new(title: T, buffer_func: F) -> Self { - let btn_layout = ButtonLayout::cancel_none_text("CHANGE".into()); + let btn_layout = ButtonLayout::cancel_none_text(tr("buttons__change").into()); ConfirmHomescreen { title: Child::new(Label::centered(title, theme::TEXT_BOLD)), buffer_func, diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/choice.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/choice.rs index 8acd54003..376ed2122 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/choice.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/choice.rs @@ -26,9 +26,7 @@ pub trait Choice { 0 } - fn btn_layout(&self) -> ButtonLayout { - ButtonLayout::default_three_icons() - } + fn btn_layout(&self) -> ButtonLayout; /// Whether it is possible to do the middle action event without /// releasing the button - after long-press duration is reached. diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/mod.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/mod.rs index e3c8cc649..dd801000a 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/mod.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/mod.rs @@ -1,8 +1,13 @@ pub mod choice; pub mod choice_item; +#[cfg(feature = "translations")] pub mod number_input; +#[cfg(feature = "translations")] pub mod passphrase; +#[cfg(feature = "translations")] pub mod pin; +#[cfg(feature = "translations")] pub mod simple_choice; +#[cfg(feature = "translations")] pub mod wordlist; diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/number_input.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/number_input.rs index bdc132f0b..cb8e93cff 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/number_input.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/number_input.rs @@ -3,6 +3,7 @@ use crate::{ ui::{ component::{Component, Event, EventCtx}, geometry::Rect, + translations::tr, }, }; @@ -31,7 +32,10 @@ impl ChoiceFactory for ChoiceFactoryNumberInput { fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) { let num = self.min + choice_index as u32; let text: String<10> = String::from(num); - let mut choice_item = ChoiceItem::new(text, ButtonLayout::default_three_icons()); + let mut choice_item = ChoiceItem::new( + text, + ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()), + ); // Disabling prev/next buttons for the first/last choice. // (could be done to the same button if there is only one) diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/passphrase.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/passphrase.rs index 47327311b..47a8b0473 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/passphrase.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/passphrase.rs @@ -5,6 +5,7 @@ use crate::{ component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx}, display::Icon, geometry::Rect, + translations::tr, util::char_to_string, }, }; @@ -43,64 +44,81 @@ const DIGITS_INDEX: usize = 5; const SPECIAL_INDEX: usize = 6; const SPACE_INDEX: usize = 7; -/// Menu text, action, icon data, middle button with CONFIRM, without_release -const MENU: [(&str, PassphraseAction, Option, bool, bool); MENU_LENGTH] = [ - ( - "SHOW", - PassphraseAction::Show, - Some(theme::ICON_EYE), - true, - false, - ), - ( - "CANCEL_OR_DELETE", // will be chosen dynamically - PassphraseAction::CancelOrDelete, - None, - true, - true, // without_release - ), - ( - "ENTER", - PassphraseAction::Enter, - Some(theme::ICON_TICK), - true, - false, - ), - ( - "abc", - PassphraseAction::Category(ChoiceCategory::LowercaseLetter), - None, - false, - false, - ), - ( - "ABC", - PassphraseAction::Category(ChoiceCategory::UppercaseLetter), - None, - false, - false, - ), - ( - "123", - PassphraseAction::Category(ChoiceCategory::Digit), - None, - false, - false, - ), - ( - "#$!", - PassphraseAction::Category(ChoiceCategory::SpecialSymbol), - None, - false, - false, - ), - ( - "SPACE", - PassphraseAction::Character(' '), - Some(theme::ICON_SPACE), - false, - false, - ), +#[derive(Copy, Clone)] +struct MenuItem { + text: &'static str, + action: PassphraseAction, + icon: Option, + show_confirm: bool, + without_release: bool, + translate: bool, +} + +const MENU: [MenuItem; MENU_LENGTH] = [ + MenuItem { + text: "inputs__show", + action: PassphraseAction::Show, + icon: Some(theme::ICON_EYE), + show_confirm: true, + without_release: false, + translate: true, + }, + MenuItem { + text: "CANCEL_OR_DELETE", + action: PassphraseAction::CancelOrDelete, + icon: None, + show_confirm: true, + without_release: true, + translate: false, + }, + MenuItem { + text: "inputs__enter", + action: PassphraseAction::Enter, + icon: Some(theme::ICON_TICK), + show_confirm: true, + without_release: false, + translate: true, + }, + MenuItem { + text: "abc", + action: PassphraseAction::Category(ChoiceCategory::LowercaseLetter), + icon: None, + show_confirm: false, + without_release: false, + translate: false, + }, + MenuItem { + text: "ABC", + action: PassphraseAction::Category(ChoiceCategory::UppercaseLetter), + icon: None, + show_confirm: false, + without_release: false, + translate: false, + }, + MenuItem { + text: "123", + action: PassphraseAction::Category(ChoiceCategory::Digit), + icon: None, + show_confirm: false, + without_release: false, + translate: false, + }, + MenuItem { + text: "#$!", + action: PassphraseAction::Category(ChoiceCategory::SpecialSymbol), + icon: None, + show_confirm: false, + without_release: false, + translate: false, + }, + MenuItem { + text: "inputs__space", + action: PassphraseAction::Character(' '), + icon: Some(theme::ICON_SPACE), + show_confirm: false, + without_release: false, + translate: true, + }, ]; #[derive(Clone, Copy)] @@ -177,34 +195,42 @@ impl ChoiceFactoryPassphrase { choice_index: usize, ) -> (ChoiceItem, PassphraseAction) { // More options for CANCEL/DELETE button - let (mut text, action, mut icon, show_confirm, without_release) = MENU[choice_index]; - if matches!(action, PassphraseAction::CancelOrDelete) { + let mut current_item = MENU[choice_index]; + if matches!(current_item.action, PassphraseAction::CancelOrDelete) { if self.is_empty { - text = "CANCEL"; - icon = Some(theme::ICON_CANCEL); + current_item.text = tr("inputs__cancel"); + current_item.icon = Some(theme::ICON_CANCEL); } else { - text = "DELETE"; - icon = Some(theme::ICON_DELETE); + current_item.text = tr("inputs__delete"); + current_item.icon = Some(theme::ICON_DELETE); } } - let mut menu_item = ChoiceItem::new(text, ButtonLayout::default_three_icons()); + // Translating when needed + if current_item.translate { + current_item.text = tr(current_item.text); + } + + let mut menu_item = ChoiceItem::new( + current_item.text, + ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()), + ); // Action buttons have different middle button text - if show_confirm { - let confirm_btn = ButtonDetails::armed_text("CONFIRM".into()); + if current_item.show_confirm { + let confirm_btn = ButtonDetails::armed_text(tr("buttons__confirm").into()); menu_item.set_middle_btn(Some(confirm_btn)); } // Making middle button create LongPress events - if without_release { + if current_item.without_release { menu_item = menu_item.with_middle_action_without_release(); } - if let Some(icon) = icon { + if let Some(icon) = current_item.icon { menu_item = menu_item.with_icon(icon); } - (menu_item, action) + (menu_item, current_item.action) } /// Character choices with a BACK to MENU choice at the end (visible from @@ -215,14 +241,20 @@ impl ChoiceFactoryPassphrase { ) -> (ChoiceItem, PassphraseAction) { if is_menu_choice(&self.current_category, choice_index) { ( - ChoiceItem::new("BACK", ButtonLayout::arrow_armed_arrow("RETURN".into())) - .with_icon(theme::ICON_ARROW_BACK_UP), + ChoiceItem::new( + tr("inputs__back"), + ButtonLayout::arrow_armed_arrow(tr("inputs__return").into()), + ) + .with_icon(theme::ICON_ARROW_BACK_UP), PassphraseAction::Menu, ) } else { let ch = get_char(&self.current_category, choice_index); ( - ChoiceItem::new(char_to_string(ch), ButtonLayout::default_three_icons()), + ChoiceItem::new( + char_to_string(ch), + ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()), + ), PassphraseAction::Character(ch), ) } @@ -453,10 +485,10 @@ where "current_category", match self.current_category { ChoiceCategory::Menu => "MENU", - ChoiceCategory::LowercaseLetter => MENU[LOWERCASE_INDEX].0, - ChoiceCategory::UppercaseLetter => MENU[UPPERCASE_INDEX].0, - ChoiceCategory::Digit => MENU[DIGITS_INDEX].0, - ChoiceCategory::SpecialSymbol => MENU[SPECIAL_INDEX].0, + ChoiceCategory::LowercaseLetter => MENU[LOWERCASE_INDEX].text, + ChoiceCategory::UppercaseLetter => MENU[UPPERCASE_INDEX].text, + ChoiceCategory::Digit => MENU[DIGITS_INDEX].text, + ChoiceCategory::SpecialSymbol => MENU[SPECIAL_INDEX].text, }, ); t.child("choice_page", &self.choice_page); diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs index 3c2206bb0..722345e35 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs @@ -5,6 +5,7 @@ use crate::{ component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx}, display::{Font, Icon}, geometry::Rect, + translations::tr, }, }; @@ -27,22 +28,40 @@ const EMPTY_PIN_STR: &str = "_"; const CHOICE_LENGTH: usize = 13; const NUMBER_START_INDEX: usize = 3; -/// Text, action, icon, without_release -const CHOICES: [(&str, PinAction, Option, bool); CHOICE_LENGTH] = [ +/// Text, action, icon, without_release, translate +const CHOICES: [(&str, PinAction, Option, bool, bool); CHOICE_LENGTH] = [ // DELETE should be triggerable without release (after long-press) - ("DELETE", PinAction::Delete, Some(theme::ICON_DELETE), true), - ("SHOW", PinAction::Show, Some(theme::ICON_EYE), false), - ("ENTER", PinAction::Enter, Some(theme::ICON_TICK), false), - ("0", PinAction::Digit('0'), None, false), - ("1", PinAction::Digit('1'), None, false), - ("2", PinAction::Digit('2'), None, false), - ("3", PinAction::Digit('3'), None, false), - ("4", PinAction::Digit('4'), None, false), - ("5", PinAction::Digit('5'), None, false), - ("6", PinAction::Digit('6'), None, false), - ("7", PinAction::Digit('7'), None, false), - ("8", PinAction::Digit('8'), None, false), - ("9", PinAction::Digit('9'), None, false), + ( + "inputs__delete", + PinAction::Delete, + Some(theme::ICON_DELETE), + true, + true, + ), + ( + "inputs__show", + PinAction::Show, + Some(theme::ICON_EYE), + false, + true, + ), + ( + "inputs__enter", + PinAction::Enter, + Some(theme::ICON_TICK), + false, + true, + ), + ("0", PinAction::Digit('0'), None, false, false), + ("1", PinAction::Digit('1'), None, false, false), + ("2", PinAction::Digit('2'), None, false, false), + ("3", PinAction::Digit('3'), None, false, false), + ("4", PinAction::Digit('4'), None, false, false), + ("5", PinAction::Digit('5'), None, false, false), + ("6", PinAction::Digit('6'), None, false, false), + ("7", PinAction::Digit('7'), None, false, false), + ("8", PinAction::Digit('8'), None, false, false), + ("9", PinAction::Digit('9'), None, false, false), ]; fn get_random_digit_position() -> usize { @@ -56,13 +75,21 @@ impl ChoiceFactory for ChoiceFactoryPIN { type Item = ChoiceItem; fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) { - let (choice_str, action, icon, without_release) = CHOICES[choice_index]; + let (mut choice_str, action, icon, without_release, translate) = CHOICES[choice_index]; - let mut choice_item = ChoiceItem::new(choice_str, ButtonLayout::default_three_icons()); + // Translating when needed + if translate { + choice_str = tr(choice_str); + } + + let mut choice_item = ChoiceItem::new( + choice_str, + ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()), + ); // Action buttons have different middle button text if !matches!(action, PinAction::Digit(_)) { - let confirm_btn = ButtonDetails::armed_text("CONFIRM".into()); + let confirm_btn = ButtonDetails::armed_text(tr("buttons__confirm").into()); choice_item.set_middle_btn(Some(confirm_btn)); } @@ -111,7 +138,7 @@ where let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt { ( false, - String::from("WRONG PIN"), + String::from(tr("pin__title_wrong_pin")), String::from(subprompt.as_ref()), ) } else { diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs index 33d99fd9b..ddf205c3e 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs @@ -3,6 +3,7 @@ use crate::{ ui::{ component::{Component, Event, EventCtx}, geometry::Rect, + translations::tr, }, }; @@ -38,7 +39,10 @@ impl ChoiceFactory for ChoiceFactorySimple { fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) { let text = &self.choices[choice_index]; - let mut choice_item = ChoiceItem::new(text, ButtonLayout::default_three_icons()); + let mut choice_item = ChoiceItem::new( + text, + ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()), + ); // Disabling prev/next buttons for the first/last choice when not in carousel. // (could be done to the same button if there is only one) diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/wordlist.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/wordlist.rs index 8dd2f63f5..4c2aaf0d7 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/wordlist.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/wordlist.rs @@ -4,6 +4,7 @@ use crate::{ ui::{ component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx}, geometry::Rect, + translations::tr, util::char_to_string, }, }; @@ -94,9 +95,12 @@ impl ChoiceFactory for ChoiceFactoryWordlist { // (is a requirement for WORDS, doing it for LETTERS as well to unite it) if choice_index == DELETE_INDEX { return ( - ChoiceItem::new("DELETE", ButtonLayout::arrow_armed_arrow("CONFIRM".into())) - .with_icon(theme::ICON_DELETE) - .with_middle_action_without_release(), + ChoiceItem::new( + tr("inputs__delete"), + ButtonLayout::arrow_armed_arrow(tr("buttons__confirm").into()), + ) + .with_icon(theme::ICON_DELETE) + .with_middle_action_without_release(), WordlistAction::Delete, ); } @@ -105,7 +109,10 @@ impl ChoiceFactory for ChoiceFactoryWordlist { let index = self.word_random_order[choice_index - 1]; let word = self.wordlist.get(index).unwrap_or_default(); ( - ChoiceItem::new(word, ButtonLayout::default_three_icons()), + ChoiceItem::new( + word, + ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()), + ), WordlistAction::Word(word), ) } else { @@ -115,7 +122,10 @@ impl ChoiceFactory for ChoiceFactoryWordlist { .nth(choice_index - 1) .unwrap_or_default(); ( - ChoiceItem::new(char_to_string(letter), ButtonLayout::default_three_icons()), + ChoiceItem::new( + char_to_string(letter), + ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()), + ), WordlistAction::Letter(letter), ) } diff --git a/core/embed/rust/src/ui/model_tr/component/mod.rs b/core/embed/rust/src/ui/model_tr/component/mod.rs index 808659007..07a8d9766 100644 --- a/core/embed/rust/src/ui/model_tr/component/mod.rs +++ b/core/embed/rust/src/ui/model_tr/component/mod.rs @@ -26,26 +26,32 @@ pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet}; pub use result::ResultScreen; pub use welcome_screen::WelcomeScreen; +#[cfg(feature = "translations")] mod address_details; mod changing_text; +#[cfg(feature = "translations")] mod coinjoin_progress; mod flow; mod flow_pages; mod frame; #[cfg(feature = "micropython")] mod homescreen; +#[cfg(feature = "translations")] mod page; mod progress; mod result_anim; mod result_popup; mod scrollbar; +#[cfg(feature = "translations")] mod share_words; mod show_more; mod title; +#[cfg(feature = "translations")] pub use address_details::AddressDetails; pub use changing_text::ChangingTextLine; +#[cfg(feature = "translations")] pub use coinjoin_progress::CoinJoinProgress; pub use flow::Flow; pub use flow_pages::{FlowPages, Page}; @@ -59,10 +65,12 @@ pub use input_methods::{ simple_choice::SimpleChoice, wordlist::{WordlistEntry, WordlistType}, }; +#[cfg(feature = "translations")] pub use page::ButtonPage; pub use progress::Progress; pub use result_anim::{ResultAnim, ResultAnimMsg}; pub use result_popup::{ResultPopup, ResultPopupMsg}; pub use scrollbar::ScrollBar; +#[cfg(feature = "translations")] pub use share_words::ShareWords; pub use show_more::{CancelInfoConfirmMsg, ShowMore}; diff --git a/core/embed/rust/src/ui/model_tr/component/page.rs b/core/embed/rust/src/ui/model_tr/component/page.rs index fc3dddbd3..07d442e28 100644 --- a/core/embed/rust/src/ui/model_tr/component/page.rs +++ b/core/embed/rust/src/ui/model_tr/component/page.rs @@ -4,6 +4,7 @@ use crate::{ component::{Child, Component, ComponentExt, Event, EventCtx, Pad, PageMsg, Paginate}, display::Color, geometry::{Insets, Rect}, + translations::tr, }, }; @@ -44,7 +45,7 @@ where content: Child::new(content), pad: Pad::with_background(background).with_clear(), cancel_btn_details: Some(ButtonDetails::cancel_icon()), - confirm_btn_details: Some(ButtonDetails::text("CONFIRM".into())), + confirm_btn_details: Some(ButtonDetails::text(tr("buttons__confirm").into())), back_btn_details: Some(ButtonDetails::up_arrow_icon()), next_btn_details: Some(ButtonDetails::down_arrow_icon_wide()), // Setting empty layout for now, we do not yet know the page count. diff --git a/core/embed/rust/src/ui/model_tr/component/progress.rs b/core/embed/rust/src/ui/model_tr/component/progress.rs index 98bcad306..c96eb41dd 100644 --- a/core/embed/rust/src/ui/model_tr/component/progress.rs +++ b/core/embed/rust/src/ui/model_tr/component/progress.rs @@ -19,7 +19,7 @@ use crate::{ use super::super::theme; const BOTTOM_DESCRIPTION_MARGIN: i16 = 10; -const LOADER_Y_OFFSET_TITLE: i16 = -10; +const LOADER_Y_OFFSET_TITLE: i16 = 0; const LOADER_Y_OFFSET_NO_TITLE: i16 = -20; pub struct Progress diff --git a/core/embed/rust/src/ui/model_tr/component/share_words.rs b/core/embed/rust/src/ui/model_tr/component/share_words.rs index 63053f804..5afab7c58 100644 --- a/core/embed/rust/src/ui/model_tr/component/share_words.rs +++ b/core/embed/rust/src/ui/model_tr/component/share_words.rs @@ -6,6 +6,7 @@ use crate::{ }, display::Font, geometry::{Alignment, Offset, Rect}, + translations::tr, }, }; @@ -71,9 +72,9 @@ where fn get_final_text(&self) -> String<50> { build_string!( 50, - "I wrote down all ", + tr("share_words__wrote_down_all"), inttostr!(self.share_words.len() as u8), - " words in order." + tr("share_words__words_in_order") ) } diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index db1708346..167a8ced4 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -39,6 +39,7 @@ use crate::{ util::{iter_into_array, iter_into_vec, upy_disable_animation, ConfirmBlob}, }, model_tr::component::check_homescreen_format, + translations::tr, }, }; @@ -319,7 +320,7 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M let action: Option = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?; let description: Option = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; - let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?; + let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, tr("buttons__confirm").into())?; let verb_cancel: Option = kwargs .get(Qstr::MP_QSTR_verb_cancel) .unwrap_or_else(|_| Obj::const_none()) @@ -355,7 +356,7 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map let description: Option = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; let extra: Option = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?; - let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?; + let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, tr("buttons__confirm").into())?; let verb_cancel: Option = kwargs .get(Qstr::MP_QSTR_verb_cancel) .unwrap_or_else(|_| Obj::const_none()) @@ -425,7 +426,7 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m content_in_button_page( title, paragraphs.into_paragraphs(), - "CONFIRM".into(), + tr("buttons__confirm").into(), None, hold, ) @@ -457,11 +458,11 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; let ops = OpTextLayout::::new(theme::TEXT_NORMAL) - .text_normal("By continuing you agree to Trezor Company's terms and conditions.".into()) + .text_normal(tr("reset__by_continuing").into()) .next_page() - .text_normal("More info at".into()) + .text_normal(tr("reset__more_info_at").into()) .newline() - .text_bold("trezor.io/tos".into()); + .text_bold(tr("reset__tos_link").into()); let formatted = FormattedText::new(ops).vertically_centered(); content_in_button_page(title, formatted, button, Some("".into()), false) @@ -473,25 +474,25 @@ extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut M let block = move |_args: &[Obj], _kwargs: &Map| { let get_page = move |page_index| match page_index { 0 => { - let btn_layout = ButtonLayout::text_none_arrow_wide("SKIP".into()); + let btn_layout = ButtonLayout::text_none_arrow_wide(tr("buttons__skip").into()); let btn_actions = ButtonActions::cancel_none_next(); let ops = OpTextLayout::new(theme::TEXT_NORMAL) - .text_normal("New wallet created.".into()) + .text_normal(tr("backup__new_wallet_created").into()) .newline() .newline() - .text_normal("It should be backed up now!".into()); + .text_normal(tr("backup__it_should_be_backed_up_now").into()); let formatted = FormattedText::new(ops).vertically_centered(); - Page::new(btn_layout, btn_actions, formatted).with_title("SUCCESS".into()) + Page::new(btn_layout, btn_actions, formatted) + .with_title(tr("words__title_success").into()) } 1 => { - let btn_layout = ButtonLayout::up_arrow_none_text("BACK UP".into()); + let btn_layout = ButtonLayout::up_arrow_none_text(tr("buttons__back_up").into()); let btn_actions = ButtonActions::prev_none_confirm(); - let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal( - "You can use your backup to recover your wallet at any time.".into(), - ); + let ops = OpTextLayout::new(theme::TEXT_NORMAL) + .text_normal(tr("backup__recover_anytime").into()); let formatted = FormattedText::new(ops).vertically_centered(); Page::::new(btn_layout, btn_actions, formatted) - .with_title("BACK UP WALLET".into()) + .with_title(tr("backup__title_backup_wallet").into()) } _ => unreachable!(), }; @@ -545,7 +546,7 @@ extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Ma content_in_button_page( title, paragraphs, - verb.unwrap_or_else(|| "CONFIRM".into()), + verb.unwrap_or_else(|| tr("buttons__confirm").into()), Some("".into()), hold, ) @@ -559,16 +560,16 @@ extern "C" fn new_confirm_joint_total(n_args: usize, args: *const Obj, kwargs: * let total_amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?; let paragraphs = Paragraphs::new([ - Paragraph::new(&theme::TEXT_BOLD, "You are contributing:".into()), + Paragraph::new(&theme::TEXT_BOLD, tr("joint__you_are_contributing").into()), Paragraph::new(&theme::TEXT_MONO, spending_amount), - Paragraph::new(&theme::TEXT_BOLD, "To the total amount:".into()), + Paragraph::new(&theme::TEXT_BOLD, tr("joint__to_the_total_amount").into()), Paragraph::new(&theme::TEXT_MONO, total_amount), ]); content_in_button_page( - "JOINT TRANSACTION".into(), + tr("joint__title").into(), paragraphs, - "HOLD TO CONFIRM".into(), + tr("buttons__hold_to_confirm").into(), Some("".into()), true, ) @@ -583,22 +584,22 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: let amount_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_new)?.try_into()?; let description = if sign < 0 { - "Decrease amount by:" + tr("modify_amount__decrease_amount") } else { - "Increase amount by:" + tr("modify_amount__increase_amount") }; let paragraphs = Paragraphs::new([ Paragraph::new(&theme::TEXT_NORMAL, description.into()), Paragraph::new(&theme::TEXT_MONO, amount_change).break_after(), - Paragraph::new(&theme::TEXT_BOLD, "New amount:".into()), + Paragraph::new(&theme::TEXT_BOLD, tr("modify_amount__new_amount").into()), Paragraph::new(&theme::TEXT_MONO, amount_new), ]); content_in_button_page( - "MODIFY AMOUNT".into(), + tr("modify_amount__title").into(), paragraphs, - "CONFIRM".into(), + tr("buttons__confirm").into(), Some("".into()), false, ) @@ -616,7 +617,7 @@ extern "C" fn new_confirm_output_address(n_args: usize, args: *const Obj, kwargs let get_page = move |page_index| { assert!(page_index == 0); // RECIPIENT + address - let btn_layout = ButtonLayout::cancel_none_text("CONTINUE".into()); + let btn_layout = ButtonLayout::cancel_none_text(tr("buttons__continue").into()); let btn_actions = ButtonActions::cancel_none_confirm(); // Not putting hyphens in the address. // Potentially adding address label in different font. @@ -654,7 +655,7 @@ extern "C" fn new_confirm_output_amount(n_args: usize, args: *const Obj, kwargs: let get_page = move |page_index| { assert!(page_index == 0); // AMOUNT + amount - let btn_layout = ButtonLayout::up_arrow_none_text("CONFIRM".into()); + let btn_layout = ButtonLayout::up_arrow_none_text(tr("buttons__confirm").into()); let btn_actions = ButtonActions::cancel_none_confirm(); let ops = OpTextLayout::new(theme::TEXT_MONO).text_mono(amount.clone()); let formatted = FormattedText::new(ops).vertically_centered(); @@ -684,7 +685,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma match page_index { 0 => { // Total amount + fee - let btn_layout = ButtonLayout::cancel_armed_info("CONFIRM".into()); + let btn_layout = ButtonLayout::cancel_armed_info(tr("buttons__confirm").into()); let btn_actions = ButtonActions::cancel_confirm_next(); let ops = OpTextLayout::new(theme::TEXT_MONO) @@ -708,11 +709,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma let fee_rate_amount = fee_rate_amount.clone().unwrap_or_default(); let ops = OpTextLayout::new(theme::TEXT_MONO) - .text_bold("FEE INFORMATION".into()) + .text_bold(tr("confirm_total__title_fee").into()) .newline() .newline() .newline_half() - .text_bold("Fee rate:".into()) + .text_bold(tr("confirm_total__fee_rate").into()) .newline() .text_mono(fee_rate_amount); @@ -729,11 +730,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma // TODO: include wallet info when available let ops = OpTextLayout::new(theme::TEXT_MONO) - .text_bold("SENDING FROM".into()) + .text_bold(tr("confirm_total__title_sending_from").into()) .newline() .newline() .newline_half() - .text_bold("Account:".into()) + .text_bold(tr("confirm_total__account").into()) .newline() .text_mono(account_label); @@ -765,9 +766,9 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m 0 => { // Amount + fee let btn_layout = if cancel_cross { - ButtonLayout::cancel_armed_info("CONFIRM".into()) + ButtonLayout::cancel_armed_info(tr("buttons__confirm").into()) } else { - ButtonLayout::up_arrow_armed_info("CONFIRM".into()) + ButtonLayout::up_arrow_armed_info(tr("buttons__confirm").into()) }; let btn_actions = ButtonActions::cancel_confirm_next(); @@ -803,7 +804,7 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m let formatted = FormattedText::new(ops).vertically_centered(); Page::new(btn_layout, btn_actions, formatted) - .with_title("FEE INFORMATION".into()) + .with_title(tr("confirm_total__title_fee").into()) .with_slim_arrows() } _ => unreachable!(), @@ -821,7 +822,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut let block = move |_args: &[Obj], kwargs: &Map| { let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?; - let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?; + let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, tr("buttons__confirm").into())?; let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?; let get_page = move |page_index| { @@ -872,62 +873,51 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj // really cancel the tutorial. match page_index { // title, text, btn_layout, btn_actions - 0 => { - tutorial_screen( - "HELLO", - "Welcome to Trezor. Press right to continue.", - ButtonLayout::cancel_none_arrow(), - ButtonActions::last_none_next(), - ) - }, - 1 => { - tutorial_screen( - "", - "Use Trezor by\nclicking the left and right buttons.\n\rContinue right.", - ButtonLayout::arrow_none_arrow(), - ButtonActions::prev_none_next(), - ) - }, - 2 => { - tutorial_screen( - "HOLD TO CONFIRM", - "Press and hold the right button to\napprove important operations.", - ButtonLayout::arrow_none_htc("HOLD TO CONFIRM".into()), - ButtonActions::prev_none_next(), - ) - }, - 3 => { - tutorial_screen( - "SCREEN SCROLL", - "Press right to scroll down to read all content when text doesn't fit on one screen.\n\rPress left to scroll up.", - ButtonLayout::arrow_none_text("CONTINUE".into()), - ButtonActions::prev_none_next(), - ) - }, - 4 => { - tutorial_screen( - "CONFIRM", - "Press both left and right at the same\ntime to confirm.", - ButtonLayout::none_armed_none("CONFIRM".into()), - ButtonActions::none_next_none(), - ) - }, - 5 => { - tutorial_screen( - "TUTORIAL COMPLETE", - "You're ready to\nuse Trezor.", - ButtonLayout::text_none_text("AGAIN".into(), "CONTINUE".into()), - ButtonActions::beginning_none_confirm(), - ) - }, - 6 => { - tutorial_screen( - "SKIP TUTORIAL", - "Are you sure you\nwant to skip the tutorial?", - ButtonLayout::arrow_none_text("SKIP".into()), - ButtonActions::beginning_none_cancel(), - ) - }, + 0 => tutorial_screen( + tr("tutorial__title_hello"), + tr("tutorial__welcome_press_right"), + ButtonLayout::cancel_none_arrow(), + ButtonActions::last_none_next(), + ), + 1 => tutorial_screen( + "", + tr("tutorial__use_trezor"), + ButtonLayout::arrow_none_arrow(), + ButtonActions::prev_none_next(), + ), + 2 => tutorial_screen( + tr("buttons__hold_to_confirm"), + tr("tutorial__press_and_hold"), + ButtonLayout::arrow_none_htc(tr("buttons__hold_to_confirm").into()), + ButtonActions::prev_none_next(), + ), + 3 => tutorial_screen( + tr("tutorial__title_screen_scroll"), + tr("tutorial__scroll_down"), + ButtonLayout::arrow_none_text(tr("buttons__continue").into()), + ButtonActions::prev_none_next(), + ), + 4 => tutorial_screen( + tr("buttons__confirm"), + tr("tutorial__middle_click"), + ButtonLayout::none_armed_none(tr("buttons__confirm").into()), + ButtonActions::none_next_none(), + ), + 5 => tutorial_screen( + tr("tutorial__title_tutorial_complete"), + tr("tutorial__ready_to_use"), + ButtonLayout::text_none_text( + tr("buttons__again").into(), + tr("buttons__continue").into(), + ), + ButtonActions::beginning_none_confirm(), + ), + 6 => tutorial_screen( + tr("tutorial__title_skip"), + tr("tutorial__sure_you_want_skip"), + ButtonLayout::arrow_none_text(tr("buttons__skip").into()), + ButtonActions::beginning_none_cancel(), + ), _ => unreachable!(), } }; @@ -957,28 +947,33 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m .try_into_option()?; let (description, change) = match sign { - s if s < 0 => ("Decrease fee by:", user_fee_change), - s if s > 0 => ("Increase fee by:", user_fee_change), - _ => ("Your fee did not change.", StrBuffer::empty()), + s if s < 0 => (tr("modify_fee__decrease_fee"), user_fee_change), + s if s > 0 => (tr("modify_fee__increase_fee"), user_fee_change), + _ => (tr("modify_fee__no_change"), StrBuffer::empty()), }; let mut paragraphs_vec = ParagraphVecShort::new(); paragraphs_vec .add(Paragraph::new(&theme::TEXT_BOLD, description.into())) .add(Paragraph::new(&theme::TEXT_MONO, change)) - .add(Paragraph::new(&theme::TEXT_BOLD, "Transaction fee:".into()).no_break()) + .add( + Paragraph::new(&theme::TEXT_BOLD, tr("modify_fee__transaction_fee").into()) + .no_break(), + ) .add(Paragraph::new(&theme::TEXT_MONO, total_fee_new)); if let Some(fee_rate_amount) = fee_rate_amount { paragraphs_vec - .add(Paragraph::new(&theme::TEXT_BOLD, "Fee rate:".into()).no_break()) + .add( + Paragraph::new(&theme::TEXT_BOLD, tr("modify_fee__fee_rate").into()).no_break(), + ) .add(Paragraph::new(&theme::TEXT_MONO, fee_rate_amount)); } content_in_button_page( - "MODIFY FEE".into(), + tr("modify_fee__title").into(), paragraphs_vec.into_paragraphs(), - "CONFIRM".into(), + tr("buttons__confirm").into(), Some("".into()), false, ) @@ -1060,25 +1055,25 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map let (btn_layout, btn_actions) = if page_count == 1 { // There is only one page ( - ButtonLayout::cancel_none_text("CONFIRM".into()), + ButtonLayout::cancel_none_text(tr("buttons__confirm").into()), ButtonActions::cancel_none_confirm(), ) } else if page_index == 0 { // First page ( - ButtonLayout::cancel_armed_arrow("SELECT".into()), + ButtonLayout::cancel_armed_arrow(tr("buttons__select").into()), ButtonActions::cancel_confirm_next(), ) } else if page_index == page_count - 1 { // Last page ( - ButtonLayout::arrow_armed_none("SELECT".into()), + ButtonLayout::arrow_armed_none(tr("buttons__select").into()), ButtonActions::prev_confirm_none(), ) } else { // Page in the middle ( - ButtonLayout::arrow_armed_arrow("SELECT".into()), + ButtonLayout::arrow_armed_arrow(tr("buttons__select").into()), ButtonActions::prev_confirm_next(), ) }; @@ -1162,7 +1157,7 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) - extern "C" fn new_show_passphrase() -> Obj { let block = move || { - let text: StrBuffer = "Please enter your passphrase.".into(); + let text: StrBuffer = tr("passphrase__please_enter").into(); let paragraph = Paragraph::new(&theme::TEXT_NORMAL, text).centered(); let content = Paragraphs::new([paragraph]); let obj = LayoutObj::new(content)?; @@ -1177,15 +1172,15 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma let get_page = move |page_index| { assert!(page_index == 0); - let btn_layout = ButtonLayout::arrow_none_text("QUIT".into()); + let btn_layout = ButtonLayout::arrow_none_text(tr("buttons__quit").into()); let btn_actions = ButtonActions::cancel_none_confirm(); let ops = OpTextLayout::::new(theme::TEXT_NORMAL) .text_bold(title.clone()) .newline() .newline_half() - .text_normal("Please contact Trezor support at".into()) + .text_normal(tr("addr_mismatch__contact_support").into()) .newline() - .text_bold("trezor.io/support".into()); + .text_bold(tr("addr_mismatch__support_url").into()); let formatted = FormattedText::new(ops); Page::new(btn_layout, btn_actions, formatted) }; @@ -1265,18 +1260,19 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut // Decreasing bottom padding between paragraphs to fit one screen let paragraphs = Paragraphs::new([ - Paragraph::new(&theme::TEXT_BOLD, "Max rounds".into()).with_bottom_padding(2), + Paragraph::new(&theme::TEXT_BOLD, tr("coinjoin__max_rounds").into()) + .with_bottom_padding(2), Paragraph::new(&theme::TEXT_MONO, max_rounds), - Paragraph::new(&theme::TEXT_BOLD, "Max mining fee".into()) + Paragraph::new(&theme::TEXT_BOLD, tr("coinjoin__max_mining_fee").into()) .with_bottom_padding(2) .no_break(), Paragraph::new(&theme::TEXT_MONO, max_feerate).with_bottom_padding(2), ]); content_in_button_page( - "AUTHORIZE COINJOIN".into(), + tr("coinjoin__title").into(), paragraphs, - "HOLD TO CONFIRM".into(), + tr("buttons__hold_to_confirm").into(), None, true, ) @@ -1367,7 +1363,8 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut let cancel_btn = Some(ButtonDetails::up_arrow_icon()); let confirm_btn = Some( - ButtonDetails::::text("HOLD TO CONFIRM".into()).with_default_duration(), + ButtonDetails::::text(tr("buttons__hold_to_confirm").into()) + .with_default_duration(), ); let obj = LayoutObj::new( @@ -1449,18 +1446,21 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut let mut paragraphs = ParagraphVecShort::new(); paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, description)); if show_info { - let first = "You'll only have to select the first 2-4 letters of each word."; - let second = - "Position of the cursor will change between entries for enhanced security."; paragraphs - .add(Paragraph::new(&theme::TEXT_NORMAL, first.into())) - .add(Paragraph::new(&theme::TEXT_NORMAL, second.into())); + .add(Paragraph::new( + &theme::TEXT_NORMAL, + tr("recovery__only_first_n_letters").into(), + )) + .add(Paragraph::new( + &theme::TEXT_NORMAL, + tr("recovery__cursor_will_change").into(), + )); } let title = if dry_run { - "BACKUP CHECK" + tr("recovery__title_dry_run") } else { - "RECOVER WALLET" + tr("recovery__title") }; content_in_button_page( @@ -1476,7 +1476,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = |_args: &[Obj], _kwargs: &Map| { - let title: StrBuffer = "NUMBER OF WORDS".into(); + let title: StrBuffer = tr("word_count__title").into(); let choices: Vec = ["12", "18", "20", "24", "33"] .map(|num| num.into()) @@ -1509,7 +1509,13 @@ extern "C" fn new_show_group_share_success( Paragraph::new(&theme::TEXT_BOLD, l3), ]); - content_in_button_page("".into(), paragraphs, "CONTINUE".into(), None, false) + content_in_button_page( + "".into(), + paragraphs, + tr("buttons__continue").into(), + None, + false, + ) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } @@ -1607,7 +1613,7 @@ extern "C" fn new_confirm_firmware_update( let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?; - let title = "UPDATE FIRMWARE"; + let title = tr("firmware_update__title"); let message = Label::left_aligned(description, theme::TEXT_NORMAL).vertically_centered(); let fingerprint = Label::left_aligned( fingerprint, @@ -1616,8 +1622,18 @@ extern "C" fn new_confirm_firmware_update( .vertically_centered(); let obj = LayoutObj::new( - Confirm::new(theme::BG, title, message, None, "INSTALL".into(), false) - .with_info_screen(StrBuffer::from("FW FINGERPRINT"), fingerprint), + Confirm::new( + theme::BG, + title, + message, + None, + tr("buttons__install").into(), + false, + ) + .with_info_screen( + StrBuffer::from(tr("firmware_update__title_fingerprint")), + fingerprint, + ), )?; Ok(obj.into()) }; diff --git a/core/embed/rust/src/ui/model_tt/component/address_details.rs b/core/embed/rust/src/ui/model_tt/component/address_details.rs index c563c2c2f..cd1b9448d 100644 --- a/core/embed/rust/src/ui/model_tt/component/address_details.rs +++ b/core/embed/rust/src/ui/model_tt/component/address_details.rs @@ -9,6 +9,7 @@ use crate::{ Component, Event, EventCtx, Paginate, Qr, }, geometry::Rect, + translations::tr, }, }; @@ -42,13 +43,16 @@ where { let mut para = ParagraphVecShort::new(); if let Some(a) = account { - para.add(Paragraph::new(&theme::TEXT_NORMAL, "Account:".into())); + para.add(Paragraph::new( + &theme::TEXT_NORMAL, + tr("address_details__account").into(), + )); para.add(Paragraph::new(&theme::TEXT_MONO, a)); } if let Some(p) = path { para.add(Paragraph::new( &theme::TEXT_NORMAL, - "Derivation path:".into(), + tr("address_details__derivation_path").into(), )); para.add(Paragraph::new(&theme::TEXT_MONO, p)); } diff --git a/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs b/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs index 1bde6d975..b419b0f3d 100644 --- a/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs +++ b/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs @@ -9,6 +9,7 @@ use crate::{ }, display::loader::{loader_circular_uncompress, LoaderDimensions}, geometry::{Insets, Rect}, + translations::tr, util::animation_disabled, }, }; @@ -43,7 +44,8 @@ where T: AsRef, { let style = theme::label_coinjoin_progress(); - let label = Label::centered("DO NOT DISCONNECT YOUR TREZOR!", style).vertically_centered(); + let label = + Label::centered(tr("coinjoin__title_do_not_disconnect"), style).vertically_centered(); let bg = painter::rect_painter(style.background_color, theme::BG); let inner = (bg, label); CoinJoinProgress::with_background(text, inner, indeterminate) @@ -61,7 +63,7 @@ where indeterminate, content: Frame::centered( theme::label_title(), - "COINJOIN IN PROGRESS", + tr("coinjoin__title_progress"), Split::bottom(RECTANGLE_HEIGHT, 0, Empty, inner), ) .into_child(), diff --git a/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs b/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs index dad943255..f4ff43395 100644 --- a/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs +++ b/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs @@ -10,6 +10,7 @@ use crate::{ geometry::{Offset, Point, Rect}, layout::util::get_user_custom_image, model_tt::{constant, theme::IMAGE_HOMESCREEN}, + translations::tr, }, }; @@ -87,7 +88,7 @@ where if !usb_configured() { let (color, icon) = Self::level_to_style(0); Some(HomescreenNotification { - text: "NO USB CONNECTION", + text: tr("homescreen__title_no_usb_connection"), icon, color, }) @@ -106,7 +107,7 @@ where fn paint_loader(&mut self) { display::text_center( TOP_CENTER + Offset::y(HOLD_Y), - "HOLD TO LOCK", + tr("homescreen__title_hold_to_lock"), Font::BOLD, theme::FG, theme::BG, @@ -302,9 +303,15 @@ where fn paint(&mut self) { let (locked, tap) = if self.bootscreen { - ("NOT CONNECTED", "Tap to connect") + ( + tr("lockscreen__title_not_connected"), + tr("lockscreen__tap_to_connect"), + ) } else { - ("LOCKED", "Tap to unlock") + ( + tr("lockscreen__title_locked"), + tr("lockscreen__tap_to_unlock"), + ) }; let mut label_style = theme::TEXT_DEMIBOLD; diff --git a/core/embed/rust/src/ui/model_tt/component/mod.rs b/core/embed/rust/src/ui/model_tt/component/mod.rs index 4a9daaa06..ec6d1b0b7 100644 --- a/core/embed/rust/src/ui/model_tt/component/mod.rs +++ b/core/embed/rust/src/ui/model_tt/component/mod.rs @@ -1,6 +1,8 @@ +#[cfg(feature = "translations")] mod address_details; pub mod bl_confirm; mod button; +#[cfg(feature = "translations")] mod coinjoin_progress; mod dialog; mod fido; @@ -12,7 +14,9 @@ mod frame; mod homescreen; mod keyboard; mod loader; +#[cfg(feature = "translations")] mod number_input; +#[cfg(feature = "translations")] mod page; mod progress; mod result; @@ -21,11 +25,13 @@ mod simple_page; mod swipe; mod welcome_screen; +#[cfg(feature = "translations")] pub use address_details::AddressDetails; pub use button::{ Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet, CancelConfirmMsg, CancelInfoConfirmMsg, IconText, SelectWordMsg, }; +#[cfg(feature = "translations")] pub use coinjoin_progress::CoinJoinProgress; pub use dialog::{Dialog, DialogMsg, IconDialog}; pub use error::ErrorScreen; @@ -42,7 +48,9 @@ pub use keyboard::{ word_count::{SelectWordCount, SelectWordCountMsg}, }; pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet}; +#[cfg(feature = "translations")] pub use number_input::{NumberInputDialog, NumberInputDialogMsg}; +#[cfg(feature = "translations")] pub use page::ButtonPage; pub use progress::Progress; pub use result::{ResultFooter, ResultScreen, ResultStyle}; diff --git a/core/embed/rust/src/ui/model_tt/component/number_input.rs b/core/embed/rust/src/ui/model_tt/component/number_input.rs index 8edb37bbc..f9f7d1329 100644 --- a/core/embed/rust/src/ui/model_tt/component/number_input.rs +++ b/core/embed/rust/src/ui/model_tt/component/number_input.rs @@ -9,6 +9,7 @@ use crate::{ }, display::{self, Font}, geometry::{Grid, Insets, Offset, Rect}, + translations::tr, }, }; @@ -45,8 +46,8 @@ where input: NumberInput::new(min, max, init_value).into_child(), paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, text)).into_child(), paragraphs_pad: Pad::with_background(theme::BG), - info_button: Button::with_text("INFO").into_child(), - confirm_button: Button::with_text("CONTINUE") + info_button: Button::with_text(tr("buttons__info")).into_child(), + confirm_button: Button::with_text(tr("buttons__continue")) .styled(theme::button_confirm()) .into_child(), } diff --git a/core/embed/rust/src/ui/model_tt/component/page.rs b/core/embed/rust/src/ui/model_tt/component/page.rs index f2b3be19e..087c6ab21 100644 --- a/core/embed/rust/src/ui/model_tt/component/page.rs +++ b/core/embed/rust/src/ui/model_tt/component/page.rs @@ -5,6 +5,7 @@ use crate::{ constant, display::{self, Color}, geometry::{Insets, Rect}, + translations::tr, util::animation_disabled, }, }; @@ -88,8 +89,8 @@ where } pub fn with_hold(mut self) -> Self { - self.button_confirm = - Button::with_text("HOLD TO CONFIRM".into()).styled(theme::button_confirm()); + self.button_confirm = Button::with_text(tr("buttons__hold_to_confirm").into()) + .styled(theme::button_confirm()); self.loader = Some(Loader::new()); self } diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 7b3edd7a0..ee6a8797b 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -40,6 +40,7 @@ use crate::{ util::{iter_into_array, upy_disable_animation, ConfirmBlob, PropsList}, }, model_tt::component::check_homescreen_format, + translations::tr, }, }; @@ -577,7 +578,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let description: Option = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; - let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?; + let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, tr("buttons__confirm").into())?; let extra: Option = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?; let data: Obj = kwargs.get(Qstr::MP_QSTR_data)?; let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?; @@ -630,7 +631,7 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold() } else { ButtonPage::new(paragraphs.into_paragraphs(), theme::BG) - .with_cancel_confirm(None, Some("CONFIRM".into())) + .with_cancel_confirm(None, Some(tr("buttons__confirm").into())) }; let obj = LayoutObj::new(Frame::left_aligned(theme::label_title(), title, page))?; Ok(obj.into()) @@ -663,7 +664,7 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m _ => return Err(value_error!("Invalid image.")), }; - let buttons = Button::cancel_confirm_text(None, Some("CHANGE")); + let buttons = Button::cancel_confirm_text(None, Some(tr("buttons__change"))); let obj = LayoutObj::new(Frame::centered( theme::label_title(), title, @@ -683,12 +684,17 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: let paragraphs = Paragraphs::new([ Paragraph::new( &theme::TEXT_NORMAL, - StrBuffer::from( - "By continuing you agree\nto Trezor Company's\nterms and conditions.\r", - ), + StrBuffer::from(tr("reset__by_continuing")), + ) + .with_bottom_padding(17), // simulating a carriage return + Paragraph::new( + &theme::TEXT_NORMAL, + StrBuffer::from(tr("reset__more_info_at")), + ), + Paragraph::new( + &theme::TEXT_DEMIBOLD, + StrBuffer::from(tr("reset__tos_link")), ), - Paragraph::new(&theme::TEXT_NORMAL, StrBuffer::from("More info at")), - Paragraph::new(&theme::TEXT_DEMIBOLD, StrBuffer::from("trezor.io/tos")), ]); let buttons = Button::cancel_confirm( Button::with_icon(theme::ICON_CANCEL), @@ -840,23 +846,23 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: let amount_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_new)?.try_into()?; let description = if sign < 0 { - "Decrease amount by:" + tr("modify_amount__decrease_amount") } else { - "Increase amount by:" + tr("modify_amount__increase_amount") }; let paragraphs = Paragraphs::new([ Paragraph::new(&theme::TEXT_NORMAL, description.into()), Paragraph::new(&theme::TEXT_MONO, amount_change), - Paragraph::new(&theme::TEXT_NORMAL, "New amount:".into()), + Paragraph::new(&theme::TEXT_NORMAL, tr("modify_amount__new_amount").into()), Paragraph::new(&theme::TEXT_MONO, amount_new), ]); let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - "MODIFY AMOUNT", + tr("modify_amount__title"), ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG) - .with_cancel_confirm(Some("^".into()), Some("CONTINUE".into())), + .with_cancel_confirm(Some("^".into()), Some(tr("buttons__continue").into())), ))?; Ok(obj.into()) }; @@ -871,12 +877,20 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m let total_fee_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_fee_new)?.try_into()?; let (description, change, total_label) = match sign { - s if s < 0 => ("Decrease fee by:", user_fee_change, "New transaction fee:"), - s if s > 0 => ("Increase fee by:", user_fee_change, "New transaction fee:"), + s if s < 0 => ( + tr("modify_fee__decrease_fee"), + user_fee_change, + tr("modify_fee__new_transaction_fee"), + ), + s if s > 0 => ( + tr("modify_fee__increase_fee"), + user_fee_change, + tr("modify_fee__new_transaction_fee"), + ), _ => ( - "Fee did not change.\r", + tr("modify_fee__no_change"), StrBuffer::empty(), - "Transaction fee:", + tr("modify_fee__transaction_fee"), ), }; @@ -910,7 +924,7 @@ fn new_show_modal( let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let value: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_value, StrBuffer::empty())?; let description: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_description, StrBuffer::empty())?; - let button: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_button, "CONTINUE".into())?; + let button: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_button, tr("buttons__continue").into())?; let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?; let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?; @@ -1004,7 +1018,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map let controls = Button::cancel_confirm( Button::with_icon(theme::ICON_CANCEL), - Button::with_text("CONFIRM").styled(theme::button_confirm()), + Button::with_text(tr("buttons__confirm")).styled(theme::button_confirm()), true, ); @@ -1061,9 +1075,9 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) - extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let description: StrBuffer = "Please contact Trezor support at".into(); - let url: StrBuffer = "trezor.io/support".into(); - let button = "QUIT"; + let description: StrBuffer = tr("addr_mismatch__contact_support").into(); + let url: StrBuffer = tr("addr_mismatch__support_url").into(); + let button = tr("buttons__quit"); let icon = BlendedImage::new( theme::IMAGE_BG_OCTAGON, @@ -1211,15 +1225,15 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut let max_feerate: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_feerate)?.try_into()?; let paragraphs = Paragraphs::new([ - Paragraph::new(&theme::TEXT_NORMAL, "Max rounds".into()), + Paragraph::new(&theme::TEXT_NORMAL, tr("coinjoin__max_rounds").into()), Paragraph::new(&theme::TEXT_MONO, max_rounds), - Paragraph::new(&theme::TEXT_NORMAL, "Max mining fee".into()), + Paragraph::new(&theme::TEXT_NORMAL, tr("coinjoin__max_mining_fee").into()), Paragraph::new(&theme::TEXT_MONO, max_feerate), ]); let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - "AUTHORIZE COINJOIN", + tr("coinjoin__title"), ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG).with_hold(), ))?; Ok(obj.into()) @@ -1234,7 +1248,7 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?; let warning: bool = kwargs.get_or(Qstr::MP_QSTR_wrong_pin, false)?; let warning = if warning { - Some("Wrong PIN".into()) + Some(tr("pin__wrong_pin").into()) } else { None }; @@ -1399,9 +1413,9 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut .with_spacing(theme::RECOVERY_SPACING); let notification = if dry_run { - "BACKUP CHECK" + tr("recovery__title_dry_run") } else { - "RECOVER WALLET" + tr("recovery__title") }; let obj = if info_button { @@ -1410,7 +1424,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut notification, Dialog::new( paragraphs, - Button::cancel_info_confirm("CONTINUE", "MORE INFO"), + Button::cancel_info_confirm(tr("buttons__continue"), tr("buttons__more_info")), ), ))? } else { @@ -1429,14 +1443,14 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu let block = move |_args: &[Obj], kwargs: &Map| { let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?; let title = if dry_run { - "BACKUP CHECK" + tr("recovery__title_dry_run") } else { - "RECOVER WALLET" + tr("recovery__title") }; let paragraphs = Paragraphs::new(Paragraph::new( &theme::TEXT_DEMIBOLD, - StrBuffer::from("Select the number of words in your backup."), + StrBuffer::from(tr("recovery__select_num_of_words")), )); let obj = LayoutObj::new(Frame::left_aligned( @@ -1460,7 +1474,7 @@ extern "C" fn new_show_group_share_success( let obj = LayoutObj::new(IconDialog::new_shares( lines, - theme::button_bar(Button::with_text("CONTINUE").map(|msg| { + theme::button_bar(Button::with_text(tr("buttons__continue")).map(|msg| { (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) })), ))?; @@ -1483,9 +1497,9 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs: let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - "REMAINING SHARES", + tr("recovery__title_remaining_shares"), ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG) - .with_cancel_confirm(None, Some("CONTINUE".into())) + .with_cancel_confirm(None, Some(tr("buttons__continue").into())) .with_confirm_style(theme::button_default()) .without_cancel(), ))?; @@ -1600,16 +1614,16 @@ extern "C" fn new_confirm_firmware_update( let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?; - let title_str = StrBuffer::from("UPDATE FIRMWARE"); + let title_str = StrBuffer::from(tr("firmware_update__title")); let title = Label::left_aligned(title_str, theme::TEXT_BOLD).vertically_centered(); let msg = Label::left_aligned(description, theme::TEXT_NORMAL); - let left = Button::with_text("CANCEL").styled(theme::button_default()); - let right = Button::with_text("INSTALL").styled(theme::button_confirm()); + let left = Button::with_text(tr("buttons__cancel")).styled(theme::button_default()); + let right = Button::with_text(tr("buttons__install")).styled(theme::button_confirm()); let obj = LayoutObj::new( Confirm::new(theme::BG, left, right, ConfirmTitle::Text(title), msg).with_info( - "FW FINGERPRINT".into(), + tr("firmware_update__title_fingerprint").into(), fingerprint, theme::button_moreinfo(), ), diff --git a/core/embed/rust/src/ui/translations/crowdin.py b/core/embed/rust/src/ui/translations/crowdin.py new file mode 100644 index 000000000..1ac121fd1 --- /dev/null +++ b/core/embed/rust/src/ui/translations/crowdin.py @@ -0,0 +1,55 @@ +import subprocess +import tempfile +from pathlib import Path +import json +import sys +import os + +HERE = Path(__file__).parent + + +def download() -> None: + with tempfile.TemporaryDirectory() as temp_dir: + command = f"crowdin download --all --verbose --token $CROWDIN_TOKEN --base-path={temp_dir}" + print("command", command) + + subprocess.run(command, shell=True, check=True) + + for directory in Path(temp_dir).iterdir(): + print("directory", directory) + lang_name = directory.name + en_file = directory / "en.json" + if not en_file.exists(): + print("Skipping - no en.json inside", lang_name) + continue + print("Processing", lang_name) + data = json.loads(en_file.read_text()) + lang_file = HERE / f"{lang_name}.json" + if not lang_file.exists(): + print("Skipping - no lang_file on our side", lang_name) + continue + lang_file_data = json.loads(lang_file.read_text()) + lang_file_data["translations"] = data["translations"] + lang_file.write_text(json.dumps(lang_file_data, indent=2, sort_keys=True, ensure_ascii=False) + "\n") + print("Translations updated", lang_name) + + +def upload() -> None: + command = "crowdin upload sources --token $CROWDIN_TOKEN" + print("command", command) + + subprocess.run(command, shell=True, check=True) + + +if __name__ == "__main__": + if not os.environ.get("CROWDIN_TOKEN"): + print("CROWDIN_TOKEN env variable not set") + sys.exit(1) + + if "download" in sys.argv: + download() + elif "upload" in sys.argv: + upload() + else: + print("Usage: python crowdin.py [download|upload]") + sys.exit(1) diff --git a/core/embed/rust/src/ui/translations/crowdin.yml b/core/embed/rust/src/ui/translations/crowdin.yml new file mode 100644 index 000000000..410bae43a --- /dev/null +++ b/core/embed/rust/src/ui/translations/crowdin.yml @@ -0,0 +1,12 @@ +"project_id" : "625982" +"base_path" : "." +# api_token is being supplied on via `--token $CROWDIN_TOKEN` CLI option +"base_url" : "https://api.crowdin.com" +"preserve_hierarchy": true + +files: [ + { + "source" : "en.json", + "translation": "%two_letters_code%/%original_file_name%", + } +] diff --git a/core/embed/rust/src/ui/translations/cs.json b/core/embed/rust/src/ui/translations/cs.json new file mode 100644 index 000000000..33d9e1496 --- /dev/null +++ b/core/embed/rust/src/ui/translations/cs.json @@ -0,0 +1,957 @@ +{ + "font": { + "Safe 3": { + "1_FONT_NORMAL": "font_pixeloperator_regular_8_cs.json", + "2_FONT_BOLD": "font_pixeloperator_bold_8_cs.json", + "3_FONT_MONO": "font_pixeloperatormono_regular_8_cs.json", + "4_FONT_BIG": "font_unifont_regular_16_cs.json", + "5_FONT_DEMIBOLD": "font_unifont_bold_16_cs.json" + }, + "T": { + "1_FONT_NORMAL": "font_tthoves_regular_21_cs.json", + "2_FONT_BOLD": "font_tthoves_bold_17_cs.json", + "3_FONT_MONO": "font_robotomono_medium_20_cs.json", + "4_FONT_BIG": null, + "5_FONT_DEMIBOLD": "font_tthoves_demibold_21_cs.json" + } + }, + "header": { + "change_language_prompt": "Zmenit jazyk na cestinu?", + "change_language_title": "ZMENA JAZYKA", + "language": "cs", + "version": "2.6.4" + }, + "translations": { + "addr_mismatch": { + "contact_support": "Prosím, obraťte se na podporu Trezor na", + "key_mismatch": "Nesouhlasí klíč?", + "mismatch": "Nesouhlasí adresa?", + "support_url": "trezor.io/support", + "title": "NESOUHLASÍ ADRESA?", + "title_key_mismatch": "NESOUHLASÍ KLÍČ?", + "wrong_derication_path": "Špatná derivační cesta pro zvolený účet.", + "xpub_mismatch": "Nesouhlasí XPUB?" + }, + "address": { + "address": "Adresa:", + "public_key": "Veřejný klíč", + "title_cosigner": "SPOLUPODEPSÁNÍ", + "title_receive_address": "ADRESA PRO PŘIJETÍ", + "title_yours": "VAŠE" + }, + "address_details": { + "account": "Účet:", + "derivation_path": "Cesta odvození:", + "title_receive_address": "PŘIJMOUT ADRESU", + "title_receiving_to": "PŘIJÍMÁNÍ DO" + }, + "authenticate": { + "confirm_template": "Povolit připojený počítač potvrdit váš {} je pravý?", + "header": "Ověřené zařízení" + }, + "auto_lock": { + "change_template": "Automaticky zamknout Trezor po {} nečinnosti?", + "title": "AUTO-ZÁMKOVÁNÍ" + }, + "backup": { + "can_back_up_anytime": "Svůj Trezor můžete zálohovat jednou, kdykoli.", + "it_should_be_backed_up": "Měli byste nyní zálohovat svou novou peněženku.", + "it_should_be_backed_up_now": "Proveďte zálohu!", + "new_wallet_created": "Nová peněženka vytvořena.", + "new_wallet_successfully_created": "Nová peněženka úspěšně vytvořena.", + "recover_anytime": "Kdykoli můžete použít zálohu k obnovení peněženky.", + "title_backup_wallet": "ZÁLOHOVAT PENĚŽENKU", + "title_skip": "PŘESKOČIT ZÁLOHU", + "want_to_skip": "Jste si jisti, že chcete přeskočit zálohu?" + }, + "binance": { + "buy": "Koupit", + "confirm_cancel": "Potvrdit zrušení", + "confirm_input": "Potvrdit vstup", + "confirm_order": "Potvrdit objednávku", + "confirm_output": "Potvrdit výstup", + "order_id": "ID objednávky:", + "pair": "Pár:", + "price": "Cena:", + "quantity": "Množství:", + "sell": "Prodat", + "sender_address": "Adresa odesílatele:", + "side": "Strana:", + "unknown": "Neznámý" + }, + "bitcoin": { + "commitment_data": "Data závazku:", + "confirm_locktime": "Potvrdit locktime", + "create_proof_of_ownership": "Chcete vytvořit důkaz vlastnictví?", + "high_mining_fee_template": "Poplatek za těžbu\n{}\nje neočekávaně vysoký.", + "locktime_no_effect": "Locktime je nastaven, ale nebude mít žádný účinek.", + "locktime_set_to": "Locktime nastaven:", + "locktime_set_to_blockheight": "Locktime nastaven na výšku bloku:", + "lot_of_change_outputs": "Mnoho výstupů změn.", + "multiple_accounts": "Více účtů", + "new_fee_rate": "Nová sazba poplatku:", + "simple_send_of": "Jednoduché odeslání", + "ticket_amount": "Částka lístku:", + "title_confirm_details": "POTVRDIT DETAILY", + "title_finalize_transaction": "DOKONČIT TRANSAKCI", + "title_high_mining_fee": "VYSOKÝ POPLATEK ZA TĚŽBU", + "title_meld_transaction": "SPOJIT TRANSAKCE", + "title_modify_amount": "UPRAVIT ČÁSTKU", + "title_payjoin": "PAYJOIN", + "title_proof_of_ownership": "DŮKAZ VLASTNICTVÍ", + "title_purchase_ticket": "KOUPIT LÍSTEK", + "title_update_transaction": "AKTUALIZOVAT TRANSAKCI", + "unknown_path": "Neznámá cesta", + "unknown_transaction": "Neznámá transakce", + "unusually_high_fee": "Neobvykle vysoký poplatek.", + "unverified_external_inputs": "Transakce obsahuje neověřené vnější vstupy.", + "valid_signature": "Podpis je platný.", + "voting_rights": "Právo hlasovat pro:" + }, + "buttons": { + "abort": "ZRUŠIT", + "access": "PŘÍSTUP", + "again": "ZNOVU", + "allow": "DOVOLIT", + "back_up": "ZÁLOHOVAT", + "cancel": "ZRUŠIT", + "change": "ZMĚNIT", + "check": "ZKONTROLOVAT", + "check_again": "ZKONTROLOVAT ZNOVU", + "close": "ZAVŘÍT", + "confirm": "POTVRDIT", + "continue": "POKRAČOVAT", + "details": "DETAILY", + "enable": "POVOLIT", + "enter": "ZADAT", + "enter_share": "ZADAT PODÍL", + "export": "EXPORTOVAT", + "format": "FORMÁTOVAT", + "go_back": "VRÁTIT", + "hold_to_confirm": "PODRŽTE", + "info": "INFORMACE", + "install": "NAINSTALUJTE", + "more_info": "VÍCE INFORMACÍ", + "ok_i_understand": "OK, CHÁPU", + "purchase": "KOUPI", + "quit": "UKONČIT", + "restart": "RESTARTOVAT", + "retry": "ZNOVU", + "select": "VYBRAT", + "set": "NASTAVIT", + "show_all": "UKÁZAT VŠE", + "show_words": "UKÁZAT SLOVA", + "skip": "PŘESKOČIT", + "try_again": "ZKUSIT ZNOVU", + "turn_off": "VYPNOUT", + "turn_on": "ZAPNOUT" + }, + "cardano": { + "addr_base": "Základ", + "addr_enterprise": "Podnik", + "addr_legacy": "Dědictví", + "addr_pointer": "Ukazatel", + "addr_reward": "Odměna", + "address_no_staking": "adresa - žádné odměny za stávkování.", + "amount": "Částka:", + "amount_burned_decimals_unknown": "Spálená částka:", + "amount_minted_decimals_unknown": "Vytvořená částka:", + "amount_sent_decimals_unknown": "Odeslána částka (desetinná čára neznámá):", + "anonymous_pool": "Pool nemá metadata (anonymní pool)", + "asset_fingerprint": "Otisk aktiv:", + "auxiliary_data_hash": "Hash pomocných dat:", + "block": "Blok", + "catalyst": "Katalyzátor", + "certificate": "Certifikát", + "certificate_path": "Cesta certifikátu", + "change_output": "Změnit výstup", + "change_output_path": "Cesta změny výstupu", + "change_output_staking_path": "Cesta stávkování změny výstupu", + "check_all_items": "Pečlivě zkontrolujte všechny položky.", + "choose_level_of_details": "Vyberte úroveň detailů:", + "collateral_input_id": "ID vstupu zajištění:", + "collateral_input_index": "Index vstupu zajištění:", + "collateral_output_contains_tokens": "Výstup zajištění obsahuje tokeny.", + "collateral_return": "Návrat zajištění", + "confirm": "Potvrdit:", + "confirm_signing_stake_pool": "Potvrdit podepsání registrace stávkového poolu jako vlastníka.", + "confirm_transaction": "Potvrdit transakci", + "confirming_a_multisig_transaction": "Potvrzování multisig transakce.", + "confirming_pool_registration": "Potvrzení registrace stávkového poolu.", + "confirming_transction": "Potvrzení transakce.", + "cost": "Náklady", + "credential_mismatch": "Pověření neodpovídá platebnímu pověření.", + "datum_hash": "Datum hash:", + "delegating_to": "Delegace na:", + "for_account_and_index_template": "pro účet {} a index {}:", + "for_account_template": "pro účet {}:", + "for_key_hash": "pro hash klíče:", + "for_script": "pro skript:", + "inline_datum": "Vložená data", + "input_id": "ID vstupu:", + "input_index": "Index vstupu:", + "intro_text_address": "Adresa", + "intro_text_change": "Následující adresa je adresa změny. Její", + "intro_text_owned_by_device": "Následující adresa patří tomuto zařízení. Její", + "intro_text_registration_payment": "Adresa platby za registraci hlasovacího klíče patří tomuto zařízení. Její", + "key_hash": "hash klíče", + "margin": "Marže", + "multisig_path": "cesta multi-podpisu", + "nested_scripts_template": "Obsahuje {} vnořené skripty.", + "network": "Síť:", + "no_output_tx": "Transakce nemá výstupy, síť nelze ověřit.", + "nonce": "Nonce:", + "other": "jiné", + "path": "cesta", + "pledge": "Záruka", + "pointer": "ukazatel", + "policy_id": "ID politiky:", + "pool_metadata_hash": "Hash metadat poolu:", + "pool_metadata_url": "URL metadat poolu:", + "pool_owner": "Vlastník poolu:", + "pool_owner_path": "Cesta stávkování vlastníka poolu", + "pool_reward_account": "Účet odměn poolu:", + "reference_input_id": "ID referenčního vstupu:", + "reference_input_index": "Index referenčního vstupu:", + "reference_script": "Referenční skript", + "required_signer": "Požadovaný podepsaný", + "reward": "odměna", + "reward_address": "Adresa je odměna.", + "reward_eligibility_warning": "Varování: Adresa není platná adresa pro platby, není způsobilá pro odměny.", + "rewards_go_to": "Odměny jdou na:", + "script": "skript", + "script_all": "Vše", + "script_any": "Jakýkoliv", + "script_data_hash": "Hash dat skriptu:", + "script_hash": "Skript hash:", + "script_invalid_before": "Neplatné před", + "script_invalid_hereafter": "Neplatné odteď", + "script_key": "Klíč", + "script_n_of_k": "N z K", + "script_reward": "odměna skriptu", + "sending": "Odesílání", + "show_simple": "Zobrazit jednoduše", + "sign_tx_path_template": "Podepsat transakci pomocí {}:", + "stake_delegation": "Delegace stávky", + "stake_deregistration": "Zrušení registrace stávkového klíče", + "stake_pool_registration": "Registrace stávkového poolu", + "stake_pool_registration_pool_id": "Registrace stávkového poolu\nID poolu:", + "stake_registration": "Registrace stávkového klíče", + "staking_key_for_account": "Stávkový klíč pro účet", + "to_pool": "do poolu:", + "token_minting_path": "cesta ražby tokenů", + "total_collateral": "Celkové zajištění", + "transaction": "Transakce", + "transaction_contains_minting_or_burning": "Transakce obsahuje ražbu nebo spálení tokenů.", + "transaction_contains_script_address_no_datum": "Následující výstup transakce obsahuje skriptovou adresu, ale neobsahuje data.", + "transaction_fee": "Poplatek za transakci:", + "transaction_id": "ID transakce:", + "transaction_no_collateral_input": "Transakce neobsahuje vstupy zajištění. Skript Plutus nebude moci běžet.", + "transaction_no_script_data_hash": "Transakce neobsahuje hash dat skriptu. Skript Plutus nebude moci běžet.", + "transaction_output_contains_tokens": "Následující výstup transakce obsahuje tokeny.", + "ttl": "TTL:", + "unknown": "Neznámý", + "unknown_collateral_amount": "Neznámá částka zajištění.", + "unusual_path": "Neobvyklá cesta.", + "valid_since": "Platné od:", + "verify_script": "Ověřit skript", + "vote_key_registration": "Registrace hlasovacího klíče (CIP-36)", + "vote_public_key": "Veřejný klíč hlasování:", + "voting_purpose": "Účel hlasování:", + "warning": "Varování", + "weight": "Váha:", + "withdrawal_for_address_template": "Potvrdit výběr pro adresu {}:", + "witness_path": "Cesta svědka", + "x_of_y_signatures_template": "Vyžaduje {} z {} podpisů." + }, + "coinjoin": { + "access_account": "Přístup k vašemu coinjoin účtu?", + "do_not_disconnect": "Neodpojujte svůj Trezor!", + "max_mining_fee": "Maximální poplatek za těžbu:", + "max_rounds": "Maximální počet kol:", + "title": "AUTORIZOVAT COINJOIN", + "title_do_not_disconnect": "NEODPOJUJTE SVŮJ TREZOR!", + "title_progress": "COINJOIN V PRŮBĚHU", + "waiting_for_others": "Čekání na ostatní" + }, + "confirm_total": { + "account": "Účet:", + "fee_rate": "Sazba poplatku:", + "sending_from_account": "Odesílání z účtu:", + "title_fee": "INFORMACE O POPLATKU", + "title_sending_from": "ODESLÁNÍ OD" + }, + "debug": { + "loading_seed": "Načítání seedu", + "loading_seed_not_recommended": "Načítání soukromého seedu není doporučeno." + }, + "device_name": { + "change_template": "Změnit název zařízení na {}?", + "title": "NÁZEV ZAŘÍZENÍ" + }, + "entropy": { + "send": "Opravdu chcete odeslat entropii?", + "title": "VNITŘNÍ ENTROPIE", + "title_confirm": "POTVRDIT ENTROPII" + }, + "eos": { + "about_to_sign_template": "Chystáte se podepsat {}.", + "account": "Účet:", + "action_name": "Název akce:", + "amount": "Částka:", + "arbitrary_data": "Libovolná data", + "buy_ram": "Koupit RAM", + "bytes": "Byty:", + "cancel_vote": "Zrušit hlasování", + "checksum": "Kontrolní součet:", + "code": "Kód:", + "contract": "Smlouva:", + "cpu": "CPU:", + "creator": "Tvůrce:", + "delegate": "Delegovat", + "delete_auth": "Smazat ověření", + "from": "Od:", + "link_auth": "Propojit ověření", + "memo": "Poznámka", + "name": "Jméno:", + "net": "NET:", + "new_account": "Nový účet", + "no": "Ne", + "owner": "Vlastník:", + "parent": "Rodič:", + "payer": "Plátce:", + "permission": "Oprávnění:", + "proxy": "Proxy:", + "receiver": "Příjemce:", + "refund": "Vrátit peníze", + "requirement": "Požadavek:", + "sell_ram": "Prodat RAM", + "sender": "Odesílatel:", + "sign_transaction": "Podepsat transakci", + "threshold": "Práh:", + "to": "Do:", + "transfer": "Převod:", + "type": "Typ:", + "undelegate": "Zrušit delegaci", + "unlink_auth": "Odpojit ověření", + "update_auth": "Aktualizovat ověření", + "vote_for_producers": "Hlasovat pro producenty", + "vote_for_proxy": "Hlasovat pro proxy", + "voter": "Volitel:", + "yes": "Ano" + }, + "ethereum": { + "amount_sent": "Odeslaná částka:", + "confirm_fee": "Potvrdit poplatek", + "contract": "Smlouva:", + "data_size_template": "Velikost: {} bajtů", + "gas_limit": "Limit plynu:", + "gas_price": "Cena plynu:", + "max_gas_price": "Maximální cena plynu:", + "name_and_version": "Jméno a verze", + "new_contract": "nová smlouva?", + "no_message_field": "Žádné pole zprávy", + "priority_fee": "Prioritní poplatek:", + "show_full_array": "Zobrazit celé pole", + "show_full_domain": "Zobrazit celou doménu", + "show_full_message": "Zobrazit celou zprávu", + "show_full_struct": "Zobrazit celou strukturu", + "sign_eip712": "Opravdu podepsat data podle EIP-712?", + "title_confirm_data": "POTVRDIT DATA", + "title_confirm_domain": "POTVRDIT DOMÉNU", + "title_confirm_message": "POTVRDIT ZPRÁVU", + "title_confirm_struct": "POTVRDIT STRUKTURU", + "title_confirm_typed_data": "POTVRDIT ZAPSANÁ DATA", + "title_signing_address": "ADRESA PRO PODEPSÁNÍ", + "units_template": "{} jednotek", + "unknown_token": "Neznámý token", + "valid_signature": "Podpis je platný." + }, + "experimental_mode": { + "enable": "Povolit experimentální funkce?", + "only_for_dev": "Pouze pro vývoj a beta testování!", + "title": "EXPERIMENTÁLNÍ REŽIM" + }, + "fido": { + "already_registered": "Již zaregistrováno", + "device_already_registered": "Toto zařízení je již zaregistrováno s touto aplikací.", + "device_already_registered_with_template": "Toto zařízení je již zaregistrováno s {}.", + "device_not_registered": "Toto zařízení není zaregistrováno s touto aplikací.", + "does_not_belong": "Kredence nepatří tomuto autentikátoru.", + "erase_credentials": "Opravdu chcete smazat všechny pověření?", + "export_credentials": "Exportovat informace z tohoto autentikátoru?", + "not_registered": "Není zaregistrováno", + "not_registered_with_template": "Toto zařízení není zaregistrováno s\n{}.", + "please_enable_pin_protection": "Prosím, povolte ochranu PIN.", + "title_authenticate": "FIDO2 AUTENTIZACE", + "title_import_credential": "IMPORTOVAT KREDENCE", + "title_list_credentials": "UKAŽ KREDENCE", + "title_register": "FIDO2 REGISTRACE", + "title_remove_credential": "ODSTRANIT KREDENCE", + "title_reset": "RESET FIDO2", + "title_u2f_auth": "U2F AUTENTIZACE", + "title_u2f_register": "U2F REGISTRACE", + "title_verify_user": "FIDO2 OVĚŘIT UŽIVATELE", + "unable_to_verify_user": "Nelze ověřit uživatele.", + "wanna_erase_credentials": "Opravdu chcete smazat všechny přihlašovací údaje?" + }, + "firmware_update": { + "title": "AKTUALIZOVAT FIRMWARE", + "title_fingerprint": "FW OTISKY PRSTŮ" + }, + "homescreen": { + "click_to_connect": "Klikněte pro připojení", + "click_to_unlock": "Klikněte pro odemčení", + "title_backup_failed": "ZÁLOHA SELHALA", + "title_backup_needed": "ZÁLOHA POTŘEBNÁ", + "title_coinjoin_authorized": "COINJOIN AUTORIZOVÁNO", + "title_experimental_mode": "EXPERIMENTÁLNÍ REŽIM", + "title_hold_to_lock": "DRŽET PRO ZAMKNUTÍ", + "title_no_usb_connection": "ŽÁDNÉ USB PŘIPOJENÍ", + "title_pin_not_set": "PIN NEBYL NASTAVEN", + "title_seedless": "BEZ SEMÍNKA", + "title_set": "ZMĚNIT ÚVODNÍ OBRAZOVKU?" + }, + "inputs": { + "back": "ZPĚT", + "cancel": "ZRUŠIT", + "delete": "SMAZAT", + "enter": "ZADAT", + "return": "VRÁTIT", + "show": "UKÁZAT", + "space": "MEZERNÍK" + }, + "joint": { + "title": "SPOLEČNÁ TRANSAKCE", + "to_the_total_amount": "Celkové částce:", + "you_are_contributing": "Přispíváte:" + }, + "lockscreen": { + "tap_to_connect": "Klepněte pro připojení", + "tap_to_unlock": "Klepněte pro odemčení", + "title_locked": "ZAMKNUTO", + "title_not_connected": "NEPŘIPOJENO" + }, + "misc": { + "decrypt_value": "Dešifrovat hodnotu", + "encrypt_value": "Zašifrovat hodnotu", + "title_suite_labeling": "OZNAČENÍ SUITE" + }, + "modify_amount": { + "address": "Adresa:", + "decrease_amount": "Snížit částku o:", + "increase_amount": "Zvýšit částku o:", + "new_amount": "Nová částka:", + "title": "UPRAVIT ČÁSTKU" + }, + "modify_fee": { + "decrease_fee": "Snížit poplatek o:", + "fee_rate": "Sazba poplatku:", + "increase_fee": "Zvýšit poplatek o:", + "new_transaction_fee": "Nový transakční poplatek:", + "no_change": "Váš poplatek se nezměnil.", + "title": "UPRAVIT POPLATEK", + "transaction_fee": "Transakční poplatek:" + }, + "monero": { + "confirm_export": "Potvrdit export", + "confirm_fee": "Potvrdit poplatek", + "confirm_ki_sync": "Potvrdit synchronizaci klíčových obrázků", + "confirm_refresh": "Potvrdit obnovení", + "confirm_unlock_time": "Potvrdit čas odemknutí", + "hashing_inputs": "Hašování vstupů", + "payment_id": "ID platby", + "postprocessing": "Dokončování zpracování...", + "processing": "Zpracovávání...", + "processing_inputs": "Zpracování vstupů", + "processing_outputs": "Zpracování výstupů", + "signing": "Podepisování...", + "signing_inputs": "Podepisování vstupů", + "unlock_time_set_template": "Čas odemknutí této transakce je nastaven na {}", + "wanna_export_tx_der": "Opravdu chcete exportovat tx_der pro tx_proof?", + "wanna_export_tx_key": "Opravdu chcete exportovat tx_key?", + "wanna_export_watchkey": "Opravdu chcete exportovat pouze sledovací pověření?", + "wanna_start_refresh": "Opravdu chcete spustit obnovení?", + "wanna_sync_key_images": "Opravdu chcete synchronizovat klíčové obrázky?" + }, + "nem": { + "absolute": "absolutní", + "activate": "Aktivovat", + "add": "Přidat", + "confirm_action": "Potvrdit akci", + "confirm_address": "Potvrdit adresu", + "confirm_creation_fee": "Potvrdit poplatek za vytvoření", + "confirm_fee": "Potvrdit poplatek", + "confirm_mosaic": "Potvrdit mozaiku", + "confirm_multisig_fee": "Potvrdit vícepodepisový poplatek", + "confirm_namespace": "Potvrdit jmenný prostor", + "confirm_payload": "Potvrdit náklad", + "confirm_properties": "Potvrdit vlastnosti", + "confirm_rental_fee": "Potvrdit nájemné", + "confirm_transfer_of": "Potvrdit převod", + "convert_account_to_multisig": "Převést účet na vícepodepisový účet?", + "cosign_transaction_for": "Spolupodepsat transakci pro", + "cosignatory": " spolupodepisovatel", + "create_mosaic": "Vytvořit mozaiku", + "create_namespace": "Vytvořit jmenný prostor", + "deactivate": "Deaktivovat", + "decrease": "Snížit", + "description": "Popis:", + "divisibility_and_levy_cannot_be_shown": "Dělitelnost a zpoplatnění nelze zobrazit pro neznámé mozaiky", + "encrypted": "Zašifrované:", + "final_confirm": "Konečné potvrzení", + "immutable": "neměnitelný", + "increase": "Zvýšit", + "initial_supply": "Počáteční zásoba:", + "initiate_transaction_for": "Zahájit transakci pro", + "levy_divisibility": "Dělitelnost zpoplatnění:", + "levy_fee": "Poplatek za zpoplatnění:", + "levy_fee_of": "Poplatek ze zpoplatnění:", + "levy_mosaic": "Mozaika zpoplatnění:", + "levy_namespace": "Jmenný prostor zpoplatnění:", + "levy_recipient": "Příjemce zpoplatnění:", + "levy_type": "Typ zpoplatnění:", + "modify_supply_for": "Upravit zásobu pro", + "modify_the_number_of_cosignatories_by": "Upravit počet spolupodepisovatelů o ", + "mutable": "měnitelný", + "no": "Ne", + "of": "z", + "percentile": "percentil", + "raw_units_template": "{} surové jednotky", + "remote_harvesting": " vzdálená sklizeň?", + "remove": "Odebrat", + "set_minimum_cosignatories_to": "Nastavit minimální počet spolupodepisovatelů na ", + "sign_tx_fee_template": "Podepsat tuto transakci\na zaplatit {}\nza síťový poplatek?", + "supply_change": "Změna zásob", + "supply_units_template": "{} zásoby po {} celých jednotkách?", + "transferable": "Přenosné?", + "under_namespace": "pod jmenným prostorem", + "unencrypted": "Nezašifrované:", + "unknown_mosaic": "Neznámá mozaika!", + "yes": "Ano" + }, + "passphrase": { + "access_hidden_wallet": "Přistoupit ke skryté peněžence?", + "always_on_device": "Opravdu chcete heslo vždy zadávat na zařízení?", + "from_host_not_shown": "Heslo poskytnuté hostitelem bude použita, ale nebude zobrazeno kvůli nastavení zařízení.", + "hidden_wallet": "Skrytá peněženka", + "hide": "Skrýt heslo pocházející z hostitele?", + "next_screen_will_show_passphrase": "Další obrazovka zobrazí heslo.", + "please_enter": "Prosím, zadejte své heslo.", + "revoke_on_device": "Chcete zrušit nastavení hesla na zařízení?", + "title_confirm": "POTVRDIT HESLO", + "title_enter": "ZADAT HESLO", + "title_hide": "SKRÝT HESLO", + "title_settings": "NASTAVENÍ HESLA", + "title_source": "ZDROJ HESLA", + "turn_off": "Vypnout ochranu heslem?", + "turn_on": "Zapnout ochranu heslem?" + }, + "pin": { + "change": "Změnit PIN?", + "changed": "PIN změněn", + "cursor_will_change": "Pozice kurzoru se bude mezi zadáními měnit pro zvýšení bezpečnosti.", + "diff_from_wipe_code": "PIN nesmí být stejný jako kód pro vymazání.", + "disabled": "Ochrana PIN vypnuta.", + "enabled": "Ochrana PIN zapnuta.", + "enter": "Zadat PIN", + "enter_new": "Zadejte nový PIN", + "entered_not_valid": "Zadaný PIN není platný.", + "info": "PIN bude vyžadován pro přístup k tomuto zařízení.", + "invalid_pin": "Neplatný PIN", + "last_attempt": "Poslední pokus", + "mismatch": "Zadané PINy nesouhlasí!", + "pin_mismatch": "NESHODA PIN", + "please_check_again": "Zkontrolujte prosím znovu.", + "reenter_new": "Znovu PIN", + "reenter_to_confirm": "Pro potvrzení znovu zadejte PIN.", + "should_be_long": "PIN by měl být 4-50 číslic dlouhý.", + "title_check_pin": "ZKONTROLOVAT PIN", + "title_settings": "NASTAVENÍ PINU", + "title_wrong_pin": "ŠPATNÝ ŠPENDLÍK", + "tries_left": "zbývající pokusy", + "turn_off": "Opravdu chcete vypnout ochranu PIN?", + "turn_on": "Zapnout ochranu PIN?", + "wrong_pin": "Špatný PIN" + }, + "plurals": { + "contains_x_keys": "klíč|klíče|klíčů", + "lock_after_x_hours": "hodině|hodinách|hodinách", + "lock_after_x_milliseconds": "milisekundě|milisekundách|milisekundách", + "lock_after_x_minutes": "minutě|minutách|minutách", + "lock_after_x_seconds": "sekundě|sekundách|sekundách", + "sign_x_actions": "akci|akce|akcí", + "transaction_of_x_operations": "operace|operací|operací", + "x_groups_needed": "skupina|skupiny|skupin", + "x_shares_needed": "podíl|podíly|podílů" + }, + "progress": { + "authenticity_check": "Kontrola autentičnosti ...", + "done": "Hotovo", + "loading_transaction": "Načítání transakce...", + "one_second_left": "Zbývá 1 sekunda", + "please_wait": "PROSÍM ČEKEJTE", + "processing": "PROCESOVÁNÍ", + "refreshing": "Obnovování...", + "signing_transaction": "Podpis transakce...", + "syncing": "Synchronizace...", + "x_seconds_left_template": "Zbývá {} sekund" + }, + "reboot_to_bootloader": { + "restart": "Chcete restartovat Trezor v režimu bootloader?", + "title": "PŘEJÍT DO REŽIMU BOOTLOADER", + "version_by_template": "Verze firmwaru {}\nod {}" + }, + "recovery": { + "cancel_dry_run": "Zrušit kontrolu zálohy", + "check_dry_run": "Zkontrolovat zálohu?", + "cursor_will_change": "Pozice kurzoru se změní mezi položkami pro zvýšenou bezpečnost.", + "dry_run_bip39_valid_match": "Zadané záchranné semínko je platné a odpovídá tomu v zařízení.", + "dry_run_bip39_valid_mismatch": "Zadané záchranné semínko je platné, ale neodpovídá tomu v zařízení.", + "dry_run_slip39_valid_match": "Zadané záchranné podíly jsou platné a odpovídají tomu, co je aktuálně v zařízení.", + "dry_run_slip39_valid_mismatch": "Zadané záchranné podíly jsou platné, ale neodpovídají tomu, co je aktuálně v zařízení.", + "enter_any_share": "Zadejte libovolný podíl", + "enter_backup": "Zadejte svou zálohu", + "enter_different_share": "Zadejte prosím odlišný podíl.", + "enter_share_from_diff_group": "Zadejte podíl z odlišné skupiny.", + "group_num_template": "Skupina {}", + "group_threshold_reached": "Práh skupiny dosažen.", + "invalid_seed_entered": "Zadáno neplatné záchranné semínko.", + "invalid_share_entered": "Zadán neplatný záchranný podíl.", + "more_shares_needed": "Potřebuje se více podílů", + "num_of_words": "Vyberte počet slov ve vaší záloze.", + "only_first_n_letters": "Budete muset vybrat pouze první 2-4 písmena každého slova.", + "progress_will_be_lost": "Veškerý pokrok bude ztracen.", + "select_num_of_words": "Vyberte počet slov ve vaší záloze.", + "share_already_entered": "Podíl již byl zadán", + "share_from_another_shamir": "Zadali jste podíl z jiného Shamir Backupu.", + "share_num_template": "Podíl {}", + "title": "OBNOVIT PENĚŽENKU", + "title_cancel_dry_run": "ZRUŠIT KONTROLU ZÁLOHY", + "title_cancel_recovery": "ZRUŠIT OBNOVENÍ", + "title_dry_run": "KONTROLA ZÁLOHY", + "title_recover": "OBNOVIT PENĚŽENKU", + "title_remaining_shares": "ZBÝVAJÍCÍ PODÍLY", + "type_word_x_of_y_template": "Napište slovo {} z {}", + "wallet_recovered": "Peněženka úspěšně obnovena", + "wanna_cancel_dry_run": "Jste si jisti, že chcete zrušit kontrolu zálohy?", + "wanna_cancel_recovery": "Jste si jisti, že chcete zrušit proces obnovení?", + "word_count_template": "({} slov)", + "word_x_of_y_template": "SLOVO {} Z {}", + "x_more_items_starting_template_plural": "Ještě {count} {plural} začínající", + "x_more_shares_needed_template_plural": "Potřebujete ještě {count} {plural}.", + "x_of_y_entered_template": "{} z {} podílů úspěšně zadáno.", + "you_have_entered": "Zadali jste" + }, + "reset": { + "advanced_group_threshold_info": "Prah skupiny určuje počet skupin potřebných k obnovení vaší peněženky.", + "all_x_of_y_template": "všechny {} z {} podílů", + "any_x_of_y_template": "libovolný {} z {} podílů", + "button_create": "VYTVOŘIT PENĚŽENKU", + "button_recover": "OBNOVIT PENĚŽENKU", + "by_continuing": "Pokračováním souhlasíte s podmínkami společnosti Trezor.", + "check_backup_title": "ZKONTROLOVAT ZÁLOHU", + "check_group_share_title_template": "KONTROLA G{} - PODÍL {}", + "check_seed_title": "ZKONTROLOVAT SEED", + "check_share_title_template": "KONTROLA PODÍL #{}", + "continue_with_next_share": "Pokračujte s dalším podílem.", + "continue_with_share_template": "Pokračujte s podílem #{}.", + "finished_verifying_group_template": "Dokončili jste ověření svých záchranných podílů pro skupinu {}.", + "finished_verifying_seed": "Dokončili jste ověření svého záchranného semínka.", + "finished_verifying_shares": "Dokončili jste ověření svých záchranných podílů.", + "group_description": "Skupina se skládá z obnovovacích podílů.", + "group_info": "Každá skupina má stanovený počet podílů a vlastní práh. V dalších krocích nastavíte počty podílů a prahy.", + "group_share_checked_successfully_template": "Skupina {} - Podíl {} úspěšně zkontrolován.", + "group_share_title_template": "SKUPINA {} - PODÍL {}", + "more_info_at": "Více informací na", + "need_all_share_template": "K obnovení budete potřebovat všechny {} podíly.", + "need_any_share_template": "K obnovení budete potřebovat libovolný {} z podílů.", + "needed_to_form_a_group": "potřebné k vytvoření skupiny. ", + "needed_to_recover_your_wallet": "potřebné k obnovení vaší peněženky. ", + "never_make_digital_copy": "Nikdy nedělejte digitální kopii své zálohy nebo ji neukládejte online!", + "num_of_share_holders_template": "{} lidí nebo míst bude držet jeden podíl.", + "num_of_shares_advanced_info_template": "Každý obnovovací podíl je posloupnost 20 slov. V dalším kroku zvolíte prahový počet podílů potřebných k vytvoření skupiny {}.", + "num_of_shares_basic_info": "Každý obnovovací podíl je posloupnost 20 slov. V dalším kroku zvolíte, kolik podílů potřebujete k obnovení vaší peněženky.", + "num_shares_for_group_template": "Požadovaný počet podílů pro vytvoření skupiny {}.", + "number_of_shares_info": "= celkový počet unikátních seznamů slov použitých pro zálohu peněženky.", + "one_share": "1 podíl", + "only_one_share_will_be_created": "Bude vytvořen pouze jeden podíl.", + "recovery_seed_title": "OBNOVIT SEED", + "recovery_share_title_template": "OBNOVIT PODÍL #{}", + "required_number_of_groups": "Požadovaný počet skupin pro obnovení.", + "select_correct_word": "Vyberte správné slovo pro každou pozici.", + "select_word_template": "VYBRAT {} SLOVO", + "select_word_x_of_y_template": "Vyberte slovo {} z {}:", + "set_it_to_count_template": "Nastavte na {} a budete potřebovat ", + "share_checked_successfully_template": "Záchranný podíl #{} úspěšně zkontrolován.", + "share_words_title": "STANDARDNÍ ZÁLOHA", + "slip39_checklist_num_groups": "Počet skupin", + "slip39_checklist_num_shares": "Počet podílů", + "slip39_checklist_set_num_groups": "Nastavit počet skupin", + "slip39_checklist_set_num_shares": "Nastavit počet akcií", + "slip39_checklist_set_sizes": "Nastavit velikosti a prahy", + "slip39_checklist_set_sizes_longer": "Nastavit velikosti a prahové hodnoty pro každou skupinu", + "slip39_checklist_set_threshold": "Nastavit práh", + "slip39_checklist_title": "KONTROLNÍ SEZNAM ZÁLOHY", + "slip39_checklist_write_down": "Zapsat a ověřit podíly", + "slip39_checklist_write_down_recovery": "Zapište si a zkontrolujte všechny obnovovací akcie", + "the_threshold_sets_the_number_of_shares": "Prah určuje počet podílů ", + "threshold_info": "= minimální počet unikátních seznamů slov použitých pro obnovení.", + "title_backup_is_done": "ZÁLOHA JE HOTOVÁ", + "title_create_wallet": "VYTVOŘIT PENĚŽENKU", + "title_create_wallet_shamir": "VYTVOŘIT PENĚŽENKU (SHAMIR)", + "title_group_threshold": "PRÁH SKUPINY", + "title_number_of_groups": "POČET SKUPIN", + "title_number_of_shares": "POČET PODÍLŮ", + "title_set_group_threshold": "NASTAVIT PRAHOVÝ POČET SKUPIN", + "title_set_number_of_groups": "NASTAVIT POČET SKUPIN", + "title_set_number_of_shares": "NASTAVIT POČET PODÍLŮ", + "title_set_threshold": "NASTAVIT PRAH", + "to_form_group_template": "pro vytvoření skupiny {}.", + "tos_link": "trezor.io/tos", + "total_number_of_shares_in_group_template": "Nastavte celkový počet podílů ve skupině {}.", + "use_your_backup": "Použijte zálohu, pokud potřebujete obnovit svou peněženku.", + "write_down_words_template": "Zapište všechna {} slova v pořadí.", + "wrong_word_selected": "Vybráno nesprávné slovo!", + "you_need_one_share": "K obnovení potřebujete 1 podíl.", + "your_backup_is_done": "Vaše záloha je hotova." + }, + "ripple": { + "confirm_tag": "Potvrdit značku", + "destination_tag_template": "Cílová značka: {}" + }, + "rotation": { + "change_template": "Chcete změnit rotaci zařízení na {}?", + "east": "východ", + "north": "sever", + "south": "jih", + "title_change": "ZMĚNIT ROTACI", + "west": "západ" + }, + "safety_checks": { + "approve_unsafe_always": "Trezor vám umožní schválit některé akce, které by mohly být nebezpečné.", + "approve_unsafe_temporary": "Trezor vám dočasně umožní schválit některé akce, které by mohly být nebezpečné.", + "enforce_strict": "Opravdu chcete vynutit přísné kontroly bezpečnosti (doporučeno)?", + "title": "KONTROLY BEZPEČNOSTI", + "title_safety_override": "PŘEPÍNÁNÍ BEZPEČNOSTI" + }, + "sd_card": { + "all_data_will_be_lost": "Všechna data na SD kartě budou ztracena.", + "card_required": "Vyžadována SD karta.", + "disable": "Opravdu chcete odebrat ochranu SD karty ze svého zařízení?", + "disabled": "Úspěšně jste zakázali ochranu SD karty.", + "enable": "Opravdu chcete zajistit své zařízení ochranou SD karty?", + "enabled": "Úspěšně jste povolili ochranu SD karty.", + "error": "Chyba SD karty", + "format_card": "Naformátovat SD kartu", + "insert_correct_card": "Vložte do zařízení správnou SD kartu.", + "please_insert": "Vložte prosím vaši SD kartu.", + "please_unplug_and_insert": "Odpojte prosím zařízení a vložte vaši SD kartu.", + "problem_accessing": "Došlo k problému při přístupu k SD kartě.", + "refresh": "Opravdu chcete nahradit aktuální tajemství SD karty nově vygenerovaným?", + "refreshed": "Úspěšně jste obnovili ochranu SD karty.", + "restart": "Chcete restartovat Trezor v režimu bootloader?", + "title": "OCHRANA SD KARTY", + "title_problem": "PROBLÉM SE SD KARTOU", + "unknown_filesystem": "Neznámý souborový systém", + "unplug_and_insert_correct": "Odpojte zařízení a vložte správnou SD kartu.", + "use_different_card": "Použijte jinou kartu nebo naformátujte SD kartu na souborový systém FAT32", + "wanna_format": "Opravdu chcete naformátovat SD kartu?", + "wrong_sd_card": "Špatná SD karta" + }, + "send": { + "address_path": "Adresová cesta", + "amount": "Částka:", + "confirm_sending": "Potvrdit_odeslání", + "from_multiple_accounts": "Odesílání z více účtů.", + "including_fee": "Včetně poplatku:", + "maximum_fee": "Maximální poplatek:", + "receiving_to_multisig": "Přijímání na multisig adresu.", + "title_amount": "ČÁSTKA", + "title_confirm_sending": "POTVRDIT ODESLÁNÍ", + "title_joint_transaction": "SPOLEČNÁ TRANSAKCE", + "title_receiving_to": "PŘIJÍMÁNÍ NA", + "title_recipient": "PŘÍJEMCE", + "title_sending": "ODESÍLÁNÍ", + "title_sending_amount": "ČÁSTKA K ODESLÁNÍ", + "title_sending_to": "ODESLÁNÍ NA", + "to_the_total_amount": "Celková částka:", + "total_amount": "Celková částka:", + "transaction_id": "ID transakce:", + "you_are_contributing": "Přispíváte:" + }, + "share_words": { + "words_in_order": " slova v pořadí.", + "wrote_down_all": "Napsal jsem všechny " + }, + "sign_message": { + "bytes_template": "{} Bajty", + "confirm_address": "POTVRDIT ADRESU", + "confirm_message": "POTVRDIT ZPRÁVU", + "message_size": "Velikost zprávy:", + "verify_address": "Ověřte adresu" + }, + "stellar": { + "account": "Účet", + "account_merge": "Sloučení účtů", + "account_thresholds": "Práhové hodnoty účtu", + "add_signer": "Přidat podepisovatele", + "add_trust": "Přidat důvěru", + "all_will_be_sent_to": "Vše XLM bude odesláno na:", + "allow_trust": "Povolit důvěru", + "asset": "Aktiva", + "bump_sequence": "Zvýšit sekvenci", + "buying": "Kupování:", + "clear_data": "Vymazat data", + "clear_flags": "Vymazat příznaky", + "confirm_issuer": "Potvrdit vydavatele", + "confirm_memo": "Potvrdit poznámku", + "confirm_network": "Potvrdit síť", + "confirm_operation": "Potvrdit operaci", + "confirm_stellar": "Potvrdit Stellar", + "confirm_timebounds": "Potvrdit časová omezení", + "create_account": "Vytvořit účet", + "debited_amount": "Odepsaná částka", + "delete": "Smazat", + "delete_passive_offer": "Smazat pasivní nabídku", + "delete_trust": "Smazat důvěru", + "destination": "Cíl:", + "exchanges_require_memo": "Důležité: Mnoho směnáren vyžaduje poznámku při vkladu", + "final_confirm": "Konečné potvrzení", + "hash": "Haš:", + "high": "Vysoký:", + "home_domain": "Domovská doména", + "inflation": "Inflace", + "initial_balance": "Počáteční zůstatek", + "initialize_signing_with": "Inicializovat podepisování s", + "issuer_template": "{} vydavatel:", + "key": "Klíč:", + "limit": "Limit:", + "low": "Nízký:", + "master_weight": "Hlavní váha:", + "medium": "Střední:", + "new_offer": "Nová nabídka", + "new_passive_offer": "Nová pasivní nabídka", + "no_memo_set": "Žádná poznámka nenastavena!", + "no_restriction": "[bez omezení]", + "on_network_template": "Transakce je na {}", + "path_pay": "Platba cestou", + "path_pay_at_least": "Platba cestou alespoň", + "pay": "Platit:", + "pay_at_most": "Zaplatit nejvýše:", + "preauth_transaction": "Předautentizovaná transakce:", + "price_per_template": "Cena za {}:", + "private_network": "soukromá síť", + "remove_signer": "Odebrat podepisovatele", + "revoke_trust": "Zrušit důvěru", + "selling": "Prodej:", + "set_data": "Nastavit data", + "set_flags": "Nastavit příznaky", + "set_sequence_to_template": "Nastavit sekvenci na {}?", + "sign_tx_count_template": "Podepsat tuto transakci složenou z {}", + "sign_tx_fee_template": "a zaplatit {} za poplatek?", + "source_account": "Zdrojový účet:", + "testnet_network": "testovací síť", + "trusted_account": "Důvěryhodný účet", + "update": "Aktualizovat", + "valid_from": "Platné od (UTC)", + "valid_to": "Platné do (UTC)", + "value_sha256": "Hodnota (SHA-256):", + "wanna_clean_value_key_template": "Chcete vymazat klíč hodnoty {}?", + "your_account": "váš účet" + }, + "tezos": { + "address": "Adresa:", + "amount": "Částka:", + "baker_address": "Adresa pekaře:", + "balance": "Zůstatek:", + "ballot": "Hlasovací lístek:", + "confirm_delegation": "Potvrdit delegaci", + "confirm_origination": "Potvrdit vznik", + "delegator": "Delegátor:", + "fee": "Poplatek:", + "proposal": "Návrh", + "register_delegate": "Registrovat delegáta", + "remove_delegation": "Odstranit delegaci", + "submit_ballot": "Odeslat hlasovací lístek", + "submit_proposal": "Předložit návrh", + "submit_proposals": "Předložit návrhy" + }, + "tutorial": { + "middle_click": "Stiskněte současně vlevo a vpravo pro potvrzení.", + "press_and_hold": "Stiskněte a podržte pravé tlačítko pro schválení důležitých operací.", + "ready_to_use": "Jste připraveni používat Trezor.", + "scroll_down": "Stiskněte vpravo pro posun dolů a přečtěte si veškerý obsah, pokud text nezapadne na jednu obrazovku. Stiskněte vlevo pro posun nahoru.", + "sure_you_want_skip": "Jste si jisti, že chcete tutoriál přeskočit?", + "title_hello": "AHOJ", + "title_screen_scroll": "POSUN OBRAZOVKY", + "title_skip": "PŘESKOČIT TUTORIÁL", + "title_tutorial_complete": "TUTORIÁL DOKONČEN", + "use_trezor": "Trezor má levé a pravé tlačítko.\n\rPokračujte vpravo.", + "welcome_press_right": "Vítejte v Trezoru. Stiskněte vpravo pro pokračování." + }, + "u2f": { + "get": "Zvýšit a získat U2F čítač?", + "set_template": "Nastavit U2F čítač na {}?", + "title_get": "ZÍSKAT U2F ČÍTAČ", + "title_set": "NASTAVIT U2F ČÍTAČ" + }, + "wipe": { + "info": "Všechna data budou smazána.", + "title": "VYMAZAT ZAŘÍZENÍ", + "want_to_wipe": "Opravdu chcete vymazat zařízení?\n" + }, + "wipe_code": { + "change": "Změnit kód pro vymazání?", + "changed": "Kód pro vymazání změněn", + "diff_from_pin": "Kód pro vymazání musí být odlišný od vašeho PINu.\nZkuste to prosím znovu.", + "disabled": "Kód pro vymazání zakázán.", + "enabled": "Kód pro vymazání povolen.", + "enter_new": "Zadejte nový kód pro vymazání", + "info": "Kód vymaže všechna data z tohoto zařízení.", + "invalid": "Neplatný kód pro vymazání", + "mismatch": "Zadané kódy pro vymazání nesouhlasí!", + "reenter": "Znovu zadejte kód pro vymazání", + "reenter_to_confirm": "Pro potvrzení znovu zadejte kód pro vymazání.", + "title_check": "ZKONTROLOVAT KÓD PRO VYMAZÁNÍ", + "title_invalid": "NEPLATNÝ KÓD PRO VYMAZÁNÍ", + "title_settings": "NASTAVENÍ VÝMAZU", + "turn_off": "Vypnout ochranu kódem pro vymazání?", + "turn_on": "Zapnout ochranu kódem pro vymazání?", + "wipe_code_mismatch": "NESHODA KÓDU PRO VYMAZÁNÍ" + }, + "word_count": { + "title": "POČET SLOV" + }, + "words": { + "are_you_sure": "Jste si jisti?", + "array_of": "Pole o", + "buying": "Koupit", + "confirm": "Potvrdit", + "contains": "Obsahuje", + "continue_anyway": "Přesto pokračovat?", + "continue_with": "Pokračovat s", + "error": "Chyba", + "from": "od", + "keep_it_safe": "Uchovávejte v bezpečí!", + "know_what_your_doing": "Pokračujte pouze, pokud víte, co děláte!", + "my_trezor": "Můj Trezor", + "outputs": "výstupy", + "please_check_again": "Zkontrolujte to prosím znovu", + "please_try_again": "Zkuste to prosím znovu", + "really_wanna": "Opravdu chcete", + "sign": "Podepsat", + "title_check": "KONTROLA", + "title_group": "SKUPINA", + "title_information": "INFORMACE", + "title_remember": "PAMATOVAT", + "title_share": "PODÍL", + "title_shares": "PODÍLY", + "title_success": "ÚSPĚCH", + "title_summary": "SHRNUTÍ", + "title_threshold": "PRÁH", + "unknown": "Neznámý", + "warning": "Varování" + } + } +} diff --git a/core/embed/rust/src/ui/translations/cs.rs b/core/embed/rust/src/ui/translations/cs.rs new file mode 100644 index 000000000..61ddf4e07 --- /dev/null +++ b/core/embed/rust/src/ui/translations/cs.rs @@ -0,0 +1,841 @@ +//! generated from cs.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +// NOTE: not used as a code, only for +// documentation purposes + +use super::general::TranslationsGeneral; + +#[rustfmt::skip] +pub const TRANSLATIONS: TranslationsGeneral = TranslationsGeneral { + addr_mismatch__contact_support: "Prosím, obraťte se na podporu Trezor na", + addr_mismatch__key_mismatch: "Nesouhlasí klíč?", + addr_mismatch__mismatch: "Nesouhlasí adresa?", + addr_mismatch__support_url: "trezor.io/support", + addr_mismatch__title: "NESOUHLASÍ ADRESA?", + addr_mismatch__title_key_mismatch: "NESOUHLASÍ KLÍČ?", + addr_mismatch__wrong_derication_path: "Špatná derivační cesta pro zvolený účet.", + addr_mismatch__xpub_mismatch: "Nesouhlasí XPUB?", + address__address: "Adresa:", + address__public_key: "Veřejný klíč", + address__title_cosigner: "SPOLUPODEPSÁNÍ", + address__title_receive_address: "ADRESA PRO PŘIJETÍ", + address__title_yours: "VAŠE", + address_details__account: "Účet:", + address_details__derivation_path: "Cesta odvození:", + address_details__title_receive_address: "PŘIJMOUT ADRESU", + address_details__title_receiving_to: "PŘIJÍMÁNÍ DO", + authenticate__confirm_template: "Povolit připojený počítač potvrdit váš {} je pravý?", + authenticate__header: "Ověřené zařízení", + auto_lock__change_template: "Automaticky zamknout Trezor po {} nečinnosti?", + auto_lock__title: "AUTO-ZÁMKOVÁNÍ", + backup__can_back_up_anytime: "Svůj Trezor můžete zálohovat jednou, kdykoli.", + backup__it_should_be_backed_up: "Měli byste nyní zálohovat svou novou peněženku.", + backup__it_should_be_backed_up_now: "Proveďte zálohu!", + backup__new_wallet_created: "Nová peněženka vytvořena.", + backup__new_wallet_successfully_created: "Nová peněženka úspěšně vytvořena.", + backup__recover_anytime: "Kdykoli můžete použít zálohu k obnovení peněženky.", + backup__title_backup_wallet: "ZÁLOHOVAT PENĚŽENKU", + backup__title_skip: "PŘESKOČIT ZÁLOHU", + backup__want_to_skip: "Jste si jisti, že chcete přeskočit zálohu?", + binance__buy: "Koupit", + binance__confirm_cancel: "Potvrdit zrušení", + binance__confirm_input: "Potvrdit vstup", + binance__confirm_order: "Potvrdit objednávku", + binance__confirm_output: "Potvrdit výstup", + binance__order_id: "ID objednávky:", + binance__pair: "Pár:", + binance__price: "Cena:", + binance__quantity: "Množství:", + binance__sell: "Prodat", + binance__sender_address: "Adresa odesílatele:", + binance__side: "Strana:", + binance__unknown: "Neznámý", + bitcoin__commitment_data: "Data závazku:", + bitcoin__confirm_locktime: "Potvrdit locktime", + bitcoin__create_proof_of_ownership: "Chcete vytvořit důkaz vlastnictví?", + bitcoin__high_mining_fee_template: "Poplatek za těžbu\n{}\nje neočekávaně vysoký.", + bitcoin__locktime_no_effect: "Locktime je nastaven, ale nebude mít žádný účinek.", + bitcoin__locktime_set_to: "Locktime nastaven:", + bitcoin__locktime_set_to_blockheight: "Locktime nastaven na výšku bloku:", + bitcoin__lot_of_change_outputs: "Mnoho výstupů změn.", + bitcoin__multiple_accounts: "Více účtů", + bitcoin__new_fee_rate: "Nová sazba poplatku:", + bitcoin__simple_send_of: "Jednoduché odeslání", + bitcoin__ticket_amount: "Částka lístku:", + bitcoin__title_confirm_details: "POTVRDIT DETAILY", + bitcoin__title_finalize_transaction: "DOKONČIT TRANSAKCI", + bitcoin__title_high_mining_fee: "VYSOKÝ POPLATEK ZA TĚŽBU", + bitcoin__title_meld_transaction: "SPOJIT TRANSAKCE", + bitcoin__title_modify_amount: "UPRAVIT ČÁSTKU", + bitcoin__title_payjoin: "PAYJOIN", + bitcoin__title_proof_of_ownership: "DŮKAZ VLASTNICTVÍ", + bitcoin__title_purchase_ticket: "KOUPIT LÍSTEK", + bitcoin__title_update_transaction: "AKTUALIZOVAT TRANSAKCI", + bitcoin__unknown_path: "Neznámá cesta", + bitcoin__unknown_transaction: "Neznámá transakce", + bitcoin__unusually_high_fee: "Neobvykle vysoký poplatek.", + bitcoin__unverified_external_inputs: "Transakce obsahuje neověřené vnější vstupy.", + bitcoin__valid_signature: "Podpis je platný.", + bitcoin__voting_rights: "Právo hlasovat pro:", + buttons__abort: "ZRUŠIT", + buttons__access: "PŘÍSTUP", + buttons__again: "ZNOVU", + buttons__allow: "DOVOLIT", + buttons__back_up: "ZÁLOHOVAT", + buttons__cancel: "ZRUŠIT", + buttons__change: "ZMĚNIT", + buttons__check: "ZKONTROLOVAT", + buttons__check_again: "ZKONTROLOVAT ZNOVU", + buttons__close: "ZAVŘÍT", + buttons__confirm: "POTVRDIT", + buttons__continue: "POKRAČOVAT", + buttons__details: "DETAILY", + buttons__enable: "POVOLIT", + buttons__enter: "ZADAT", + buttons__enter_share: "ZADAT PODÍL", + buttons__export: "EXPORTOVAT", + buttons__format: "FORMÁTOVAT", + buttons__go_back: "VRÁTIT", + buttons__hold_to_confirm: "PODRŽTE", + buttons__info: "INFORMACE", + buttons__install: "NAINSTALUJTE", + buttons__more_info: "VÍCE INFORMACÍ", + buttons__ok_i_understand: "OK, CHÁPU", + buttons__purchase: "KOUPI", + buttons__quit: "UKONČIT", + buttons__restart: "RESTARTOVAT", + buttons__retry: "ZNOVU", + buttons__select: "VYBRAT", + buttons__set: "NASTAVIT", + buttons__show_all: "UKÁZAT VŠE", + buttons__show_words: "UKÁZAT SLOVA", + buttons__skip: "PŘESKOČIT", + buttons__try_again: "ZKUSIT ZNOVU", + buttons__turn_off: "VYPNOUT", + buttons__turn_on: "ZAPNOUT", + cardano__addr_base: "Základ", + cardano__addr_enterprise: "Podnik", + cardano__addr_legacy: "Dědictví", + cardano__addr_pointer: "Ukazatel", + cardano__addr_reward: "Odměna", + cardano__address_no_staking: "adresa - žádné odměny za stávkování.", + cardano__amount: "Částka:", + cardano__amount_burned_decimals_unknown: "Spálená částka:", + cardano__amount_minted_decimals_unknown: "Vytvořená částka:", + cardano__amount_sent_decimals_unknown: "Odeslána částka (desetinná čára neznámá):", + cardano__anonymous_pool: "Pool nemá metadata (anonymní pool)", + cardano__asset_fingerprint: "Otisk aktiv:", + cardano__auxiliary_data_hash: "Hash pomocných dat:", + cardano__block: "Blok", + cardano__catalyst: "Katalyzátor", + cardano__certificate: "Certifikát", + cardano__certificate_path: "Cesta certifikátu", + cardano__change_output: "Změnit výstup", + cardano__change_output_path: "Cesta změny výstupu", + cardano__change_output_staking_path: "Cesta stávkování změny výstupu", + cardano__check_all_items: "Pečlivě zkontrolujte všechny položky.", + cardano__choose_level_of_details: "Vyberte úroveň detailů:", + cardano__collateral_input_id: "ID vstupu zajištění:", + cardano__collateral_input_index: "Index vstupu zajištění:", + cardano__collateral_output_contains_tokens: "Výstup zajištění obsahuje tokeny.", + cardano__collateral_return: "Návrat zajištění", + cardano__confirm: "Potvrdit:", + cardano__confirm_signing_stake_pool: "Potvrdit podepsání registrace stávkového poolu jako vlastníka.", + cardano__confirm_transaction: "Potvrdit transakci", + cardano__confirming_a_multisig_transaction: "Potvrzování multisig transakce.", + cardano__confirming_pool_registration: "Potvrzení registrace stávkového poolu.", + cardano__confirming_transction: "Potvrzení transakce.", + cardano__cost: "Náklady", + cardano__credential_mismatch: "Pověření neodpovídá platebnímu pověření.", + cardano__datum_hash: "Datum hash:", + cardano__delegating_to: "Delegace na:", + cardano__for_account_and_index_template: "pro účet {} a index {}:", + cardano__for_account_template: "pro účet {}:", + cardano__for_key_hash: "pro hash klíče:", + cardano__for_script: "pro skript:", + cardano__inline_datum: "Vložená data", + cardano__input_id: "ID vstupu:", + cardano__input_index: "Index vstupu:", + cardano__intro_text_address: "Adresa", + cardano__intro_text_change: "Následující adresa je adresa změny. Její", + cardano__intro_text_owned_by_device: "Následující adresa patří tomuto zařízení. Její", + cardano__intro_text_registration_payment: "Adresa platby za registraci hlasovacího klíče patří tomuto zařízení. Její", + cardano__key_hash: "hash klíče", + cardano__margin: "Marže", + cardano__multisig_path: "cesta multi-podpisu", + cardano__nested_scripts_template: "Obsahuje {} vnořené skripty.", + cardano__network: "Síť:", + cardano__no_output_tx: "Transakce nemá výstupy, síť nelze ověřit.", + cardano__nonce: "Nonce:", + cardano__other: "jiné", + cardano__path: "cesta", + cardano__pledge: "Záruka", + cardano__pointer: "ukazatel", + cardano__policy_id: "ID politiky:", + cardano__pool_metadata_hash: "Hash metadat poolu:", + cardano__pool_metadata_url: "URL metadat poolu:", + cardano__pool_owner: "Vlastník poolu:", + cardano__pool_owner_path: "Cesta stávkování vlastníka poolu", + cardano__pool_reward_account: "Účet odměn poolu:", + cardano__reference_input_id: "ID referenčního vstupu:", + cardano__reference_input_index: "Index referenčního vstupu:", + cardano__reference_script: "Referenční skript", + cardano__required_signer: "Požadovaný podepsaný", + cardano__reward: "odměna", + cardano__reward_address: "Adresa je odměna.", + cardano__reward_eligibility_warning: "Varování: Adresa není platná adresa pro platby, není způsobilá pro odměny.", + cardano__rewards_go_to: "Odměny jdou na:", + cardano__script: "skript", + cardano__script_all: "Vše", + cardano__script_any: "Jakýkoliv", + cardano__script_data_hash: "Hash dat skriptu:", + cardano__script_hash: "Skript hash:", + cardano__script_invalid_before: "Neplatné před", + cardano__script_invalid_hereafter: "Neplatné odteď", + cardano__script_key: "Klíč", + cardano__script_n_of_k: "N z K", + cardano__script_reward: "odměna skriptu", + cardano__sending: "Odesílání", + cardano__show_simple: "Zobrazit jednoduše", + cardano__sign_tx_path_template: "Podepsat transakci pomocí {}:", + cardano__stake_delegation: "Delegace stávky", + cardano__stake_deregistration: "Zrušení registrace stávkového klíče", + cardano__stake_pool_registration: "Registrace stávkového poolu", + cardano__stake_pool_registration_pool_id: "Registrace stávkového poolu\nID poolu:", + cardano__stake_registration: "Registrace stávkového klíče", + cardano__staking_key_for_account: "Stávkový klíč pro účet", + cardano__to_pool: "do poolu:", + cardano__token_minting_path: "cesta ražby tokenů", + cardano__total_collateral: "Celkové zajištění", + cardano__transaction: "Transakce", + cardano__transaction_contains_minting_or_burning: "Transakce obsahuje ražbu nebo spálení tokenů.", + cardano__transaction_contains_script_address_no_datum: "Následující výstup transakce obsahuje skriptovou adresu, ale neobsahuje data.", + cardano__transaction_fee: "Poplatek za transakci:", + cardano__transaction_id: "ID transakce:", + cardano__transaction_no_collateral_input: "Transakce neobsahuje vstupy zajištění. Skript Plutus nebude moci běžet.", + cardano__transaction_no_script_data_hash: "Transakce neobsahuje hash dat skriptu. Skript Plutus nebude moci běžet.", + cardano__transaction_output_contains_tokens: "Následující výstup transakce obsahuje tokeny.", + cardano__ttl: "TTL:", + cardano__unknown: "Neznámý", + cardano__unknown_collateral_amount: "Neznámá částka zajištění.", + cardano__unusual_path: "Neobvyklá cesta.", + cardano__valid_since: "Platné od:", + cardano__verify_script: "Ověřit skript", + cardano__vote_key_registration: "Registrace hlasovacího klíče (CIP-36)", + cardano__vote_public_key: "Veřejný klíč hlasování:", + cardano__voting_purpose: "Účel hlasování:", + cardano__warning: "Varování", + cardano__weight: "Váha:", + cardano__withdrawal_for_address_template: "Potvrdit výběr pro adresu {}:", + cardano__witness_path: "Cesta svědka", + cardano__x_of_y_signatures_template: "Vyžaduje {} z {} podpisů.", + coinjoin__access_account: "Přístup k vašemu coinjoin účtu?", + coinjoin__do_not_disconnect: "Neodpojujte svůj Trezor!", + coinjoin__max_mining_fee: "Maximální poplatek za těžbu:", + coinjoin__max_rounds: "Maximální počet kol:", + coinjoin__title: "AUTORIZOVAT COINJOIN", + coinjoin__title_do_not_disconnect: "NEODPOJUJTE SVŮJ TREZOR!", + coinjoin__title_progress: "COINJOIN V PRŮBĚHU", + coinjoin__waiting_for_others: "Čekání na ostatní", + confirm_total__account: "Účet:", + confirm_total__fee_rate: "Sazba poplatku:", + confirm_total__sending_from_account: "Odesílání z účtu:", + confirm_total__title_fee: "INFORMACE O POPLATKU", + confirm_total__title_sending_from: "ODESLÁNÍ OD", + debug__loading_seed: "Načítání seedu", + debug__loading_seed_not_recommended: "Načítání soukromého seedu není doporučeno.", + device_name__change_template: "Změnit název zařízení na {}?", + device_name__title: "NÁZEV ZAŘÍZENÍ", + entropy__send: "Opravdu chcete odeslat entropii?", + entropy__title: "VNITŘNÍ ENTROPIE", + entropy__title_confirm: "POTVRDIT ENTROPII", + eos__about_to_sign_template: "Chystáte se podepsat {}.", + eos__account: "Účet:", + eos__action_name: "Název akce:", + eos__amount: "Částka:", + eos__arbitrary_data: "Libovolná data", + eos__buy_ram: "Koupit RAM", + eos__bytes: "Byty:", + eos__cancel_vote: "Zrušit hlasování", + eos__checksum: "Kontrolní součet:", + eos__code: "Kód:", + eos__contract: "Smlouva:", + eos__cpu: "CPU:", + eos__creator: "Tvůrce:", + eos__delegate: "Delegovat", + eos__delete_auth: "Smazat ověření", + eos__from: "Od:", + eos__link_auth: "Propojit ověření", + eos__memo: "Poznámka", + eos__name: "Jméno:", + eos__net: "NET:", + eos__new_account: "Nový účet", + eos__no: "Ne", + eos__owner: "Vlastník:", + eos__parent: "Rodič:", + eos__payer: "Plátce:", + eos__permission: "Oprávnění:", + eos__proxy: "Proxy:", + eos__receiver: "Příjemce:", + eos__refund: "Vrátit peníze", + eos__requirement: "Požadavek:", + eos__sell_ram: "Prodat RAM", + eos__sender: "Odesílatel:", + eos__sign_transaction: "Podepsat transakci", + eos__threshold: "Práh:", + eos__to: "Do:", + eos__transfer: "Převod:", + eos__type: "Typ:", + eos__undelegate: "Zrušit delegaci", + eos__unlink_auth: "Odpojit ověření", + eos__update_auth: "Aktualizovat ověření", + eos__vote_for_producers: "Hlasovat pro producenty", + eos__vote_for_proxy: "Hlasovat pro proxy", + eos__voter: "Volitel:", + eos__yes: "Ano", + ethereum__amount_sent: "Odeslaná částka:", + ethereum__confirm_fee: "Potvrdit poplatek", + ethereum__contract: "Smlouva:", + ethereum__data_size_template: "Velikost: {} bajtů", + ethereum__gas_limit: "Limit plynu:", + ethereum__gas_price: "Cena plynu:", + ethereum__max_gas_price: "Maximální cena plynu:", + ethereum__name_and_version: "Jméno a verze", + ethereum__new_contract: "nová smlouva?", + ethereum__no_message_field: "Žádné pole zprávy", + ethereum__priority_fee: "Prioritní poplatek:", + ethereum__show_full_array: "Zobrazit celé pole", + ethereum__show_full_domain: "Zobrazit celou doménu", + ethereum__show_full_message: "Zobrazit celou zprávu", + ethereum__show_full_struct: "Zobrazit celou strukturu", + ethereum__sign_eip712: "Opravdu podepsat data podle EIP-712?", + ethereum__title_confirm_data: "POTVRDIT DATA", + ethereum__title_confirm_domain: "POTVRDIT DOMÉNU", + ethereum__title_confirm_message: "POTVRDIT ZPRÁVU", + ethereum__title_confirm_struct: "POTVRDIT STRUKTURU", + ethereum__title_confirm_typed_data: "POTVRDIT ZAPSANÁ DATA", + ethereum__title_signing_address: "ADRESA PRO PODEPSÁNÍ", + ethereum__units_template: "{} jednotek", + ethereum__unknown_token: "Neznámý token", + ethereum__valid_signature: "Podpis je platný.", + experimental_mode__enable: "Povolit experimentální funkce?", + experimental_mode__only_for_dev: "Pouze pro vývoj a beta testování!", + experimental_mode__title: "EXPERIMENTÁLNÍ REŽIM", + fido__already_registered: "Již zaregistrováno", + fido__device_already_registered: "Toto zařízení je již zaregistrováno s touto aplikací.", + fido__device_already_registered_with_template: "Toto zařízení je již zaregistrováno s {}.", + fido__device_not_registered: "Toto zařízení není zaregistrováno s touto aplikací.", + fido__does_not_belong: "Kredence nepatří tomuto autentikátoru.", + fido__erase_credentials: "Opravdu chcete smazat všechny pověření?", + fido__export_credentials: "Exportovat informace z tohoto autentikátoru?", + fido__not_registered: "Není zaregistrováno", + fido__not_registered_with_template: "Toto zařízení není zaregistrováno s\n{}.", + fido__please_enable_pin_protection: "Prosím, povolte ochranu PIN.", + fido__title_authenticate: "FIDO2 AUTENTIZACE", + fido__title_import_credential: "IMPORTOVAT KREDENCE", + fido__title_list_credentials: "UKAŽ KREDENCE", + fido__title_register: "FIDO2 REGISTRACE", + fido__title_remove_credential: "ODSTRANIT KREDENCE", + fido__title_reset: "RESET FIDO2", + fido__title_u2f_auth: "U2F AUTENTIZACE", + fido__title_u2f_register: "U2F REGISTRACE", + fido__title_verify_user: "FIDO2 OVĚŘIT UŽIVATELE", + fido__unable_to_verify_user: "Nelze ověřit uživatele.", + fido__wanna_erase_credentials: "Opravdu chcete smazat všechny přihlašovací údaje?", + firmware_update__title: "AKTUALIZOVAT FIRMWARE", + firmware_update__title_fingerprint: "FW OTISKY PRSTŮ", + homescreen__click_to_connect: "Klikněte pro připojení", + homescreen__click_to_unlock: "Klikněte pro odemčení", + homescreen__title_backup_failed: "ZÁLOHA SELHALA", + homescreen__title_backup_needed: "ZÁLOHA POTŘEBNÁ", + homescreen__title_coinjoin_authorized: "COINJOIN AUTORIZOVÁNO", + homescreen__title_experimental_mode: "EXPERIMENTÁLNÍ REŽIM", + homescreen__title_hold_to_lock: "DRŽET PRO ZAMKNUTÍ", + homescreen__title_no_usb_connection: "ŽÁDNÉ USB PŘIPOJENÍ", + homescreen__title_pin_not_set: "PIN NEBYL NASTAVEN", + homescreen__title_seedless: "BEZ SEMÍNKA", + homescreen__title_set: "ZMĚNIT ÚVODNÍ OBRAZOVKU?", + inputs__back: "ZPĚT", + inputs__cancel: "ZRUŠIT", + inputs__delete: "SMAZAT", + inputs__enter: "ZADAT", + inputs__return: "VRÁTIT", + inputs__show: "UKÁZAT", + inputs__space: "MEZERNÍK", + joint__title: "SPOLEČNÁ TRANSAKCE", + joint__to_the_total_amount: "Celkové částce:", + joint__you_are_contributing: "Přispíváte:", + lockscreen__tap_to_connect: "Klepněte pro připojení", + lockscreen__tap_to_unlock: "Klepněte pro odemčení", + lockscreen__title_locked: "ZAMKNUTO", + lockscreen__title_not_connected: "NEPŘIPOJENO", + misc__decrypt_value: "Dešifrovat hodnotu", + misc__encrypt_value: "Zašifrovat hodnotu", + misc__title_suite_labeling: "OZNAČENÍ SUITE", + modify_amount__address: "Adresa:", + modify_amount__decrease_amount: "Snížit částku o:", + modify_amount__increase_amount: "Zvýšit částku o:", + modify_amount__new_amount: "Nová částka:", + modify_amount__title: "UPRAVIT ČÁSTKU", + modify_fee__decrease_fee: "Snížit poplatek o:", + modify_fee__fee_rate: "Sazba poplatku:", + modify_fee__increase_fee: "Zvýšit poplatek o:", + modify_fee__new_transaction_fee: "Nový transakční poplatek:", + modify_fee__no_change: "Váš poplatek se nezměnil.", + modify_fee__title: "UPRAVIT POPLATEK", + modify_fee__transaction_fee: "Transakční poplatek:", + monero__confirm_export: "Potvrdit export", + monero__confirm_fee: "Potvrdit poplatek", + monero__confirm_ki_sync: "Potvrdit synchronizaci klíčových obrázků", + monero__confirm_refresh: "Potvrdit obnovení", + monero__confirm_unlock_time: "Potvrdit čas odemknutí", + monero__hashing_inputs: "Hašování vstupů", + monero__payment_id: "ID platby", + monero__postprocessing: "Dokončování zpracování...", + monero__processing: "Zpracovávání...", + monero__processing_inputs: "Zpracování vstupů", + monero__processing_outputs: "Zpracování výstupů", + monero__signing: "Podepisování...", + monero__signing_inputs: "Podepisování vstupů", + monero__unlock_time_set_template: "Čas odemknutí této transakce je nastaven na {}", + monero__wanna_export_tx_der: "Opravdu chcete exportovat tx_der pro tx_proof?", + monero__wanna_export_tx_key: "Opravdu chcete exportovat tx_key?", + monero__wanna_export_watchkey: "Opravdu chcete exportovat pouze sledovací pověření?", + monero__wanna_start_refresh: "Opravdu chcete spustit obnovení?", + monero__wanna_sync_key_images: "Opravdu chcete synchronizovat klíčové obrázky?", + nem__absolute: "absolutní", + nem__activate: "Aktivovat", + nem__add: "Přidat", + nem__confirm_action: "Potvrdit akci", + nem__confirm_address: "Potvrdit adresu", + nem__confirm_creation_fee: "Potvrdit poplatek za vytvoření", + nem__confirm_fee: "Potvrdit poplatek", + nem__confirm_mosaic: "Potvrdit mozaiku", + nem__confirm_multisig_fee: "Potvrdit vícepodepisový poplatek", + nem__confirm_namespace: "Potvrdit jmenný prostor", + nem__confirm_payload: "Potvrdit náklad", + nem__confirm_properties: "Potvrdit vlastnosti", + nem__confirm_rental_fee: "Potvrdit nájemné", + nem__confirm_transfer_of: "Potvrdit převod", + nem__convert_account_to_multisig: "Převést účet na vícepodepisový účet?", + nem__cosign_transaction_for: "Spolupodepsat transakci pro", + nem__cosignatory: " spolupodepisovatel", + nem__create_mosaic: "Vytvořit mozaiku", + nem__create_namespace: "Vytvořit jmenný prostor", + nem__deactivate: "Deaktivovat", + nem__decrease: "Snížit", + nem__description: "Popis:", + nem__divisibility_and_levy_cannot_be_shown: "Dělitelnost a zpoplatnění nelze zobrazit pro neznámé mozaiky", + nem__encrypted: "Zašifrované:", + nem__final_confirm: "Konečné potvrzení", + nem__immutable: "neměnitelný", + nem__increase: "Zvýšit", + nem__initial_supply: "Počáteční zásoba:", + nem__initiate_transaction_for: "Zahájit transakci pro", + nem__levy_divisibility: "Dělitelnost zpoplatnění:", + nem__levy_fee: "Poplatek za zpoplatnění:", + nem__levy_fee_of: "Poplatek ze zpoplatnění:", + nem__levy_mosaic: "Mozaika zpoplatnění:", + nem__levy_namespace: "Jmenný prostor zpoplatnění:", + nem__levy_recipient: "Příjemce zpoplatnění:", + nem__levy_type: "Typ zpoplatnění:", + nem__modify_supply_for: "Upravit zásobu pro", + nem__modify_the_number_of_cosignatories_by: "Upravit počet spolupodepisovatelů o ", + nem__mutable: "měnitelný", + nem__no: "Ne", + nem__of: "z", + nem__percentile: "percentil", + nem__raw_units_template: "{} surové jednotky", + nem__remote_harvesting: " vzdálená sklizeň?", + nem__remove: "Odebrat", + nem__set_minimum_cosignatories_to: "Nastavit minimální počet spolupodepisovatelů na ", + nem__sign_tx_fee_template: "Podepsat tuto transakci\na zaplatit {}\nza síťový poplatek?", + nem__supply_change: "Změna zásob", + nem__supply_units_template: "{} zásoby po {} celých jednotkách?", + nem__transferable: "Přenosné?", + nem__under_namespace: "pod jmenným prostorem", + nem__unencrypted: "Nezašifrované:", + nem__unknown_mosaic: "Neznámá mozaika!", + nem__yes: "Ano", + passphrase__access_hidden_wallet: "Přistoupit ke skryté peněžence?", + passphrase__always_on_device: "Opravdu chcete heslo vždy zadávat na zařízení?", + passphrase__from_host_not_shown: "Heslo poskytnuté hostitelem bude použita, ale nebude zobrazeno kvůli nastavení zařízení.", + passphrase__hidden_wallet: "Skrytá peněženka", + passphrase__hide: "Skrýt heslo pocházející z hostitele?", + passphrase__next_screen_will_show_passphrase: "Další obrazovka zobrazí heslo.", + passphrase__please_enter: "Prosím, zadejte své heslo.", + passphrase__revoke_on_device: "Chcete zrušit nastavení hesla na zařízení?", + passphrase__title_confirm: "POTVRDIT HESLO", + passphrase__title_enter: "ZADAT HESLO", + passphrase__title_hide: "SKRÝT HESLO", + passphrase__title_settings: "NASTAVENÍ HESLA", + passphrase__title_source: "ZDROJ HESLA", + passphrase__turn_off: "Vypnout ochranu heslem?", + passphrase__turn_on: "Zapnout ochranu heslem?", + pin__change: "Změnit PIN?", + pin__changed: "PIN změněn", + pin__cursor_will_change: "Pozice kurzoru se bude mezi zadáními měnit pro zvýšení bezpečnosti.", + pin__diff_from_wipe_code: "PIN nesmí být stejný jako kód pro vymazání.", + pin__disabled: "Ochrana PIN vypnuta.", + pin__enabled: "Ochrana PIN zapnuta.", + pin__enter: "Zadat PIN", + pin__enter_new: "Zadejte nový PIN", + pin__entered_not_valid: "Zadaný PIN není platný.", + pin__info: "PIN bude vyžadován pro přístup k tomuto zařízení.", + pin__invalid_pin: "Neplatný PIN", + pin__last_attempt: "Poslední pokus", + pin__mismatch: "Zadané PINy nesouhlasí!", + pin__pin_mismatch: "NESHODA PIN", + pin__please_check_again: "Zkontrolujte prosím znovu.", + pin__reenter_new: "Znovu PIN", + pin__reenter_to_confirm: "Pro potvrzení znovu zadejte PIN.", + pin__should_be_long: "PIN by měl být 4-50 číslic dlouhý.", + pin__title_check_pin: "ZKONTROLOVAT PIN", + pin__title_settings: "NASTAVENÍ PINU", + pin__title_wrong_pin: "ŠPATNÝ ŠPENDLÍK", + pin__tries_left: "zbývající pokusy", + pin__turn_off: "Opravdu chcete vypnout ochranu PIN?", + pin__turn_on: "Zapnout ochranu PIN?", + pin__wrong_pin: "Špatný PIN", + plurals__contains_x_keys: "klíč|klíče|klíčů", + plurals__lock_after_x_hours: "hodině|hodinách|hodinách", + plurals__lock_after_x_milliseconds: "milisekundě|milisekundách|milisekundách", + plurals__lock_after_x_minutes: "minutě|minutách|minutách", + plurals__lock_after_x_seconds: "sekundě|sekundách|sekundách", + plurals__sign_x_actions: "akci|akce|akcí", + plurals__transaction_of_x_operations: "operace|operací|operací", + plurals__x_groups_needed: "skupina|skupiny|skupin", + plurals__x_shares_needed: "podíl|podíly|podílů", + progress__authenticity_check: "Kontrola autentičnosti ...", + progress__done: "Hotovo", + progress__loading_transaction: "Načítání transakce...", + progress__one_second_left: "Zbývá 1 sekunda", + progress__please_wait: "PROSÍM ČEKEJTE", + progress__processing: "PROCESOVÁNÍ", + progress__refreshing: "Obnovování...", + progress__signing_transaction: "Podpis transakce...", + progress__syncing: "Synchronizace...", + progress__x_seconds_left_template: "Zbývá {} sekund", + reboot_to_bootloader__restart: "Chcete restartovat Trezor v režimu bootloader?", + reboot_to_bootloader__title: "PŘEJÍT DO REŽIMU BOOTLOADER", + reboot_to_bootloader__version_by_template: "Verze firmwaru {}\nod {}", + recovery__cancel_dry_run: "Zrušit kontrolu zálohy", + recovery__check_dry_run: "Zkontrolovat zálohu?", + recovery__cursor_will_change: "Pozice kurzoru se změní mezi položkami pro zvýšenou bezpečnost.", + recovery__dry_run_bip39_valid_match: "Zadané záchranné semínko je platné a odpovídá tomu v zařízení.", + recovery__dry_run_bip39_valid_mismatch: "Zadané záchranné semínko je platné, ale neodpovídá tomu v zařízení.", + recovery__dry_run_slip39_valid_match: "Zadané záchranné podíly jsou platné a odpovídají tomu, co je aktuálně v zařízení.", + recovery__dry_run_slip39_valid_mismatch: "Zadané záchranné podíly jsou platné, ale neodpovídají tomu, co je aktuálně v zařízení.", + recovery__enter_any_share: "Zadejte libovolný podíl", + recovery__enter_backup: "Zadejte svou zálohu", + recovery__enter_different_share: "Zadejte prosím odlišný podíl.", + recovery__enter_share_from_diff_group: "Zadejte podíl z odlišné skupiny.", + recovery__group_num_template: "Skupina {}", + recovery__group_threshold_reached: "Práh skupiny dosažen.", + recovery__invalid_seed_entered: "Zadáno neplatné záchranné semínko.", + recovery__invalid_share_entered: "Zadán neplatný záchranný podíl.", + recovery__more_shares_needed: "Potřebuje se více podílů", + recovery__num_of_words: "Vyberte počet slov ve vaší záloze.", + recovery__only_first_n_letters: "Budete muset vybrat pouze první 2-4 písmena každého slova.", + recovery__progress_will_be_lost: "Veškerý pokrok bude ztracen.", + recovery__select_num_of_words: "Vyberte počet slov ve vaší záloze.", + recovery__share_already_entered: "Podíl již byl zadán", + recovery__share_from_another_shamir: "Zadali jste podíl z jiného Shamir Backupu.", + recovery__share_num_template: "Podíl {}", + recovery__title: "OBNOVIT PENĚŽENKU", + recovery__title_cancel_dry_run: "ZRUŠIT KONTROLU ZÁLOHY", + recovery__title_cancel_recovery: "ZRUŠIT OBNOVENÍ", + recovery__title_dry_run: "KONTROLA ZÁLOHY", + recovery__title_recover: "OBNOVIT PENĚŽENKU", + recovery__title_remaining_shares: "ZBÝVAJÍCÍ PODÍLY", + recovery__type_word_x_of_y_template: "Napište slovo {} z {}", + recovery__wallet_recovered: "Peněženka úspěšně obnovena", + recovery__wanna_cancel_dry_run: "Jste si jisti, že chcete zrušit kontrolu zálohy?", + recovery__wanna_cancel_recovery: "Jste si jisti, že chcete zrušit proces obnovení?", + recovery__word_count_template: "({} slov)", + recovery__word_x_of_y_template: "SLOVO {} Z {}", + recovery__x_more_items_starting_template_plural: "Ještě {count} {plural} začínající", + recovery__x_more_shares_needed_template_plural: "Potřebujete ještě {count} {plural}.", + recovery__x_of_y_entered_template: "{} z {} podílů úspěšně zadáno.", + recovery__you_have_entered: "Zadali jste", + reset__advanced_group_threshold_info: "Prah skupiny určuje počet skupin potřebných k obnovení vaší peněženky.", + reset__all_x_of_y_template: "všechny {} z {} podílů", + reset__any_x_of_y_template: "libovolný {} z {} podílů", + reset__button_create: "VYTVOŘIT PENĚŽENKU", + reset__button_recover: "OBNOVIT PENĚŽENKU", + reset__by_continuing: "Pokračováním souhlasíte s podmínkami společnosti Trezor.", + reset__check_backup_title: "ZKONTROLOVAT ZÁLOHU", + reset__check_group_share_title_template: "KONTROLA G{} - PODÍL {}", + reset__check_seed_title: "ZKONTROLOVAT SEED", + reset__check_share_title_template: "KONTROLA PODÍL #{}", + reset__continue_with_next_share: "Pokračujte s dalším podílem.", + reset__continue_with_share_template: "Pokračujte s podílem #{}.", + reset__finished_verifying_group_template: "Dokončili jste ověření svých záchranných podílů pro skupinu {}.", + reset__finished_verifying_seed: "Dokončili jste ověření svého záchranného semínka.", + reset__finished_verifying_shares: "Dokončili jste ověření svých záchranných podílů.", + reset__group_description: "Skupina se skládá z obnovovacích podílů.", + reset__group_info: "Každá skupina má stanovený počet podílů a vlastní práh. V dalších krocích nastavíte počty podílů a prahy.", + reset__group_share_checked_successfully_template: "Skupina {} - Podíl {} úspěšně zkontrolován.", + reset__group_share_title_template: "SKUPINA {} - PODÍL {}", + reset__more_info_at: "Více informací na", + reset__need_all_share_template: "K obnovení budete potřebovat všechny {} podíly.", + reset__need_any_share_template: "K obnovení budete potřebovat libovolný {} z podílů.", + reset__needed_to_form_a_group: "potřebné k vytvoření skupiny. ", + reset__needed_to_recover_your_wallet: "potřebné k obnovení vaší peněženky. ", + reset__never_make_digital_copy: "Nikdy nedělejte digitální kopii své zálohy nebo ji neukládejte online!", + reset__num_of_share_holders_template: "{} lidí nebo míst bude držet jeden podíl.", + reset__num_of_shares_advanced_info_template: "Každý obnovovací podíl je posloupnost 20 slov. V dalším kroku zvolíte prahový počet podílů potřebných k vytvoření skupiny {}.", + reset__num_of_shares_basic_info: "Každý obnovovací podíl je posloupnost 20 slov. V dalším kroku zvolíte, kolik podílů potřebujete k obnovení vaší peněženky.", + reset__num_shares_for_group_template: "Požadovaný počet podílů pro vytvoření skupiny {}.", + reset__number_of_shares_info: "= celkový počet unikátních seznamů slov použitých pro zálohu peněženky.", + reset__one_share: "1 podíl", + reset__only_one_share_will_be_created: "Bude vytvořen pouze jeden podíl.", + reset__recovery_seed_title: "OBNOVIT SEED", + reset__recovery_share_title_template: "OBNOVIT PODÍL #{}", + reset__required_number_of_groups: "Požadovaný počet skupin pro obnovení.", + reset__select_correct_word: "Vyberte správné slovo pro každou pozici.", + reset__select_word_template: "VYBRAT {} SLOVO", + reset__select_word_x_of_y_template: "Vyberte slovo {} z {}:", + reset__set_it_to_count_template: "Nastavte na {} a budete potřebovat ", + reset__share_checked_successfully_template: "Záchranný podíl #{} úspěšně zkontrolován.", + reset__share_words_title: "STANDARDNÍ ZÁLOHA", + reset__slip39_checklist_num_groups: "Počet skupin", + reset__slip39_checklist_num_shares: "Počet podílů", + reset__slip39_checklist_set_num_groups: "Nastavit počet skupin", + reset__slip39_checklist_set_num_shares: "Nastavit počet akcií", + reset__slip39_checklist_set_sizes: "Nastavit velikosti a prahy", + reset__slip39_checklist_set_sizes_longer: "Nastavit velikosti a prahové hodnoty pro každou skupinu", + reset__slip39_checklist_set_threshold: "Nastavit práh", + reset__slip39_checklist_title: "KONTROLNÍ SEZNAM ZÁLOHY", + reset__slip39_checklist_write_down: "Zapsat a ověřit podíly", + reset__slip39_checklist_write_down_recovery: "Zapište si a zkontrolujte všechny obnovovací akcie", + reset__the_threshold_sets_the_number_of_shares: "Prah určuje počet podílů ", + reset__threshold_info: "= minimální počet unikátních seznamů slov použitých pro obnovení.", + reset__title_backup_is_done: "ZÁLOHA JE HOTOVÁ", + reset__title_create_wallet: "VYTVOŘIT PENĚŽENKU", + reset__title_create_wallet_shamir: "VYTVOŘIT PENĚŽENKU (SHAMIR)", + reset__title_group_threshold: "PRÁH SKUPINY", + reset__title_number_of_groups: "POČET SKUPIN", + reset__title_number_of_shares: "POČET PODÍLŮ", + reset__title_set_group_threshold: "NASTAVIT PRAHOVÝ POČET SKUPIN", + reset__title_set_number_of_groups: "NASTAVIT POČET SKUPIN", + reset__title_set_number_of_shares: "NASTAVIT POČET PODÍLŮ", + reset__title_set_threshold: "NASTAVIT PRAH", + reset__to_form_group_template: "pro vytvoření skupiny {}.", + reset__tos_link: "trezor.io/tos", + reset__total_number_of_shares_in_group_template: "Nastavte celkový počet podílů ve skupině {}.", + reset__use_your_backup: "Použijte zálohu, pokud potřebujete obnovit svou peněženku.", + reset__write_down_words_template: "Zapište všechna {} slova v pořadí.", + reset__wrong_word_selected: "Vybráno nesprávné slovo!", + reset__you_need_one_share: "K obnovení potřebujete 1 podíl.", + reset__your_backup_is_done: "Vaše záloha je hotova.", + ripple__confirm_tag: "Potvrdit značku", + ripple__destination_tag_template: "Cílová značka: {}", + rotation__change_template: "Chcete změnit rotaci zařízení na {}?", + rotation__east: "východ", + rotation__north: "sever", + rotation__south: "jih", + rotation__title_change: "ZMĚNIT ROTACI", + rotation__west: "západ", + safety_checks__approve_unsafe_always: "Trezor vám umožní schválit některé akce, které by mohly být nebezpečné.", + safety_checks__approve_unsafe_temporary: "Trezor vám dočasně umožní schválit některé akce, které by mohly být nebezpečné.", + safety_checks__enforce_strict: "Opravdu chcete vynutit přísné kontroly bezpečnosti (doporučeno)?", + safety_checks__title: "KONTROLY BEZPEČNOSTI", + safety_checks__title_safety_override: "PŘEPÍNÁNÍ BEZPEČNOSTI", + sd_card__all_data_will_be_lost: "Všechna data na SD kartě budou ztracena.", + sd_card__card_required: "Vyžadována SD karta.", + sd_card__disable: "Opravdu chcete odebrat ochranu SD karty ze svého zařízení?", + sd_card__disabled: "Úspěšně jste zakázali ochranu SD karty.", + sd_card__enable: "Opravdu chcete zajistit své zařízení ochranou SD karty?", + sd_card__enabled: "Úspěšně jste povolili ochranu SD karty.", + sd_card__error: "Chyba SD karty", + sd_card__format_card: "Naformátovat SD kartu", + sd_card__insert_correct_card: "Vložte do zařízení správnou SD kartu.", + sd_card__please_insert: "Vložte prosím vaši SD kartu.", + sd_card__please_unplug_and_insert: "Odpojte prosím zařízení a vložte vaši SD kartu.", + sd_card__problem_accessing: "Došlo k problému při přístupu k SD kartě.", + sd_card__refresh: "Opravdu chcete nahradit aktuální tajemství SD karty nově vygenerovaným?", + sd_card__refreshed: "Úspěšně jste obnovili ochranu SD karty.", + sd_card__restart: "Chcete restartovat Trezor v režimu bootloader?", + sd_card__title: "OCHRANA SD KARTY", + sd_card__title_problem: "PROBLÉM SE SD KARTOU", + sd_card__unknown_filesystem: "Neznámý souborový systém", + sd_card__unplug_and_insert_correct: "Odpojte zařízení a vložte správnou SD kartu.", + sd_card__use_different_card: "Použijte jinou kartu nebo naformátujte SD kartu na souborový systém FAT32", + sd_card__wanna_format: "Opravdu chcete naformátovat SD kartu?", + sd_card__wrong_sd_card: "Špatná SD karta", + send__address_path: "Adresová cesta", + send__amount: "Částka:", + send__confirm_sending: "Potvrdit_odeslání", + send__from_multiple_accounts: "Odesílání z více účtů.", + send__including_fee: "Včetně poplatku:", + send__maximum_fee: "Maximální poplatek:", + send__receiving_to_multisig: "Přijímání na multisig adresu.", + send__title_amount: "ČÁSTKA", + send__title_confirm_sending: "POTVRDIT ODESLÁNÍ", + send__title_joint_transaction: "SPOLEČNÁ TRANSAKCE", + send__title_receiving_to: "PŘIJÍMÁNÍ NA", + send__title_recipient: "PŘÍJEMCE", + send__title_sending: "ODESÍLÁNÍ", + send__title_sending_amount: "ČÁSTKA K ODESLÁNÍ", + send__title_sending_to: "ODESLÁNÍ NA", + send__to_the_total_amount: "Celková částka:", + send__total_amount: "Celková částka:", + send__transaction_id: "ID transakce:", + send__you_are_contributing: "Přispíváte:", + share_words__words_in_order: " slova v pořadí.", + share_words__wrote_down_all: "Napsal jsem všechny ", + sign_message__bytes_template: "{} Bajty", + sign_message__confirm_address: "POTVRDIT ADRESU", + sign_message__confirm_message: "POTVRDIT ZPRÁVU", + sign_message__message_size: "Velikost zprávy:", + sign_message__verify_address: "Ověřte adresu", + stellar__account: "Účet", + stellar__account_merge: "Sloučení účtů", + stellar__account_thresholds: "Práhové hodnoty účtu", + stellar__add_signer: "Přidat podepisovatele", + stellar__add_trust: "Přidat důvěru", + stellar__all_will_be_sent_to: "Vše XLM bude odesláno na:", + stellar__allow_trust: "Povolit důvěru", + stellar__asset: "Aktiva", + stellar__bump_sequence: "Zvýšit sekvenci", + stellar__buying: "Kupování:", + stellar__clear_data: "Vymazat data", + stellar__clear_flags: "Vymazat příznaky", + stellar__confirm_issuer: "Potvrdit vydavatele", + stellar__confirm_memo: "Potvrdit poznámku", + stellar__confirm_network: "Potvrdit síť", + stellar__confirm_operation: "Potvrdit operaci", + stellar__confirm_stellar: "Potvrdit Stellar", + stellar__confirm_timebounds: "Potvrdit časová omezení", + stellar__create_account: "Vytvořit účet", + stellar__debited_amount: "Odepsaná částka", + stellar__delete: "Smazat", + stellar__delete_passive_offer: "Smazat pasivní nabídku", + stellar__delete_trust: "Smazat důvěru", + stellar__destination: "Cíl:", + stellar__exchanges_require_memo: "Důležité: Mnoho směnáren vyžaduje poznámku při vkladu", + stellar__final_confirm: "Konečné potvrzení", + stellar__hash: "Haš:", + stellar__high: "Vysoký:", + stellar__home_domain: "Domovská doména", + stellar__inflation: "Inflace", + stellar__initial_balance: "Počáteční zůstatek", + stellar__initialize_signing_with: "Inicializovat podepisování s", + stellar__issuer_template: "{} vydavatel:", + stellar__key: "Klíč:", + stellar__limit: "Limit:", + stellar__low: "Nízký:", + stellar__master_weight: "Hlavní váha:", + stellar__medium: "Střední:", + stellar__new_offer: "Nová nabídka", + stellar__new_passive_offer: "Nová pasivní nabídka", + stellar__no_memo_set: "Žádná poznámka nenastavena!", + stellar__no_restriction: "[bez omezení]", + stellar__on_network_template: "Transakce je na {}", + stellar__path_pay: "Platba cestou", + stellar__path_pay_at_least: "Platba cestou alespoň", + stellar__pay: "Platit:", + stellar__pay_at_most: "Zaplatit nejvýše:", + stellar__preauth_transaction: "Předautentizovaná transakce:", + stellar__price_per_template: "Cena za {}:", + stellar__private_network: "soukromá síť", + stellar__remove_signer: "Odebrat podepisovatele", + stellar__revoke_trust: "Zrušit důvěru", + stellar__selling: "Prodej:", + stellar__set_data: "Nastavit data", + stellar__set_flags: "Nastavit příznaky", + stellar__set_sequence_to_template: "Nastavit sekvenci na {}?", + stellar__sign_tx_count_template: "Podepsat tuto transakci složenou z {}", + stellar__sign_tx_fee_template: "a zaplatit {} za poplatek?", + stellar__source_account: "Zdrojový účet:", + stellar__testnet_network: "testovací síť", + stellar__trusted_account: "Důvěryhodný účet", + stellar__update: "Aktualizovat", + stellar__valid_from: "Platné od (UTC)", + stellar__valid_to: "Platné do (UTC)", + stellar__value_sha256: "Hodnota (SHA-256):", + stellar__wanna_clean_value_key_template: "Chcete vymazat klíč hodnoty {}?", + stellar__your_account: "váš účet", + tezos__address: "Adresa:", + tezos__amount: "Částka:", + tezos__baker_address: "Adresa pekaře:", + tezos__balance: "Zůstatek:", + tezos__ballot: "Hlasovací lístek:", + tezos__confirm_delegation: "Potvrdit delegaci", + tezos__confirm_origination: "Potvrdit vznik", + tezos__delegator: "Delegátor:", + tezos__fee: "Poplatek:", + tezos__proposal: "Návrh", + tezos__register_delegate: "Registrovat delegáta", + tezos__remove_delegation: "Odstranit delegaci", + tezos__submit_ballot: "Odeslat hlasovací lístek", + tezos__submit_proposal: "Předložit návrh", + tezos__submit_proposals: "Předložit návrhy", + tutorial__middle_click: "Stiskněte současně vlevo a vpravo pro potvrzení.", + tutorial__press_and_hold: "Stiskněte a podržte pravé tlačítko pro schválení důležitých operací.", + tutorial__ready_to_use: "Jste připraveni používat Trezor.", + tutorial__scroll_down: "Stiskněte vpravo pro posun dolů a přečtěte si veškerý obsah, pokud text nezapadne na jednu obrazovku. Stiskněte vlevo pro posun nahoru.", + tutorial__sure_you_want_skip: "Jste si jisti, že chcete tutoriál přeskočit?", + tutorial__title_hello: "AHOJ", + tutorial__title_screen_scroll: "POSUN OBRAZOVKY", + tutorial__title_skip: "PŘESKOČIT TUTORIÁL", + tutorial__title_tutorial_complete: "TUTORIÁL DOKONČEN", + tutorial__use_trezor: "Trezor má levé a pravé tlačítko.\n\rPokračujte vpravo.", + tutorial__welcome_press_right: "Vítejte v Trezoru. Stiskněte vpravo pro pokračování.", + u2f__get: "Zvýšit a získat U2F čítač?", + u2f__set_template: "Nastavit U2F čítač na {}?", + u2f__title_get: "ZÍSKAT U2F ČÍTAČ", + u2f__title_set: "NASTAVIT U2F ČÍTAČ", + wipe__info: "Všechna data budou smazána.", + wipe__title: "VYMAZAT ZAŘÍZENÍ", + wipe__want_to_wipe: "Opravdu chcete vymazat zařízení?\n", + wipe_code__change: "Změnit kód pro vymazání?", + wipe_code__changed: "Kód pro vymazání změněn", + wipe_code__diff_from_pin: "Kód pro vymazání musí být odlišný od vašeho PINu.\nZkuste to prosím znovu.", + wipe_code__disabled: "Kód pro vymazání zakázán.", + wipe_code__enabled: "Kód pro vymazání povolen.", + wipe_code__enter_new: "Zadejte nový kód pro vymazání", + wipe_code__info: "Kód vymaže všechna data z tohoto zařízení.", + wipe_code__invalid: "Neplatný kód pro vymazání", + wipe_code__mismatch: "Zadané kódy pro vymazání nesouhlasí!", + wipe_code__reenter: "Znovu zadejte kód pro vymazání", + wipe_code__reenter_to_confirm: "Pro potvrzení znovu zadejte kód pro vymazání.", + wipe_code__title_check: "ZKONTROLOVAT KÓD PRO VYMAZÁNÍ", + wipe_code__title_invalid: "NEPLATNÝ KÓD PRO VYMAZÁNÍ", + wipe_code__title_settings: "NASTAVENÍ VÝMAZU", + wipe_code__turn_off: "Vypnout ochranu kódem pro vymazání?", + wipe_code__turn_on: "Zapnout ochranu kódem pro vymazání?", + wipe_code__wipe_code_mismatch: "NESHODA KÓDU PRO VYMAZÁNÍ", + word_count__title: "POČET SLOV", + words__are_you_sure: "Jste si jisti?", + words__array_of: "Pole o", + words__buying: "Koupit", + words__confirm: "Potvrdit", + words__contains: "Obsahuje", + words__continue_anyway: "Přesto pokračovat?", + words__continue_with: "Pokračovat s", + words__error: "Chyba", + words__from: "od", + words__keep_it_safe: "Uchovávejte v bezpečí!", + words__know_what_your_doing: "Pokračujte pouze, pokud víte, co děláte!", + words__my_trezor: "Můj Trezor", + words__outputs: "výstupy", + words__please_check_again: "Zkontrolujte to prosím znovu", + words__please_try_again: "Zkuste to prosím znovu", + words__really_wanna: "Opravdu chcete", + words__sign: "Podepsat", + words__title_check: "KONTROLA", + words__title_group: "SKUPINA", + words__title_information: "INFORMACE", + words__title_remember: "PAMATOVAT", + words__title_share: "PODÍL", + words__title_shares: "PODÍLY", + words__title_success: "ÚSPĚCH", + words__title_summary: "SHRNUTÍ", + words__title_threshold: "PRÁH", + words__unknown: "Neznámý", + words__warning: "Varování", +}; diff --git a/core/embed/rust/src/ui/translations/cs.rs.mako b/core/embed/rust/src/ui/translations/cs.rs.mako new file mode 100644 index 000000000..edc764f89 --- /dev/null +++ b/core/embed/rust/src/ui/translations/cs.rs.mako @@ -0,0 +1,33 @@ +//! generated from cs.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +// NOTE: not used as a code, only for +// documentation purposes + +<% +import json + +from pathlib import Path + +THIS = Path(local.filename).resolve() +SRCDIR = THIS.parent + +file = SRCDIR / "cs.json" + +data = json.loads(file.read_text())["translations"] +items_to_write: list[tuple[str, str]] = [] +for section_name, section in data.items(): + for k, v in section.items(): + name = f"{section_name}__{k}" + items_to_write.append((name, v)) +items_to_write.sort(key=lambda x: x[0]) +%>\ +use super::general::TranslationsGeneral; + +#[rustfmt::skip] +pub const TRANSLATIONS: TranslationsGeneral = TranslationsGeneral { +% for k, v in items_to_write: + ${k}: ${utf8_str(v)}, +% endfor +}; diff --git a/core/embed/rust/src/ui/translations/en.json b/core/embed/rust/src/ui/translations/en.json new file mode 100644 index 000000000..d29f997c5 --- /dev/null +++ b/core/embed/rust/src/ui/translations/en.json @@ -0,0 +1,939 @@ +{ + "header": { + "language": "en", + "version": "2.6.4" + }, + "translations": { + "addr_mismatch": { + "contact_support": "Please contact Trezor support at", + "key_mismatch": "Key mismatch?", + "mismatch": "Address mismatch?", + "support_url": "trezor.io/support", + "title": "ADDRESS MISMATCH?", + "title_key_mismatch": "KEY MISMATCH?", + "wrong_derication_path": "Wrong derivation path for selected account.", + "xpub_mismatch": "XPUB mismatch?" + }, + "address": { + "address": "Address:", + "public_key": "Public key", + "title_cosigner": "COSIGNER", + "title_receive_address": "RECEIVE ADDRESS", + "title_yours": "YOURS" + }, + "address_details": { + "account": "Account:", + "derivation_path": "Derivation path:", + "title_receive_address": "RECEIVE ADDRESS", + "title_receiving_to": "RECEIVING TO" + }, + "authenticate": { + "confirm_template": "Allow connected computer to confirm your {} is genuine?", + "header": "Authenticate device" + }, + "auto_lock": { + "change_template": "Auto-lock your Trezor after {} of inactivity?", + "title": "AUTO-LOCK DELAY" + }, + "backup": { + "can_back_up_anytime": "You can back up your Trezor once, at any time.", + "it_should_be_backed_up": "You should back up your new wallet right now.", + "it_should_be_backed_up_now": "It should be backed up now!", + "new_wallet_created": "New wallet created.", + "new_wallet_successfully_created": "New wallet created successfully.", + "recover_anytime": "You can use your backup to recover your wallet at any time.", + "title_backup_wallet": "BACK UP WALLET", + "title_skip": "SKIP BACKUP", + "want_to_skip": "Are you sure you want to skip the backup?" + }, + "binance": { + "buy": "Buy", + "confirm_cancel": "Confirm cancel", + "confirm_input": "Confirm input", + "confirm_order": "Confirm order", + "confirm_output": "Confirm output", + "order_id": "Order ID:", + "pair": "Pair:", + "price": "Price:", + "quantity": "Quantity:", + "sell": "Sell", + "sender_address": "Sender address:", + "side": "Side:", + "unknown": "Unknown" + }, + "bitcoin": { + "commitment_data": "Commitment data:", + "confirm_locktime": "Confirm locktime", + "create_proof_of_ownership": "Do you want to create a proof of ownership?", + "high_mining_fee_template": "The mining fee of\n{}\nis unexpectedly high.", + "locktime_no_effect": "Locktime is set but will have no effect.", + "locktime_set_to": "Locktime set to:", + "locktime_set_to_blockheight": "Locktime set to blockheight:", + "lot_of_change_outputs": "A lot of change-outputs.", + "multiple_accounts": "Multiple accounts", + "new_fee_rate": "New fee rate:", + "simple_send_of": "Simple send of", + "ticket_amount": "Ticket amount:", + "title_confirm_details": "CONFIRM DETAILS", + "title_finalize_transaction": "FINALIZE TRANSACTION", + "title_high_mining_fee": "HIGH MINING FEE", + "title_meld_transaction": "MELD TRANSACTIONS", + "title_modify_amount": "MODIFY AMOUNT", + "title_payjoin": "PAYJOIN", + "title_proof_of_ownership": "PROOF OF OWNERSHIP", + "title_purchase_ticket": "PURCHASE TICKET", + "title_update_transaction": "UPDATE TRANSACTION", + "unknown_path": "Unknown path", + "unknown_transaction": "Unknown transaction", + "unusually_high_fee": "Unusually high fee.", + "unverified_external_inputs": "The transaction contains unverified external inputs.", + "valid_signature": "The signature is valid.", + "voting_rights": "Voting rights to:" + }, + "buttons": { + "abort": "ABORT", + "access": "ACCESS", + "again": "AGAIN", + "allow": "ALLOW", + "back_up": "BACK UP", + "cancel": "CANCEL", + "change": "CHANGE", + "check": "CHECK", + "check_again": "CHECK AGAIN", + "close": "CLOSE", + "confirm": "CONFIRM", + "continue": "CONTINUE", + "details": "DETAILS", + "enable": "ENABLE", + "enter": "ENTER", + "enter_share": "ENTER SHARE", + "export": "EXPORT", + "format": "FORMAT", + "go_back": "GO BACK", + "hold_to_confirm": "HOLD TO CONFIRM", + "info": "INFO", + "install": "INSTALL", + "more_info": "MORE INFO", + "ok_i_understand": "OK, I UNDERSTAND", + "purchase": "PURCHASE", + "quit": "QUIT", + "restart": "RESTART", + "retry": "RETRY", + "select": "SELECT", + "set": "SET", + "show_all": "SHOW ALL", + "show_words": "SHOW WORDS", + "skip": "SKIP", + "try_again": "TRY AGAIN", + "turn_off": "TURN OFF", + "turn_on": "TURN ON" + }, + "cardano": { + "addr_base": "Base", + "addr_enterprise": "Enterprise", + "addr_legacy": "Legacy", + "addr_pointer": "Pointer", + "addr_reward": "Reward", + "address_no_staking": "address - no staking rewards.", + "amount": "Amount:", + "amount_burned_decimals_unknown": "Amount burned (decimals unknown):", + "amount_minted_decimals_unknown": "Amount minted (decimals unknown):", + "amount_sent_decimals_unknown": "Amount sent (decimals unknown):", + "anonymous_pool": "Pool has no metadata (anonymous pool)", + "asset_fingerprint": "Asset fingerprint:", + "auxiliary_data_hash": "Auxiliary data hash:", + "block": "Block", + "catalyst": "Catalyst", + "certificate": "Certificate", + "certificate_path": "Certificate path", + "change_output": "Change output", + "change_output_path": "Change output path", + "change_output_staking_path": "Change output staking path", + "check_all_items": "Check all items carefully.", + "choose_level_of_details": "Choose level of details:", + "collateral_input_id": "Collateral input ID:", + "collateral_input_index": "Collateral input index:", + "collateral_output_contains_tokens": "The collateral return output contains tokens.", + "collateral_return": "Collateral return", + "confirm": "Confirm:", + "confirm_signing_stake_pool": "Confirm signing the stake pool registration as an owner.", + "confirm_transaction": "Confirm transaction", + "confirming_a_multisig_transaction": "Confirming a multisig transaction.", + "confirming_pool_registration": "Confirming pool registration as owner.", + "confirming_transction": "Confirming a transaction.", + "cost": "Cost", + "credential_mismatch": "Credential doesn't match payment credential.", + "datum_hash": "Datum hash:", + "delegating_to": "Delegating to:", + "for_account_and_index_template": "for account {} and index {}:", + "for_account_template": "for account {}:", + "for_key_hash": "for key hash:", + "for_script": "for script:", + "inline_datum": "Inline datum", + "input_id": "Input ID:", + "input_index": "Input index:", + "intro_text_address": "Address", + "intro_text_change": "The following address is a change address. Its", + "intro_text_owned_by_device": "The following address is owned by this device. Its", + "intro_text_registration_payment": "The vote key registration payment address is owned by this device. Its", + "key_hash": "key hash", + "margin": "Margin", + "multisig_path": "multi-sig path", + "nested_scripts_template": "Contains {} nested scripts.", + "network": "Network:", + "no_output_tx": "Transaction has no outputs, network cannot be verified.", + "nonce": "Nonce:", + "other": "other", + "path": "path", + "pledge": "Pledge", + "pointer": "pointer", + "policy_id": "Policy ID:", + "pool_metadata_hash": "Pool metadata hash:", + "pool_metadata_url": "Pool metadata url:", + "pool_owner": "Pool owner:", + "pool_owner_path": "Pool owner staking path", + "pool_reward_account": "Pool reward account:", + "reference_input_id": "Reference input ID:", + "reference_input_index": "Reference input index:", + "reference_script": "Reference script", + "required_signer": "Required signer", + "reward": "reward", + "reward_address": "Address is a reward address.", + "reward_eligibility_warning": "Warning: The address is not a payment address, it is not eligible for rewards.", + "rewards_go_to": "Rewards go to:", + "script": "script", + "script_all": "All", + "script_any": "Any", + "script_data_hash": "Script data hash:", + "script_hash": "Script hash:", + "script_invalid_before": "Invalid before", + "script_invalid_hereafter": "Invalid hereafter", + "script_key": "Key", + "script_n_of_k": "N of K", + "script_reward": "script reward", + "sending": "Sending", + "show_simple": "Show Simple", + "sign_tx_path_template": "Sign transaction with {}:", + "stake_delegation": "Stake delegation", + "stake_deregistration": "Stake key deregistration", + "stake_pool_registration": "Stakepool registration", + "stake_pool_registration_pool_id": "Stake pool registration\nPool ID:", + "stake_registration": "Stake key registration", + "staking_key_for_account": "Staking key for account", + "to_pool": "to pool:", + "token_minting_path": "token minting path", + "total_collateral": "Total collateral:", + "transaction": "Transaction", + "transaction_contains_minting_or_burning": "The transaction contains minting or burning of tokens.", + "transaction_contains_script_address_no_datum": "The following transaction output contains a script address, but does not contain a datum.", + "transaction_fee": "Transaction fee:", + "transaction_id": "Transaction ID:", + "transaction_no_collateral_input": "The transaction contains no collateral inputs. Plutus script will not be able to run.", + "transaction_no_script_data_hash": "The transaction contains no script data hash. Plutus script will not be able to run.", + "transaction_output_contains_tokens": "The following transaction output contains tokens.", + "ttl": "TTL:", + "unknown": "Unknown", + "unknown_collateral_amount": "Unknown collateral amount.", + "unusual_path": "Path is unusual.", + "valid_since": "Valid since:", + "verify_script": "Verify script", + "vote_key_registration": "Vote key registration (CIP-36)", + "vote_public_key": "Vote public key:", + "voting_purpose": "Voting purpose:", + "warning": "Warning", + "weight": "Weight:", + "withdrawal_for_address_template": "Confirm withdrawal for {} address:", + "witness_path": "Witness path", + "x_of_y_signatures_template": "Requires {} out of {} signatures." + }, + "coinjoin": { + "access_account": "Access your coinjoin account?", + "do_not_disconnect": "Do not disconnect your Trezor!", + "max_mining_fee": "Max mining fee", + "max_rounds": "Max rounds", + "title": "AUTHORIZE COINJOIN", + "title_do_not_disconnect": "DO NOT DISCONNECT YOUR TREZOR!", + "title_progress": "COINJOIN IN PROGRESS", + "waiting_for_others": "Waiting for others" + }, + "confirm_total": { + "account": "Account:", + "fee_rate": "Fee rate:", + "sending_from_account": "Sending from account:", + "title_fee": "FEE INFORMATION", + "title_sending_from": "SENDING FROM" + }, + "debug": { + "loading_seed": "Loading seed", + "loading_seed_not_recommended": "Loading private seed is not recommended." + }, + "device_name": { + "change_template": "Change device name to {}?", + "title": "DEVICE NAME" + }, + "entropy": { + "send": "Do you really want to send entropy?", + "title": "INTERNAL ENTROPY", + "title_confirm": "CONFIRM ENTROPY" + }, + "eos": { + "about_to_sign_template": "You are about to sign {}.", + "account": "Account:", + "action_name": "Action Name:", + "amount": "Amount:", + "arbitrary_data": "Arbitrary data", + "buy_ram": "Buy RAM", + "bytes": "Bytes:", + "cancel_vote": "Cancel vote", + "checksum": "Checksum:", + "code": "Code:", + "contract": "Contract:", + "cpu": "CPU:", + "creator": "Creator:", + "delegate": "Delegate", + "delete_auth": "Delete Auth", + "from": "From:", + "link_auth": "Link Auth", + "memo": "Memo", + "name": "Name:", + "net": "NET:", + "new_account": "New account", + "no": "No", + "owner": "Owner:", + "parent": "Parent:", + "payer": "Payer:", + "permission": "Permission:", + "proxy": "Proxy:", + "receiver": "Receiver:", + "refund": "Refund", + "requirement": "Requirement:", + "sell_ram": "Sell RAM", + "sender": "Sender:", + "sign_transaction": "Sign transaction", + "threshold": "Threshold:", + "to": "To:", + "transfer": "Transfer:", + "type": "Type:", + "undelegate": "Undelegate", + "unlink_auth": "Unlink Auth", + "update_auth": "Update Auth", + "vote_for_producers": "Vote for producers", + "vote_for_proxy": "Vote for proxy", + "voter": "Voter:", + "yes": "Yes" + }, + "ethereum": { + "amount_sent": "Amount sent:", + "confirm_fee": "Confirm fee", + "contract": "Contract:", + "data_size_template": "Size: {} bytes", + "gas_limit": "Gas limit:", + "gas_price": "Gas price:", + "max_gas_price": "Max gas price:", + "name_and_version": "Name and version", + "new_contract": "new contract?", + "no_message_field": "No message field", + "priority_fee": "Priority fee:", + "show_full_array": "Show full array", + "show_full_domain": "Show full domain", + "show_full_message": "Show full message", + "show_full_struct": "Show full struct", + "sign_eip712": "Really sign EIP-712 typed data?", + "title_confirm_data": "CONFIRM DATA", + "title_confirm_domain": "CONFIRM DOMAIN", + "title_confirm_message": "CONFIRM MESSAGE", + "title_confirm_struct": "CONFIRM STRUCT", + "title_confirm_typed_data": "CONFIRM TYPED DATA", + "title_signing_address": "SIGNING ADDRESS", + "units_template": "{} units", + "unknown_token": "Unknown token", + "valid_signature": "The signature is valid." + }, + "experimental_mode": { + "enable": "Enable experimental features?", + "only_for_dev": "Only for development and beta testing!", + "title": "EXPERIMENTAL MODE" + }, + "fido": { + "already_registered": "Already registered", + "device_already_registered": "This device is already registered with this application.", + "device_already_registered_with_template": "This device is already registered with {}.", + "device_not_registered": "This device is not registered with this application.", + "does_not_belong": "The credential you are trying to import does\nnot belong to this authenticator.", + "erase_credentials": "erase all credentials?", + "export_credentials": "Export information about the credentials stored on this device?", + "not_registered": "Not registered", + "not_registered_with_template": "This device is not registered with\n{}.", + "please_enable_pin_protection": "Please enable PIN protection.", + "title_authenticate": "FIDO2 AUTHENTICATE", + "title_import_credential": "IMPORT CREDENTIAL", + "title_list_credentials": "LIST CREDENTIALS", + "title_register": "FIDO2 REGISTER", + "title_remove_credential": "REMOVE CREDENTIAL", + "title_reset": "FIDO2 RESET", + "title_u2f_auth": "U2F AUTHENTICATE", + "title_u2f_register": "U2F REGISTER", + "title_verify_user": "FIDO2 VERIFY USER", + "unable_to_verify_user": "Unable to verify user.", + "wanna_erase_credentials": "Do you really want to erase all credentials?" + }, + "firmware_update": { + "title": "UPDATE FIRMWARE", + "title_fingerprint": "FW FINGERPRINT" + }, + "homescreen": { + "click_to_connect": "Click to Connect", + "click_to_unlock": "Click to Unlock", + "title_backup_failed": "BACKUP FAILED", + "title_backup_needed": "BACKUP NEEDED", + "title_coinjoin_authorized": "COINJOIN AUTHORIZED", + "title_experimental_mode": "EXPERIMENTAL MODE", + "title_hold_to_lock": "HOLD TO LOCK", + "title_no_usb_connection": "NO USB CONNECTION", + "title_pin_not_set": "PIN NOT SET", + "title_seedless": "SEEDLESS", + "title_set": "CHANGE HOMESCREEN?" + }, + "inputs": { + "back": "BACK", + "cancel": "CANCEL", + "delete": "DELETE", + "enter": "ENTER", + "return": "RETURN", + "show": "SHOW", + "space": "SPACE" + }, + "joint": { + "title": "JOINT TRANSACTION", + "to_the_total_amount": "To the total amount:", + "you_are_contributing": "You are contributing:" + }, + "lockscreen": { + "tap_to_connect": "Tap to connect", + "tap_to_unlock": "Tap to unlock", + "title_locked": "LOCKED", + "title_not_connected": "NOT CONNECTED" + }, + "misc": { + "decrypt_value": "Decrypt value", + "encrypt_value": "Encrypt value", + "title_suite_labeling": "SUITE LABELING" + }, + "modify_amount": { + "address": "Address:", + "decrease_amount": "Decrease amount by:", + "increase_amount": "Increase amount by:", + "new_amount": "New amount:", + "title": "MODIFY AMOUNT" + }, + "modify_fee": { + "decrease_fee": "Decrease fee by:", + "fee_rate": "Fee rate:", + "increase_fee": "Increase fee by:", + "new_transaction_fee": "New transaction fee:", + "no_change": "Your fee did not change.", + "title": "MODIFY FEE", + "transaction_fee": "Transaction fee:" + }, + "monero": { + "confirm_export": "Confirm export", + "confirm_fee": "Confirm fee", + "confirm_ki_sync": "Confirm ki sync", + "confirm_refresh": "Confirm refresh", + "confirm_unlock_time": "Confirm unlock time", + "hashing_inputs": "Hashing inputs", + "payment_id": "Payment ID", + "postprocessing": "Postprocessing...", + "processing": "Processing...", + "processing_inputs": "Processing inputs", + "processing_outputs": "Processing outputs", + "signing": "Signing...", + "signing_inputs": "Signing inputs", + "unlock_time_set_template": "Unlock time for this transaction is set to {}", + "wanna_export_tx_der": "Do you really want to export tx_der\nfor tx_proof?", + "wanna_export_tx_key": "Do you really want to export tx_key?", + "wanna_export_watchkey": "Do you really want to export watch-only credentials?", + "wanna_start_refresh": "Do you really want to\nstart refresh?", + "wanna_sync_key_images": "Do you really want to\nsync key images?" + }, + "nem": { + "absolute": "absolute", + "activate": "Activate", + "add": "Add", + "confirm_action": "Confirm action", + "confirm_address": "Confirm address", + "confirm_creation_fee": "Confirm creation fee", + "confirm_fee": "Confirm fee", + "confirm_mosaic": "Confirm mosaic", + "confirm_multisig_fee": "Confirm multisig fee", + "confirm_namespace": "Confirm namespace", + "confirm_payload": "Confirm payload", + "confirm_properties": "Confirm properties", + "confirm_rental_fee": "Confirm rental fee", + "confirm_transfer_of": "Confirm transfer of", + "convert_account_to_multisig": "Convert account to multisig account?", + "cosign_transaction_for": "Cosign transaction for", + "cosignatory": " cosignatory", + "create_mosaic": "Create mosaic", + "create_namespace": "Create namespace", + "deactivate": "Deactivate", + "decrease": "Decrease", + "description": "Description:", + "divisibility_and_levy_cannot_be_shown": "Divisibility and levy cannot be shown for unknown mosaics", + "encrypted": "Encrypted:", + "final_confirm": "Final confirm", + "immutable": "immutable", + "increase": "Increase", + "initial_supply": "Initial supply:", + "initiate_transaction_for": "Initiate transaction for", + "levy_divisibility": "Levy divisibility:", + "levy_fee": "Levy fee:", + "levy_fee_of": "Confirm mosaic levy fee of", + "levy_mosaic": "Levy mosaic:", + "levy_namespace": "Levy namespace:", + "levy_recipient": "Levy recipient:", + "levy_type": "Levy type:", + "modify_supply_for": "Modify supply for", + "modify_the_number_of_cosignatories_by": "Modify the number of cosignatories by ", + "mutable": "mutable", + "no": "No", + "of": "of", + "percentile": "percentile", + "raw_units_template": "{} raw units", + "remote_harvesting": " remote harvesting?", + "remove": "Remove", + "set_minimum_cosignatories_to": "Set minimum cosignatories to ", + "sign_tx_fee_template": "Sign this transaction\nand pay {}\nfor network fee?", + "supply_change": "Supply change", + "supply_units_template": "{} supply by {} whole units?", + "transferable": "Transferable?", + "under_namespace": "under namespace", + "unencrypted": "Unencrypted:", + "unknown_mosaic": "Unknown mosaic!", + "yes": "Yes" + }, + "passphrase": { + "access_hidden_wallet": "Access hidden wallet?", + "always_on_device": "Do you really want to enter passphrase always on the device?", + "from_host_not_shown": "Passphrase provided by host will be used but will not be displayed due to the device settings.", + "hidden_wallet": "Hidden wallet", + "hide": "Hide passphrase coming from host?", + "next_screen_will_show_passphrase": "Next screen will show the passphrase.", + "please_enter": "Please enter your passphrase.", + "revoke_on_device": "Do you want to revoke the passphrase on device setting?", + "title_confirm": "CONFIRM PASSPHRASE", + "title_enter": "ENTER PASSPHRASE", + "title_hide": "HIDE PASSPHRASE", + "title_settings": "PASSPHRASE SETTINGS", + "title_source": "PASSPHRASE SOURCE", + "turn_off": "Turn off passphrase protection?", + "turn_on": "Turn on passphrase protection?" + }, + "pin": { + "change": "Change PIN?", + "changed": "PIN changed.", + "cursor_will_change": "Position of the cursor will change between entries for enhanced security.", + "diff_from_wipe_code": "The new PIN must be different from your wipe code.", + "disabled": "PIN protection\nturned off.", + "enabled": "PIN protection\nturned on.", + "enter": "Enter PIN", + "enter_new": "Enter new PIN", + "entered_not_valid": "The PIN you have entered is not valid.", + "info": "PIN will be required to access this device.", + "invalid_pin": "Invalid PIN", + "last_attempt": "Last attempt", + "mismatch": "Entered PINs do not match!", + "pin_mismatch": "PIN mismatch", + "please_check_again": "Please check again.", + "reenter_new": "Re-enter new PIN", + "reenter_to_confirm": "Please re-enter PIN to confirm.", + "should_be_long": "PIN should be 4-50 digits long.", + "title_check_pin": "CHECK PIN", + "title_settings": "PIN SETTINGS", + "title_wrong_pin": "WRONG PIN", + "tries_left": "tries left", + "turn_off": "Are you sure you want to turn off PIN protection?", + "turn_on": "Turn on PIN protection?", + "wrong_pin": "Wrong PIN" + }, + "plurals": { + "contains_x_keys": "key|keys", + "lock_after_x_hours": "hour|hours", + "lock_after_x_milliseconds": "millisecond|milliseconds", + "lock_after_x_minutes": "minute|minutes", + "lock_after_x_seconds": "second|seconds", + "sign_x_actions": "action|actions", + "transaction_of_x_operations": "operation|operations", + "x_groups_needed": "group|groups", + "x_shares_needed": "share|shares" + }, + "progress": { + "authenticity_check": "Checking authenticity...", + "done": "Done", + "loading_transaction": "Loading transaction...", + "one_second_left": "1 second left", + "please_wait": "PLEASE WAIT", + "processing": "PROCESSING", + "refreshing": "Refreshing...", + "signing_transaction": "Signing transaction...", + "syncing": "Syncing...", + "x_seconds_left_template": "{} seconds left" + }, + "reboot_to_bootloader": { + "restart": "Trezor will restart in bootloader mode.", + "title": "GO TO BOOTLOADER", + "version_by_template": "Firmware version {}\nby {}" + }, + "recovery": { + "cancel_dry_run": "Cancel backup check", + "check_dry_run": "Check your backup?", + "cursor_will_change": "Position of the cursor will change between entries for enhanced security.", + "dry_run_bip39_valid_match": "The entered recovery seed is valid and matches the one in the device.", + "dry_run_bip39_valid_mismatch": "The entered recovery seed is valid but does not match the one in the device.", + "dry_run_slip39_valid_match": "The entered recovery shares are valid and match what is currently in the device.", + "dry_run_slip39_valid_mismatch": "The entered recovery shares are valid but do not match what is currently in the device.", + "enter_any_share": "Enter any share", + "enter_backup": "Enter your backup.", + "enter_different_share": "Please enter a different share.", + "enter_share_from_diff_group": "Enter share from a different group.", + "group_num_template": "Group {}", + "group_threshold_reached": "Group threshold reached.", + "invalid_seed_entered": "Invalid recovery seed entered.", + "invalid_share_entered": "Invalid recovery share entered.", + "more_shares_needed": "More shares needed", + "num_of_words": "Select the number of words in your backup.", + "only_first_n_letters": "You'll only have to select the first 2-4 letters of each word.", + "progress_will_be_lost": "All progress will be lost.", + "select_num_of_words": "Select the number of words in your backup.", + "share_already_entered": "Share already entered", + "share_from_another_shamir": "You have entered a share from another Shamir Backup.", + "share_num_template": "Share {}", + "title": "RECOVER WALLET", + "title_cancel_dry_run": "CANCEL BACKUP CHECK", + "title_cancel_recovery": "CANCEL RECOVERY", + "title_dry_run": "BACKUP CHECK", + "title_recover": "RECOVER WALLET", + "title_remaining_shares": "REMAINING SHARES", + "type_word_x_of_y_template": "Type word {} of {}", + "wallet_recovered": "Wallet recovered successfully", + "wanna_cancel_dry_run": "Are you sure you want to cancel the backup check?", + "wanna_cancel_recovery": "Are you sure you want to cancel the recovery process?", + "word_count_template": "({} words)", + "word_x_of_y_template": "WORD {} OF {}", + "x_more_items_starting_template_plural": "{count} more {plural} starting", + "x_more_shares_needed_template_plural": "{count} more {plural} needed.", + "x_of_y_entered_template": "{} of {} shares entered successfully.", + "you_have_entered": "You have entered" + }, + "reset": { + "advanced_group_threshold_info": "The group threshold specifies the number of groups required to recover your wallet.", + "all_x_of_y_template": "all {} of {} shares", + "any_x_of_y_template": "any {} of {} shares", + "button_create": "CREATE WALLET", + "button_recover": "RECOVER WALLET", + "by_continuing": "By continuing you agree to Trezor Company's terms and conditions.", + "check_backup_title": "CHECK BACKUP", + "check_group_share_title_template": "CHECK G{} - SHARE {}", + "check_seed_title": "CHECK SEED", + "check_share_title_template": "CHECK SHARE #{}", + "continue_with_next_share": "Continue with the next share.", + "continue_with_share_template": "Continue with share #{}.", + "finished_verifying_group_template": "You have finished verifying your recovery shares for group {}.", + "finished_verifying_seed": "You have finished verifying your recovery seed.", + "finished_verifying_shares": "You have finished verifying your recovery shares.", + "group_description": "A group is made up of recovery shares.", + "group_info": "Each group has a set number of shares and its own threshold. In the next steps you will set the numbers of shares and the thresholds.", + "group_share_checked_successfully_template": "Group {} - Share {} checked successfully.", + "group_share_title_template": "GROUP {} - SHARE {}", + "more_info_at": "More info at", + "need_all_share_template": "For recovery you need all {} of the shares.", + "need_any_share_template": "For recovery you need any {} of the shares.", + "needed_to_form_a_group": "needed to form a group. ", + "needed_to_recover_your_wallet": "needed to recover your wallet. ", + "never_make_digital_copy": "Never make a digital copy of your backup or upload it online!", + "num_of_share_holders_template": "{} people or locations will each hold one share.", + "num_of_shares_advanced_info_template": "Each recovery share is a sequence of 20 words. Next you will choose the threshold number of shares needed to form Group {}.", + "num_of_shares_basic_info": "Each recovery share is a sequence of 20 words. Next you will choose how many shares you need to recover your wallet.", + "num_shares_for_group_template": "The required number of shares to form Group {}.", + "number_of_shares_info": "= total number of unique word lists used for wallet backup.", + "one_share": "1 share", + "only_one_share_will_be_created": "Only one share will be created.", + "recovery_seed_title": "RECOVERY SEED", + "recovery_share_title_template": "RECOVERY SHARE #{}", + "required_number_of_groups": "The required number of groups for recovery.", + "select_correct_word": "Select the correct word for each position.", + "select_word_template": "SELECT {} WORD", + "select_word_x_of_y_template": "Select word {} of {}:", + "set_it_to_count_template": "Set it to {} and you will need ", + "share_checked_successfully_template": "Recovery share #{} checked successfully.", + "share_words_title": "STANDARD BACKUP", + "slip39_checklist_num_groups": "Number of groups", + "slip39_checklist_num_shares": "Number of shares", + "slip39_checklist_set_num_groups": "Set number of groups", + "slip39_checklist_set_num_shares": "Set number of shares", + "slip39_checklist_set_sizes": "Set sizes and thresholds", + "slip39_checklist_set_sizes_longer": "Set size and threshold for each group", + "slip39_checklist_set_threshold": "Set threshold", + "slip39_checklist_title": "BACKUP CHECKLIST", + "slip39_checklist_write_down": "Write down and check all shares", + "slip39_checklist_write_down_recovery": "Write down and check all recovery shares", + "the_threshold_sets_the_number_of_shares": "The threshold sets the number of shares ", + "threshold_info": "= minimum number of unique word lists used for recovery.", + "title_backup_is_done": "BACKUP IS DONE", + "title_create_wallet": "CREATE WALLET", + "title_create_wallet_shamir": "CREATE WALLET (SHAMIR)", + "title_group_threshold": "GROUP THRESHOLD", + "title_number_of_groups": "NUMBER OF GROUPS", + "title_number_of_shares": "NUMBER OF SHARES", + "title_set_group_threshold": "SET GROUP THRESHOLD", + "title_set_number_of_groups": "SET NUMBER OF GROUPS", + "title_set_number_of_shares": "SET NUMBER OF SHARES", + "title_set_threshold": "SET THRESHOLD", + "to_form_group_template": "to form Group {}.", + "tos_link": "trezor.io/tos", + "total_number_of_shares_in_group_template": "Set the total number of shares in Group {}.", + "use_your_backup": "Use your backup when you need to recover your wallet.", + "write_down_words_template": "Write down all {} words in order.", + "wrong_word_selected": "Wrong word selected!", + "you_need_one_share": "For recovery you need 1 share.", + "your_backup_is_done": "Your backup is done." + }, + "ripple": { + "confirm_tag": "Confirm tag", + "destination_tag_template": "Destination tag:\n{}" + }, + "rotation": { + "change_template": "Do you want to change device rotation to {}?", + "east": "east", + "north": "north", + "south": "south", + "title_change": "CHANGE ROTATION", + "west": "west" + }, + "safety_checks": { + "approve_unsafe_always": "Trezor will allow you to approve some actions which might be unsafe.", + "approve_unsafe_temporary": "Trezor will temporarily allow you to approve some actions which might be unsafe.", + "enforce_strict": "Do you really want to enforce strict safety checks (recommended)?", + "title": "SAFETY CHECKS", + "title_safety_override": "SAFETY OVERRIDE" + }, + "sd_card": { + "all_data_will_be_lost": "All data on the SD card will be lost.", + "card_required": "SD card required.", + "disable": "Do you really want to remove SD card protection from your device?", + "disabled": "You have successfully disabled SD protection.", + "enable": "Do you really want to secure your device with SD card protection?", + "enabled": "You have successfully enabled SD protection.", + "error": "SD card error", + "format_card": "Format SD card", + "insert_correct_card": "Please insert the correct SD card for this device.", + "please_insert": "Please insert your SD card.", + "please_unplug_and_insert": "Please unplug the device and insert your SD card.", + "problem_accessing": "There was a problem accessing the SD card.", + "refresh": "Do you really want to replace the current SD card secret with a newly generated one?", + "refreshed": "You have successfully refreshed SD protection.", + "restart": "Do you want to restart Trezor in bootloader mode?", + "title": "SD CARD PROTECTION", + "title_problem": "SD CARD PROBLEM", + "unknown_filesystem": "Unknown filesystem.", + "unplug_and_insert_correct": "Please unplug the device and insert the correct SD card.", + "use_different_card": "Use a different card or format the SD card to the FAT32 filesystem.", + "wanna_format": "Do you really want to format the SD card?", + "wrong_sd_card": "Wrong SD card." + }, + "send": { + "address_path": "address path", + "amount": "Amount:", + "confirm_sending": "SENDING AMOUNT", + "from_multiple_accounts": "Sending from multiple accounts.", + "including_fee": "Including fee:", + "maximum_fee": "Maximum fee:", + "receiving_to_multisig": "Receiving to a multisig address.", + "title_amount": "AMOUNT", + "title_confirm_sending": "CONFIRM SENDING", + "title_joint_transaction": "JOINT TRANSACTION", + "title_receiving_to": "RECEIVING TO", + "title_recipient": "RECIPIENT", + "title_sending": "SENDING", + "title_sending_amount": "SENDING AMOUNT", + "title_sending_to": "SENDING TO", + "to_the_total_amount": "To the total amount:", + "total_amount": "Total amount:", + "transaction_id": "Transaction ID:", + "you_are_contributing": "You are contributing:" + }, + "share_words": { + "words_in_order": " words in order.", + "wrote_down_all": "I wrote down all " + }, + "sign_message": { + "bytes_template": "{} Bytes", + "confirm_address": "SIGNING ADDRESS", + "confirm_message": "CONFIRM MESSAGE", + "message_size": "Message size:", + "verify_address": "VERIFY ADDRESS" + }, + "stellar": { + "account": "Account", + "account_merge": "Account Merge", + "account_thresholds": "Account Thresholds", + "add_signer": "Add Signer", + "add_trust": "Add trust", + "all_will_be_sent_to": "All XLM will be sent to:", + "allow_trust": "Allow trust", + "asset": "Asset", + "bump_sequence": "Bump Sequence", + "buying": "Buying:", + "clear_data": "Clear data", + "clear_flags": "Clear flags", + "confirm_issuer": "Confirm Issuer", + "confirm_memo": "Confirm memo", + "confirm_network": "Confirm network", + "confirm_operation": "Confirm operation", + "confirm_stellar": "Confirm Stellar", + "confirm_timebounds": "Confirm timebounds", + "create_account": "Create Account", + "debited_amount": "Debited amount", + "delete": "Delete", + "delete_passive_offer": "Delete Passive Offer", + "delete_trust": "Delete trust", + "destination": "Destination:", + "exchanges_require_memo": "Important: Many exchanges require a memo when depositing", + "final_confirm": "Final confirm", + "hash": "Hash:", + "high": "High:", + "home_domain": "Home Domain", + "inflation": "Inflation", + "initial_balance": "Initial Balance", + "initialize_signing_with": "Initialize signing with", + "issuer_template": "{} issuer:", + "key": "Key:", + "limit": "Limit:", + "low": "Low:", + "master_weight": "Master Weight:", + "medium": "Medium:", + "new_offer": "New Offer", + "new_passive_offer": "New Passive Offer", + "no_memo_set": "No memo set!", + "no_restriction": "[no restriction]", + "on_network_template": "Transaction is on {}", + "path_pay": "Path Pay", + "path_pay_at_least": "Path Pay at least", + "pay": "Pay:", + "pay_at_most": "Pay at most:", + "preauth_transaction": "Pre-auth transaction:", + "price_per_template": "Price per {}:", + "private_network": "private network", + "remove_signer": "Remove Signer", + "revoke_trust": "Revoke trust", + "selling": "Selling:", + "set_data": "Set data", + "set_flags": "Set flags", + "set_sequence_to_template": "Set sequence to {}?", + "sign_tx_count_template": "Sign this transaction made up of {}", + "sign_tx_fee_template": " and pay {}\nfor fee?", + "source_account": "Source account:", + "testnet_network": "testnet network", + "trusted_account": "Trusted Account", + "update": "Update", + "valid_from": "Valid from (UTC)", + "valid_to": "Valid to (UTC)", + "value_sha256": "Value (SHA-256):", + "wanna_clean_value_key_template": "Do you want to clear value key {}?", + "your_account": " your account" + }, + "tezos": { + "address": "Address:", + "amount": "Amount:", + "baker_address": "Baker address:", + "balance": "Balance:", + "ballot": "Ballot:", + "confirm_delegation": "Confirm delegation", + "confirm_origination": "Confirm origination", + "delegator": "Delegator:", + "fee": "Fee:", + "proposal": "Proposal", + "register_delegate": "Register delegate", + "remove_delegation": "Remove delegation", + "submit_ballot": "Submit ballot", + "submit_proposal": "Submit proposal", + "submit_proposals": "Submit proposals" + }, + "tutorial": { + "middle_click": "Press both left and right at the same\ntime to confirm.", + "press_and_hold": "Press and hold the right button to\napprove important operations.", + "ready_to_use": "You're ready to\nuse Trezor.", + "scroll_down": "Press right to scroll down to read all content when text doesn't fit on one screen.\n\rPress left to scroll up.", + "sure_you_want_skip": "Are you sure you\nwant to skip the tutorial?", + "title_hello": "HELLO", + "title_screen_scroll": "SCREEN SCROLL", + "title_skip": "SKIP TUTORIAL", + "title_tutorial_complete": "TUTORIAL COMPLETE", + "use_trezor": "Use Trezor by\nclicking the left and right buttons.\n\rContinue right.", + "welcome_press_right": "Welcome to Trezor. Press right to continue." + }, + "u2f": { + "get": "Increase and retrieve the U2F counter?", + "set_template": "Set the U2F counter to {}?", + "title_get": "GET U2F COUNTER", + "title_set": "SET U2F COUNTER" + }, + "wipe": { + "info": "All data will be erased.", + "title": "WIPE DEVICE", + "want_to_wipe": "Do you really want to wipe the device?\n" + }, + "wipe_code": { + "change": "Change wipe code?", + "changed": "Wipe code changed.", + "diff_from_pin": "The wipe code must be different from your PIN.", + "disabled": "Wipe code disabled.", + "enabled": "Wipe code enabled.", + "enter_new": "Enter new wipe code", + "info": "Wipe code can be used to erase all data from this device.", + "invalid": "Invalid wipe code", + "mismatch": "Entered wipe codes do not match!", + "reenter": "Re-enter wipe code", + "reenter_to_confirm": "Please re-enter wipe code to confirm.", + "title_check": "CHECK WIPE CODE", + "title_invalid": "INVALID WIPE CODE", + "title_settings": "WIPE CODE SETTINGS", + "turn_off": "Turn off wipe code protection?", + "turn_on": "Turn on wipe code protection?", + "wipe_code_mismatch": "Wipe code mismatch" + }, + "word_count": { + "title": "NUMBER OF WORDS" + }, + "words": { + "are_you_sure": "Are you sure?", + "array_of": "Array of", + "buying": "Buying", + "confirm": "Confirm", + "contains": "Contains", + "continue_anyway": "Continue anyway?", + "continue_with": "Continue with", + "error": "Error", + "from": "from", + "keep_it_safe": "Keep it safe!", + "know_what_your_doing": "Continue only if you know what you are doing!", + "my_trezor": "My Trezor", + "outputs": "outputs", + "please_check_again": "Please check again", + "please_try_again": "Please try again", + "really_wanna": "Do you really want to", + "sign": "Sign", + "title_check": "CHECK", + "title_group": "GROUP", + "title_information": "INFORMATION", + "title_remember": "REMEMBER", + "title_share": "SHARE", + "title_shares": "SHARES", + "title_success": "SUCCESS", + "title_summary": "SUMMARY", + "title_threshold": "THRESHOLD", + "unknown": "Unknown", + "warning": "Warning" + } + } +} diff --git a/core/embed/rust/src/ui/translations/en.rs b/core/embed/rust/src/ui/translations/en.rs new file mode 100644 index 000000000..c4b227969 --- /dev/null +++ b/core/embed/rust/src/ui/translations/en.rs @@ -0,0 +1,838 @@ +//! generated from en.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +use super::general::TranslationsGeneral; + +#[rustfmt::skip] +pub const EN_TRANSLATIONS: TranslationsGeneral = TranslationsGeneral { + addr_mismatch__contact_support: "Please contact Trezor support at", + addr_mismatch__key_mismatch: "Key mismatch?", + addr_mismatch__mismatch: "Address mismatch?", + addr_mismatch__support_url: "trezor.io/support", + addr_mismatch__title: "ADDRESS MISMATCH?", + addr_mismatch__title_key_mismatch: "KEY MISMATCH?", + addr_mismatch__wrong_derication_path: "Wrong derivation path for selected account.", + addr_mismatch__xpub_mismatch: "XPUB mismatch?", + address__address: "Address:", + address__public_key: "Public key", + address__title_cosigner: "COSIGNER", + address__title_receive_address: "RECEIVE ADDRESS", + address__title_yours: "YOURS", + address_details__account: "Account:", + address_details__derivation_path: "Derivation path:", + address_details__title_receive_address: "RECEIVE ADDRESS", + address_details__title_receiving_to: "RECEIVING TO", + authenticate__confirm_template: "Allow connected computer to confirm your {} is genuine?", + authenticate__header: "Authenticate device", + auto_lock__change_template: "Auto-lock your Trezor after {} of inactivity?", + auto_lock__title: "AUTO-LOCK DELAY", + backup__can_back_up_anytime: "You can back up your Trezor once, at any time.", + backup__it_should_be_backed_up: "You should back up your new wallet right now.", + backup__it_should_be_backed_up_now: "It should be backed up now!", + backup__new_wallet_created: "New wallet created.", + backup__new_wallet_successfully_created: "New wallet created successfully.", + backup__recover_anytime: "You can use your backup to recover your wallet at any time.", + backup__title_backup_wallet: "BACK UP WALLET", + backup__title_skip: "SKIP BACKUP", + backup__want_to_skip: "Are you sure you want to skip the backup?", + binance__buy: "Buy", + binance__confirm_cancel: "Confirm cancel", + binance__confirm_input: "Confirm input", + binance__confirm_order: "Confirm order", + binance__confirm_output: "Confirm output", + binance__order_id: "Order ID:", + binance__pair: "Pair:", + binance__price: "Price:", + binance__quantity: "Quantity:", + binance__sell: "Sell", + binance__sender_address: "Sender address:", + binance__side: "Side:", + binance__unknown: "Unknown", + bitcoin__commitment_data: "Commitment data:", + bitcoin__confirm_locktime: "Confirm locktime", + bitcoin__create_proof_of_ownership: "Do you want to create a proof of ownership?", + bitcoin__high_mining_fee_template: "The mining fee of\n{}\nis unexpectedly high.", + bitcoin__locktime_no_effect: "Locktime is set but will have no effect.", + bitcoin__locktime_set_to: "Locktime set to:", + bitcoin__locktime_set_to_blockheight: "Locktime set to blockheight:", + bitcoin__lot_of_change_outputs: "A lot of change-outputs.", + bitcoin__multiple_accounts: "Multiple accounts", + bitcoin__new_fee_rate: "New fee rate:", + bitcoin__simple_send_of: "Simple send of", + bitcoin__ticket_amount: "Ticket amount:", + bitcoin__title_confirm_details: "CONFIRM DETAILS", + bitcoin__title_finalize_transaction: "FINALIZE TRANSACTION", + bitcoin__title_high_mining_fee: "HIGH MINING FEE", + bitcoin__title_meld_transaction: "MELD TRANSACTIONS", + bitcoin__title_modify_amount: "MODIFY AMOUNT", + bitcoin__title_payjoin: "PAYJOIN", + bitcoin__title_proof_of_ownership: "PROOF OF OWNERSHIP", + bitcoin__title_purchase_ticket: "PURCHASE TICKET", + bitcoin__title_update_transaction: "UPDATE TRANSACTION", + bitcoin__unknown_path: "Unknown path", + bitcoin__unknown_transaction: "Unknown transaction", + bitcoin__unusually_high_fee: "Unusually high fee.", + bitcoin__unverified_external_inputs: "The transaction contains unverified external inputs.", + bitcoin__valid_signature: "The signature is valid.", + bitcoin__voting_rights: "Voting rights to:", + buttons__abort: "ABORT", + buttons__access: "ACCESS", + buttons__again: "AGAIN", + buttons__allow: "ALLOW", + buttons__back_up: "BACK UP", + buttons__cancel: "CANCEL", + buttons__change: "CHANGE", + buttons__check: "CHECK", + buttons__check_again: "CHECK AGAIN", + buttons__close: "CLOSE", + buttons__confirm: "CONFIRM", + buttons__continue: "CONTINUE", + buttons__details: "DETAILS", + buttons__enable: "ENABLE", + buttons__enter: "ENTER", + buttons__enter_share: "ENTER SHARE", + buttons__export: "EXPORT", + buttons__format: "FORMAT", + buttons__go_back: "GO BACK", + buttons__hold_to_confirm: "HOLD TO CONFIRM", + buttons__info: "INFO", + buttons__install: "INSTALL", + buttons__more_info: "MORE INFO", + buttons__ok_i_understand: "OK, I UNDERSTAND", + buttons__purchase: "PURCHASE", + buttons__quit: "QUIT", + buttons__restart: "RESTART", + buttons__retry: "RETRY", + buttons__select: "SELECT", + buttons__set: "SET", + buttons__show_all: "SHOW ALL", + buttons__show_words: "SHOW WORDS", + buttons__skip: "SKIP", + buttons__try_again: "TRY AGAIN", + buttons__turn_off: "TURN OFF", + buttons__turn_on: "TURN ON", + cardano__addr_base: "Base", + cardano__addr_enterprise: "Enterprise", + cardano__addr_legacy: "Legacy", + cardano__addr_pointer: "Pointer", + cardano__addr_reward: "Reward", + cardano__address_no_staking: "address - no staking rewards.", + cardano__amount: "Amount:", + cardano__amount_burned_decimals_unknown: "Amount burned (decimals unknown):", + cardano__amount_minted_decimals_unknown: "Amount minted (decimals unknown):", + cardano__amount_sent_decimals_unknown: "Amount sent (decimals unknown):", + cardano__anonymous_pool: "Pool has no metadata (anonymous pool)", + cardano__asset_fingerprint: "Asset fingerprint:", + cardano__auxiliary_data_hash: "Auxiliary data hash:", + cardano__block: "Block", + cardano__catalyst: "Catalyst", + cardano__certificate: "Certificate", + cardano__certificate_path: "Certificate path", + cardano__change_output: "Change output", + cardano__change_output_path: "Change output path", + cardano__change_output_staking_path: "Change output staking path", + cardano__check_all_items: "Check all items carefully.", + cardano__choose_level_of_details: "Choose level of details:", + cardano__collateral_input_id: "Collateral input ID:", + cardano__collateral_input_index: "Collateral input index:", + cardano__collateral_output_contains_tokens: "The collateral return output contains tokens.", + cardano__collateral_return: "Collateral return", + cardano__confirm: "Confirm:", + cardano__confirm_signing_stake_pool: "Confirm signing the stake pool registration as an owner.", + cardano__confirm_transaction: "Confirm transaction", + cardano__confirming_a_multisig_transaction: "Confirming a multisig transaction.", + cardano__confirming_pool_registration: "Confirming pool registration as owner.", + cardano__confirming_transction: "Confirming a transaction.", + cardano__cost: "Cost", + cardano__credential_mismatch: "Credential doesn't match payment credential.", + cardano__datum_hash: "Datum hash:", + cardano__delegating_to: "Delegating to:", + cardano__for_account_and_index_template: "for account {} and index {}:", + cardano__for_account_template: "for account {}:", + cardano__for_key_hash: "for key hash:", + cardano__for_script: "for script:", + cardano__inline_datum: "Inline datum", + cardano__input_id: "Input ID:", + cardano__input_index: "Input index:", + cardano__intro_text_address: "Address", + cardano__intro_text_change: "The following address is a change address. Its", + cardano__intro_text_owned_by_device: "The following address is owned by this device. Its", + cardano__intro_text_registration_payment: "The vote key registration payment address is owned by this device. Its", + cardano__key_hash: "key hash", + cardano__margin: "Margin", + cardano__multisig_path: "multi-sig path", + cardano__nested_scripts_template: "Contains {} nested scripts.", + cardano__network: "Network:", + cardano__no_output_tx: "Transaction has no outputs, network cannot be verified.", + cardano__nonce: "Nonce:", + cardano__other: "other", + cardano__path: "path", + cardano__pledge: "Pledge", + cardano__pointer: "pointer", + cardano__policy_id: "Policy ID:", + cardano__pool_metadata_hash: "Pool metadata hash:", + cardano__pool_metadata_url: "Pool metadata url:", + cardano__pool_owner: "Pool owner:", + cardano__pool_owner_path: "Pool owner staking path", + cardano__pool_reward_account: "Pool reward account:", + cardano__reference_input_id: "Reference input ID:", + cardano__reference_input_index: "Reference input index:", + cardano__reference_script: "Reference script", + cardano__required_signer: "Required signer", + cardano__reward: "reward", + cardano__reward_address: "Address is a reward address.", + cardano__reward_eligibility_warning: "Warning: The address is not a payment address, it is not eligible for rewards.", + cardano__rewards_go_to: "Rewards go to:", + cardano__script: "script", + cardano__script_all: "All", + cardano__script_any: "Any", + cardano__script_data_hash: "Script data hash:", + cardano__script_hash: "Script hash:", + cardano__script_invalid_before: "Invalid before", + cardano__script_invalid_hereafter: "Invalid hereafter", + cardano__script_key: "Key", + cardano__script_n_of_k: "N of K", + cardano__script_reward: "script reward", + cardano__sending: "Sending", + cardano__show_simple: "Show Simple", + cardano__sign_tx_path_template: "Sign transaction with {}:", + cardano__stake_delegation: "Stake delegation", + cardano__stake_deregistration: "Stake key deregistration", + cardano__stake_pool_registration: "Stakepool registration", + cardano__stake_pool_registration_pool_id: "Stake pool registration\nPool ID:", + cardano__stake_registration: "Stake key registration", + cardano__staking_key_for_account: "Staking key for account", + cardano__to_pool: "to pool:", + cardano__token_minting_path: "token minting path", + cardano__total_collateral: "Total collateral:", + cardano__transaction: "Transaction", + cardano__transaction_contains_minting_or_burning: "The transaction contains minting or burning of tokens.", + cardano__transaction_contains_script_address_no_datum: "The following transaction output contains a script address, but does not contain a datum.", + cardano__transaction_fee: "Transaction fee:", + cardano__transaction_id: "Transaction ID:", + cardano__transaction_no_collateral_input: "The transaction contains no collateral inputs. Plutus script will not be able to run.", + cardano__transaction_no_script_data_hash: "The transaction contains no script data hash. Plutus script will not be able to run.", + cardano__transaction_output_contains_tokens: "The following transaction output contains tokens.", + cardano__ttl: "TTL:", + cardano__unknown: "Unknown", + cardano__unknown_collateral_amount: "Unknown collateral amount.", + cardano__unusual_path: "Path is unusual.", + cardano__valid_since: "Valid since:", + cardano__verify_script: "Verify script", + cardano__vote_key_registration: "Vote key registration (CIP-36)", + cardano__vote_public_key: "Vote public key:", + cardano__voting_purpose: "Voting purpose:", + cardano__warning: "Warning", + cardano__weight: "Weight:", + cardano__withdrawal_for_address_template: "Confirm withdrawal for {} address:", + cardano__witness_path: "Witness path", + cardano__x_of_y_signatures_template: "Requires {} out of {} signatures.", + coinjoin__access_account: "Access your coinjoin account?", + coinjoin__do_not_disconnect: "Do not disconnect your Trezor!", + coinjoin__max_mining_fee: "Max mining fee", + coinjoin__max_rounds: "Max rounds", + coinjoin__title: "AUTHORIZE COINJOIN", + coinjoin__title_do_not_disconnect: "DO NOT DISCONNECT YOUR TREZOR!", + coinjoin__title_progress: "COINJOIN IN PROGRESS", + coinjoin__waiting_for_others: "Waiting for others", + confirm_total__account: "Account:", + confirm_total__fee_rate: "Fee rate:", + confirm_total__sending_from_account: "Sending from account:", + confirm_total__title_fee: "FEE INFORMATION", + confirm_total__title_sending_from: "SENDING FROM", + debug__loading_seed: "Loading seed", + debug__loading_seed_not_recommended: "Loading private seed is not recommended.", + device_name__change_template: "Change device name to {}?", + device_name__title: "DEVICE NAME", + entropy__send: "Do you really want to send entropy?", + entropy__title: "INTERNAL ENTROPY", + entropy__title_confirm: "CONFIRM ENTROPY", + eos__about_to_sign_template: "You are about to sign {}.", + eos__account: "Account:", + eos__action_name: "Action Name:", + eos__amount: "Amount:", + eos__arbitrary_data: "Arbitrary data", + eos__buy_ram: "Buy RAM", + eos__bytes: "Bytes:", + eos__cancel_vote: "Cancel vote", + eos__checksum: "Checksum:", + eos__code: "Code:", + eos__contract: "Contract:", + eos__cpu: "CPU:", + eos__creator: "Creator:", + eos__delegate: "Delegate", + eos__delete_auth: "Delete Auth", + eos__from: "From:", + eos__link_auth: "Link Auth", + eos__memo: "Memo", + eos__name: "Name:", + eos__net: "NET:", + eos__new_account: "New account", + eos__no: "No", + eos__owner: "Owner:", + eos__parent: "Parent:", + eos__payer: "Payer:", + eos__permission: "Permission:", + eos__proxy: "Proxy:", + eos__receiver: "Receiver:", + eos__refund: "Refund", + eos__requirement: "Requirement:", + eos__sell_ram: "Sell RAM", + eos__sender: "Sender:", + eos__sign_transaction: "Sign transaction", + eos__threshold: "Threshold:", + eos__to: "To:", + eos__transfer: "Transfer:", + eos__type: "Type:", + eos__undelegate: "Undelegate", + eos__unlink_auth: "Unlink Auth", + eos__update_auth: "Update Auth", + eos__vote_for_producers: "Vote for producers", + eos__vote_for_proxy: "Vote for proxy", + eos__voter: "Voter:", + eos__yes: "Yes", + ethereum__amount_sent: "Amount sent:", + ethereum__confirm_fee: "Confirm fee", + ethereum__contract: "Contract:", + ethereum__data_size_template: "Size: {} bytes", + ethereum__gas_limit: "Gas limit:", + ethereum__gas_price: "Gas price:", + ethereum__max_gas_price: "Max gas price:", + ethereum__name_and_version: "Name and version", + ethereum__new_contract: "new contract?", + ethereum__no_message_field: "No message field", + ethereum__priority_fee: "Priority fee:", + ethereum__show_full_array: "Show full array", + ethereum__show_full_domain: "Show full domain", + ethereum__show_full_message: "Show full message", + ethereum__show_full_struct: "Show full struct", + ethereum__sign_eip712: "Really sign EIP-712 typed data?", + ethereum__title_confirm_data: "CONFIRM DATA", + ethereum__title_confirm_domain: "CONFIRM DOMAIN", + ethereum__title_confirm_message: "CONFIRM MESSAGE", + ethereum__title_confirm_struct: "CONFIRM STRUCT", + ethereum__title_confirm_typed_data: "CONFIRM TYPED DATA", + ethereum__title_signing_address: "SIGNING ADDRESS", + ethereum__units_template: "{} units", + ethereum__unknown_token: "Unknown token", + ethereum__valid_signature: "The signature is valid.", + experimental_mode__enable: "Enable experimental features?", + experimental_mode__only_for_dev: "Only for development and beta testing!", + experimental_mode__title: "EXPERIMENTAL MODE", + fido__already_registered: "Already registered", + fido__device_already_registered: "This device is already registered with this application.", + fido__device_already_registered_with_template: "This device is already registered with {}.", + fido__device_not_registered: "This device is not registered with this application.", + fido__does_not_belong: "The credential you are trying to import does\nnot belong to this authenticator.", + fido__erase_credentials: "erase all credentials?", + fido__export_credentials: "Export information about the credentials stored on this device?", + fido__not_registered: "Not registered", + fido__not_registered_with_template: "This device is not registered with\n{}.", + fido__please_enable_pin_protection: "Please enable PIN protection.", + fido__title_authenticate: "FIDO2 AUTHENTICATE", + fido__title_import_credential: "IMPORT CREDENTIAL", + fido__title_list_credentials: "LIST CREDENTIALS", + fido__title_register: "FIDO2 REGISTER", + fido__title_remove_credential: "REMOVE CREDENTIAL", + fido__title_reset: "FIDO2 RESET", + fido__title_u2f_auth: "U2F AUTHENTICATE", + fido__title_u2f_register: "U2F REGISTER", + fido__title_verify_user: "FIDO2 VERIFY USER", + fido__unable_to_verify_user: "Unable to verify user.", + fido__wanna_erase_credentials: "Do you really want to erase all credentials?", + firmware_update__title: "UPDATE FIRMWARE", + firmware_update__title_fingerprint: "FW FINGERPRINT", + homescreen__click_to_connect: "Click to Connect", + homescreen__click_to_unlock: "Click to Unlock", + homescreen__title_backup_failed: "BACKUP FAILED", + homescreen__title_backup_needed: "BACKUP NEEDED", + homescreen__title_coinjoin_authorized: "COINJOIN AUTHORIZED", + homescreen__title_experimental_mode: "EXPERIMENTAL MODE", + homescreen__title_hold_to_lock: "HOLD TO LOCK", + homescreen__title_no_usb_connection: "NO USB CONNECTION", + homescreen__title_pin_not_set: "PIN NOT SET", + homescreen__title_seedless: "SEEDLESS", + homescreen__title_set: "CHANGE HOMESCREEN?", + inputs__back: "BACK", + inputs__cancel: "CANCEL", + inputs__delete: "DELETE", + inputs__enter: "ENTER", + inputs__return: "RETURN", + inputs__show: "SHOW", + inputs__space: "SPACE", + joint__title: "JOINT TRANSACTION", + joint__to_the_total_amount: "To the total amount:", + joint__you_are_contributing: "You are contributing:", + lockscreen__tap_to_connect: "Tap to connect", + lockscreen__tap_to_unlock: "Tap to unlock", + lockscreen__title_locked: "LOCKED", + lockscreen__title_not_connected: "NOT CONNECTED", + misc__decrypt_value: "Decrypt value", + misc__encrypt_value: "Encrypt value", + misc__title_suite_labeling: "SUITE LABELING", + modify_amount__address: "Address:", + modify_amount__decrease_amount: "Decrease amount by:", + modify_amount__increase_amount: "Increase amount by:", + modify_amount__new_amount: "New amount:", + modify_amount__title: "MODIFY AMOUNT", + modify_fee__decrease_fee: "Decrease fee by:", + modify_fee__fee_rate: "Fee rate:", + modify_fee__increase_fee: "Increase fee by:", + modify_fee__new_transaction_fee: "New transaction fee:", + modify_fee__no_change: "Your fee did not change.", + modify_fee__title: "MODIFY FEE", + modify_fee__transaction_fee: "Transaction fee:", + monero__confirm_export: "Confirm export", + monero__confirm_fee: "Confirm fee", + monero__confirm_ki_sync: "Confirm ki sync", + monero__confirm_refresh: "Confirm refresh", + monero__confirm_unlock_time: "Confirm unlock time", + monero__hashing_inputs: "Hashing inputs", + monero__payment_id: "Payment ID", + monero__postprocessing: "Postprocessing...", + monero__processing: "Processing...", + monero__processing_inputs: "Processing inputs", + monero__processing_outputs: "Processing outputs", + monero__signing: "Signing...", + monero__signing_inputs: "Signing inputs", + monero__unlock_time_set_template: "Unlock time for this transaction is set to {}", + monero__wanna_export_tx_der: "Do you really want to export tx_der\nfor tx_proof?", + monero__wanna_export_tx_key: "Do you really want to export tx_key?", + monero__wanna_export_watchkey: "Do you really want to export watch-only credentials?", + monero__wanna_start_refresh: "Do you really want to\nstart refresh?", + monero__wanna_sync_key_images: "Do you really want to\nsync key images?", + nem__absolute: "absolute", + nem__activate: "Activate", + nem__add: "Add", + nem__confirm_action: "Confirm action", + nem__confirm_address: "Confirm address", + nem__confirm_creation_fee: "Confirm creation fee", + nem__confirm_fee: "Confirm fee", + nem__confirm_mosaic: "Confirm mosaic", + nem__confirm_multisig_fee: "Confirm multisig fee", + nem__confirm_namespace: "Confirm namespace", + nem__confirm_payload: "Confirm payload", + nem__confirm_properties: "Confirm properties", + nem__confirm_rental_fee: "Confirm rental fee", + nem__confirm_transfer_of: "Confirm transfer of", + nem__convert_account_to_multisig: "Convert account to multisig account?", + nem__cosign_transaction_for: "Cosign transaction for", + nem__cosignatory: " cosignatory", + nem__create_mosaic: "Create mosaic", + nem__create_namespace: "Create namespace", + nem__deactivate: "Deactivate", + nem__decrease: "Decrease", + nem__description: "Description:", + nem__divisibility_and_levy_cannot_be_shown: "Divisibility and levy cannot be shown for unknown mosaics", + nem__encrypted: "Encrypted:", + nem__final_confirm: "Final confirm", + nem__immutable: "immutable", + nem__increase: "Increase", + nem__initial_supply: "Initial supply:", + nem__initiate_transaction_for: "Initiate transaction for", + nem__levy_divisibility: "Levy divisibility:", + nem__levy_fee: "Levy fee:", + nem__levy_fee_of: "Confirm mosaic levy fee of", + nem__levy_mosaic: "Levy mosaic:", + nem__levy_namespace: "Levy namespace:", + nem__levy_recipient: "Levy recipient:", + nem__levy_type: "Levy type:", + nem__modify_supply_for: "Modify supply for", + nem__modify_the_number_of_cosignatories_by: "Modify the number of cosignatories by ", + nem__mutable: "mutable", + nem__no: "No", + nem__of: "of", + nem__percentile: "percentile", + nem__raw_units_template: "{} raw units", + nem__remote_harvesting: " remote harvesting?", + nem__remove: "Remove", + nem__set_minimum_cosignatories_to: "Set minimum cosignatories to ", + nem__sign_tx_fee_template: "Sign this transaction\nand pay {}\nfor network fee?", + nem__supply_change: "Supply change", + nem__supply_units_template: "{} supply by {} whole units?", + nem__transferable: "Transferable?", + nem__under_namespace: "under namespace", + nem__unencrypted: "Unencrypted:", + nem__unknown_mosaic: "Unknown mosaic!", + nem__yes: "Yes", + passphrase__access_hidden_wallet: "Access hidden wallet?", + passphrase__always_on_device: "Do you really want to enter passphrase always on the device?", + passphrase__from_host_not_shown: "Passphrase provided by host will be used but will not be displayed due to the device settings.", + passphrase__hidden_wallet: "Hidden wallet", + passphrase__hide: "Hide passphrase coming from host?", + passphrase__next_screen_will_show_passphrase: "Next screen will show the passphrase.", + passphrase__please_enter: "Please enter your passphrase.", + passphrase__revoke_on_device: "Do you want to revoke the passphrase on device setting?", + passphrase__title_confirm: "CONFIRM PASSPHRASE", + passphrase__title_enter: "ENTER PASSPHRASE", + passphrase__title_hide: "HIDE PASSPHRASE", + passphrase__title_settings: "PASSPHRASE SETTINGS", + passphrase__title_source: "PASSPHRASE SOURCE", + passphrase__turn_off: "Turn off passphrase protection?", + passphrase__turn_on: "Turn on passphrase protection?", + pin__change: "Change PIN?", + pin__changed: "PIN changed.", + pin__cursor_will_change: "Position of the cursor will change between entries for enhanced security.", + pin__diff_from_wipe_code: "The new PIN must be different from your wipe code.", + pin__disabled: "PIN protection\nturned off.", + pin__enabled: "PIN protection\nturned on.", + pin__enter: "Enter PIN", + pin__enter_new: "Enter new PIN", + pin__entered_not_valid: "The PIN you have entered is not valid.", + pin__info: "PIN will be required to access this device.", + pin__invalid_pin: "Invalid PIN", + pin__last_attempt: "Last attempt", + pin__mismatch: "Entered PINs do not match!", + pin__pin_mismatch: "PIN mismatch", + pin__please_check_again: "Please check again.", + pin__reenter_new: "Re-enter new PIN", + pin__reenter_to_confirm: "Please re-enter PIN to confirm.", + pin__should_be_long: "PIN should be 4-50 digits long.", + pin__title_check_pin: "CHECK PIN", + pin__title_settings: "PIN SETTINGS", + pin__title_wrong_pin: "WRONG PIN", + pin__tries_left: "tries left", + pin__turn_off: "Are you sure you want to turn off PIN protection?", + pin__turn_on: "Turn on PIN protection?", + pin__wrong_pin: "Wrong PIN", + plurals__contains_x_keys: "key|keys", + plurals__lock_after_x_hours: "hour|hours", + plurals__lock_after_x_milliseconds: "millisecond|milliseconds", + plurals__lock_after_x_minutes: "minute|minutes", + plurals__lock_after_x_seconds: "second|seconds", + plurals__sign_x_actions: "action|actions", + plurals__transaction_of_x_operations: "operation|operations", + plurals__x_groups_needed: "group|groups", + plurals__x_shares_needed: "share|shares", + progress__authenticity_check: "Checking authenticity...", + progress__done: "Done", + progress__loading_transaction: "Loading transaction...", + progress__one_second_left: "1 second left", + progress__please_wait: "PLEASE WAIT", + progress__processing: "PROCESSING", + progress__refreshing: "Refreshing...", + progress__signing_transaction: "Signing transaction...", + progress__syncing: "Syncing...", + progress__x_seconds_left_template: "{} seconds left", + reboot_to_bootloader__restart: "Trezor will restart in bootloader mode.", + reboot_to_bootloader__title: "GO TO BOOTLOADER", + reboot_to_bootloader__version_by_template: "Firmware version {}\nby {}", + recovery__cancel_dry_run: "Cancel backup check", + recovery__check_dry_run: "Check your backup?", + recovery__cursor_will_change: "Position of the cursor will change between entries for enhanced security.", + recovery__dry_run_bip39_valid_match: "The entered recovery seed is valid and matches the one in the device.", + recovery__dry_run_bip39_valid_mismatch: "The entered recovery seed is valid but does not match the one in the device.", + recovery__dry_run_slip39_valid_match: "The entered recovery shares are valid and match what is currently in the device.", + recovery__dry_run_slip39_valid_mismatch: "The entered recovery shares are valid but do not match what is currently in the device.", + recovery__enter_any_share: "Enter any share", + recovery__enter_backup: "Enter your backup.", + recovery__enter_different_share: "Please enter a different share.", + recovery__enter_share_from_diff_group: "Enter share from a different group.", + recovery__group_num_template: "Group {}", + recovery__group_threshold_reached: "Group threshold reached.", + recovery__invalid_seed_entered: "Invalid recovery seed entered.", + recovery__invalid_share_entered: "Invalid recovery share entered.", + recovery__more_shares_needed: "More shares needed", + recovery__num_of_words: "Select the number of words in your backup.", + recovery__only_first_n_letters: "You'll only have to select the first 2-4 letters of each word.", + recovery__progress_will_be_lost: "All progress will be lost.", + recovery__select_num_of_words: "Select the number of words in your backup.", + recovery__share_already_entered: "Share already entered", + recovery__share_from_another_shamir: "You have entered a share from another Shamir Backup.", + recovery__share_num_template: "Share {}", + recovery__title: "RECOVER WALLET", + recovery__title_cancel_dry_run: "CANCEL BACKUP CHECK", + recovery__title_cancel_recovery: "CANCEL RECOVERY", + recovery__title_dry_run: "BACKUP CHECK", + recovery__title_recover: "RECOVER WALLET", + recovery__title_remaining_shares: "REMAINING SHARES", + recovery__type_word_x_of_y_template: "Type word {} of {}", + recovery__wallet_recovered: "Wallet recovered successfully", + recovery__wanna_cancel_dry_run: "Are you sure you want to cancel the backup check?", + recovery__wanna_cancel_recovery: "Are you sure you want to cancel the recovery process?", + recovery__word_count_template: "({} words)", + recovery__word_x_of_y_template: "WORD {} OF {}", + recovery__x_more_items_starting_template_plural: "{count} more {plural} starting", + recovery__x_more_shares_needed_template_plural: "{count} more {plural} needed.", + recovery__x_of_y_entered_template: "{} of {} shares entered successfully.", + recovery__you_have_entered: "You have entered", + reset__advanced_group_threshold_info: "The group threshold specifies the number of groups required to recover your wallet.", + reset__all_x_of_y_template: "all {} of {} shares", + reset__any_x_of_y_template: "any {} of {} shares", + reset__button_create: "CREATE WALLET", + reset__button_recover: "RECOVER WALLET", + reset__by_continuing: "By continuing you agree to Trezor Company's terms and conditions.", + reset__check_backup_title: "CHECK BACKUP", + reset__check_group_share_title_template: "CHECK G{} - SHARE {}", + reset__check_seed_title: "CHECK SEED", + reset__check_share_title_template: "CHECK SHARE #{}", + reset__continue_with_next_share: "Continue with the next share.", + reset__continue_with_share_template: "Continue with share #{}.", + reset__finished_verifying_group_template: "You have finished verifying your recovery shares for group {}.", + reset__finished_verifying_seed: "You have finished verifying your recovery seed.", + reset__finished_verifying_shares: "You have finished verifying your recovery shares.", + reset__group_description: "A group is made up of recovery shares.", + reset__group_info: "Each group has a set number of shares and its own threshold. In the next steps you will set the numbers of shares and the thresholds.", + reset__group_share_checked_successfully_template: "Group {} - Share {} checked successfully.", + reset__group_share_title_template: "GROUP {} - SHARE {}", + reset__more_info_at: "More info at", + reset__need_all_share_template: "For recovery you need all {} of the shares.", + reset__need_any_share_template: "For recovery you need any {} of the shares.", + reset__needed_to_form_a_group: "needed to form a group. ", + reset__needed_to_recover_your_wallet: "needed to recover your wallet. ", + reset__never_make_digital_copy: "Never make a digital copy of your backup or upload it online!", + reset__num_of_share_holders_template: "{} people or locations will each hold one share.", + reset__num_of_shares_advanced_info_template: "Each recovery share is a sequence of 20 words. Next you will choose the threshold number of shares needed to form Group {}.", + reset__num_of_shares_basic_info: "Each recovery share is a sequence of 20 words. Next you will choose how many shares you need to recover your wallet.", + reset__num_shares_for_group_template: "The required number of shares to form Group {}.", + reset__number_of_shares_info: "= total number of unique word lists used for wallet backup.", + reset__one_share: "1 share", + reset__only_one_share_will_be_created: "Only one share will be created.", + reset__recovery_seed_title: "RECOVERY SEED", + reset__recovery_share_title_template: "RECOVERY SHARE #{}", + reset__required_number_of_groups: "The required number of groups for recovery.", + reset__select_correct_word: "Select the correct word for each position.", + reset__select_word_template: "SELECT {} WORD", + reset__select_word_x_of_y_template: "Select word {} of {}:", + reset__set_it_to_count_template: "Set it to {} and you will need ", + reset__share_checked_successfully_template: "Recovery share #{} checked successfully.", + reset__share_words_title: "STANDARD BACKUP", + reset__slip39_checklist_num_groups: "Number of groups", + reset__slip39_checklist_num_shares: "Number of shares", + reset__slip39_checklist_set_num_groups: "Set number of groups", + reset__slip39_checklist_set_num_shares: "Set number of shares", + reset__slip39_checklist_set_sizes: "Set sizes and thresholds", + reset__slip39_checklist_set_sizes_longer: "Set size and threshold for each group", + reset__slip39_checklist_set_threshold: "Set threshold", + reset__slip39_checklist_title: "BACKUP CHECKLIST", + reset__slip39_checklist_write_down: "Write down and check all shares", + reset__slip39_checklist_write_down_recovery: "Write down and check all recovery shares", + reset__the_threshold_sets_the_number_of_shares: "The threshold sets the number of shares ", + reset__threshold_info: "= minimum number of unique word lists used for recovery.", + reset__title_backup_is_done: "BACKUP IS DONE", + reset__title_create_wallet: "CREATE WALLET", + reset__title_create_wallet_shamir: "CREATE WALLET (SHAMIR)", + reset__title_group_threshold: "GROUP THRESHOLD", + reset__title_number_of_groups: "NUMBER OF GROUPS", + reset__title_number_of_shares: "NUMBER OF SHARES", + reset__title_set_group_threshold: "SET GROUP THRESHOLD", + reset__title_set_number_of_groups: "SET NUMBER OF GROUPS", + reset__title_set_number_of_shares: "SET NUMBER OF SHARES", + reset__title_set_threshold: "SET THRESHOLD", + reset__to_form_group_template: "to form Group {}.", + reset__tos_link: "trezor.io/tos", + reset__total_number_of_shares_in_group_template: "Set the total number of shares in Group {}.", + reset__use_your_backup: "Use your backup when you need to recover your wallet.", + reset__write_down_words_template: "Write down all {} words in order.", + reset__wrong_word_selected: "Wrong word selected!", + reset__you_need_one_share: "For recovery you need 1 share.", + reset__your_backup_is_done: "Your backup is done.", + ripple__confirm_tag: "Confirm tag", + ripple__destination_tag_template: "Destination tag:\n{}", + rotation__change_template: "Do you want to change device rotation to {}?", + rotation__east: "east", + rotation__north: "north", + rotation__south: "south", + rotation__title_change: "CHANGE ROTATION", + rotation__west: "west", + safety_checks__approve_unsafe_always: "Trezor will allow you to approve some actions which might be unsafe.", + safety_checks__approve_unsafe_temporary: "Trezor will temporarily allow you to approve some actions which might be unsafe.", + safety_checks__enforce_strict: "Do you really want to enforce strict safety checks (recommended)?", + safety_checks__title: "SAFETY CHECKS", + safety_checks__title_safety_override: "SAFETY OVERRIDE", + sd_card__all_data_will_be_lost: "All data on the SD card will be lost.", + sd_card__card_required: "SD card required.", + sd_card__disable: "Do you really want to remove SD card protection from your device?", + sd_card__disabled: "You have successfully disabled SD protection.", + sd_card__enable: "Do you really want to secure your device with SD card protection?", + sd_card__enabled: "You have successfully enabled SD protection.", + sd_card__error: "SD card error", + sd_card__format_card: "Format SD card", + sd_card__insert_correct_card: "Please insert the correct SD card for this device.", + sd_card__please_insert: "Please insert your SD card.", + sd_card__please_unplug_and_insert: "Please unplug the device and insert your SD card.", + sd_card__problem_accessing: "There was a problem accessing the SD card.", + sd_card__refresh: "Do you really want to replace the current SD card secret with a newly generated one?", + sd_card__refreshed: "You have successfully refreshed SD protection.", + sd_card__restart: "Do you want to restart Trezor in bootloader mode?", + sd_card__title: "SD CARD PROTECTION", + sd_card__title_problem: "SD CARD PROBLEM", + sd_card__unknown_filesystem: "Unknown filesystem.", + sd_card__unplug_and_insert_correct: "Please unplug the device and insert the correct SD card.", + sd_card__use_different_card: "Use a different card or format the SD card to the FAT32 filesystem.", + sd_card__wanna_format: "Do you really want to format the SD card?", + sd_card__wrong_sd_card: "Wrong SD card.", + send__address_path: "address path", + send__amount: "Amount:", + send__confirm_sending: "SENDING AMOUNT", + send__from_multiple_accounts: "Sending from multiple accounts.", + send__including_fee: "Including fee:", + send__maximum_fee: "Maximum fee:", + send__receiving_to_multisig: "Receiving to a multisig address.", + send__title_amount: "AMOUNT", + send__title_confirm_sending: "CONFIRM SENDING", + send__title_joint_transaction: "JOINT TRANSACTION", + send__title_receiving_to: "RECEIVING TO", + send__title_recipient: "RECIPIENT", + send__title_sending: "SENDING", + send__title_sending_amount: "SENDING AMOUNT", + send__title_sending_to: "SENDING TO", + send__to_the_total_amount: "To the total amount:", + send__total_amount: "Total amount:", + send__transaction_id: "Transaction ID:", + send__you_are_contributing: "You are contributing:", + share_words__words_in_order: " words in order.", + share_words__wrote_down_all: "I wrote down all ", + sign_message__bytes_template: "{} Bytes", + sign_message__confirm_address: "SIGNING ADDRESS", + sign_message__confirm_message: "CONFIRM MESSAGE", + sign_message__message_size: "Message size:", + sign_message__verify_address: "VERIFY ADDRESS", + stellar__account: "Account", + stellar__account_merge: "Account Merge", + stellar__account_thresholds: "Account Thresholds", + stellar__add_signer: "Add Signer", + stellar__add_trust: "Add trust", + stellar__all_will_be_sent_to: "All XLM will be sent to:", + stellar__allow_trust: "Allow trust", + stellar__asset: "Asset", + stellar__bump_sequence: "Bump Sequence", + stellar__buying: "Buying:", + stellar__clear_data: "Clear data", + stellar__clear_flags: "Clear flags", + stellar__confirm_issuer: "Confirm Issuer", + stellar__confirm_memo: "Confirm memo", + stellar__confirm_network: "Confirm network", + stellar__confirm_operation: "Confirm operation", + stellar__confirm_stellar: "Confirm Stellar", + stellar__confirm_timebounds: "Confirm timebounds", + stellar__create_account: "Create Account", + stellar__debited_amount: "Debited amount", + stellar__delete: "Delete", + stellar__delete_passive_offer: "Delete Passive Offer", + stellar__delete_trust: "Delete trust", + stellar__destination: "Destination:", + stellar__exchanges_require_memo: "Important: Many exchanges require a memo when depositing", + stellar__final_confirm: "Final confirm", + stellar__hash: "Hash:", + stellar__high: "High:", + stellar__home_domain: "Home Domain", + stellar__inflation: "Inflation", + stellar__initial_balance: "Initial Balance", + stellar__initialize_signing_with: "Initialize signing with", + stellar__issuer_template: "{} issuer:", + stellar__key: "Key:", + stellar__limit: "Limit:", + stellar__low: "Low:", + stellar__master_weight: "Master Weight:", + stellar__medium: "Medium:", + stellar__new_offer: "New Offer", + stellar__new_passive_offer: "New Passive Offer", + stellar__no_memo_set: "No memo set!", + stellar__no_restriction: "[no restriction]", + stellar__on_network_template: "Transaction is on {}", + stellar__path_pay: "Path Pay", + stellar__path_pay_at_least: "Path Pay at least", + stellar__pay: "Pay:", + stellar__pay_at_most: "Pay at most:", + stellar__preauth_transaction: "Pre-auth transaction:", + stellar__price_per_template: "Price per {}:", + stellar__private_network: "private network", + stellar__remove_signer: "Remove Signer", + stellar__revoke_trust: "Revoke trust", + stellar__selling: "Selling:", + stellar__set_data: "Set data", + stellar__set_flags: "Set flags", + stellar__set_sequence_to_template: "Set sequence to {}?", + stellar__sign_tx_count_template: "Sign this transaction made up of {}", + stellar__sign_tx_fee_template: " and pay {}\nfor fee?", + stellar__source_account: "Source account:", + stellar__testnet_network: "testnet network", + stellar__trusted_account: "Trusted Account", + stellar__update: "Update", + stellar__valid_from: "Valid from (UTC)", + stellar__valid_to: "Valid to (UTC)", + stellar__value_sha256: "Value (SHA-256):", + stellar__wanna_clean_value_key_template: "Do you want to clear value key {}?", + stellar__your_account: " your account", + tezos__address: "Address:", + tezos__amount: "Amount:", + tezos__baker_address: "Baker address:", + tezos__balance: "Balance:", + tezos__ballot: "Ballot:", + tezos__confirm_delegation: "Confirm delegation", + tezos__confirm_origination: "Confirm origination", + tezos__delegator: "Delegator:", + tezos__fee: "Fee:", + tezos__proposal: "Proposal", + tezos__register_delegate: "Register delegate", + tezos__remove_delegation: "Remove delegation", + tezos__submit_ballot: "Submit ballot", + tezos__submit_proposal: "Submit proposal", + tezos__submit_proposals: "Submit proposals", + tutorial__middle_click: "Press both left and right at the same\ntime to confirm.", + tutorial__press_and_hold: "Press and hold the right button to\napprove important operations.", + tutorial__ready_to_use: "You're ready to\nuse Trezor.", + tutorial__scroll_down: "Press right to scroll down to read all content when text doesn't fit on one screen.\n\rPress left to scroll up.", + tutorial__sure_you_want_skip: "Are you sure you\nwant to skip the tutorial?", + tutorial__title_hello: "HELLO", + tutorial__title_screen_scroll: "SCREEN SCROLL", + tutorial__title_skip: "SKIP TUTORIAL", + tutorial__title_tutorial_complete: "TUTORIAL COMPLETE", + tutorial__use_trezor: "Use Trezor by\nclicking the left and right buttons.\n\rContinue right.", + tutorial__welcome_press_right: "Welcome to Trezor. Press right to continue.", + u2f__get: "Increase and retrieve the U2F counter?", + u2f__set_template: "Set the U2F counter to {}?", + u2f__title_get: "GET U2F COUNTER", + u2f__title_set: "SET U2F COUNTER", + wipe__info: "All data will be erased.", + wipe__title: "WIPE DEVICE", + wipe__want_to_wipe: "Do you really want to wipe the device?\n", + wipe_code__change: "Change wipe code?", + wipe_code__changed: "Wipe code changed.", + wipe_code__diff_from_pin: "The wipe code must be different from your PIN.", + wipe_code__disabled: "Wipe code disabled.", + wipe_code__enabled: "Wipe code enabled.", + wipe_code__enter_new: "Enter new wipe code", + wipe_code__info: "Wipe code can be used to erase all data from this device.", + wipe_code__invalid: "Invalid wipe code", + wipe_code__mismatch: "Entered wipe codes do not match!", + wipe_code__reenter: "Re-enter wipe code", + wipe_code__reenter_to_confirm: "Please re-enter wipe code to confirm.", + wipe_code__title_check: "CHECK WIPE CODE", + wipe_code__title_invalid: "INVALID WIPE CODE", + wipe_code__title_settings: "WIPE CODE SETTINGS", + wipe_code__turn_off: "Turn off wipe code protection?", + wipe_code__turn_on: "Turn on wipe code protection?", + wipe_code__wipe_code_mismatch: "Wipe code mismatch", + word_count__title: "NUMBER OF WORDS", + words__are_you_sure: "Are you sure?", + words__array_of: "Array of", + words__buying: "Buying", + words__confirm: "Confirm", + words__contains: "Contains", + words__continue_anyway: "Continue anyway?", + words__continue_with: "Continue with", + words__error: "Error", + words__from: "from", + words__keep_it_safe: "Keep it safe!", + words__know_what_your_doing: "Continue only if you know what you are doing!", + words__my_trezor: "My Trezor", + words__outputs: "outputs", + words__please_check_again: "Please check again", + words__please_try_again: "Please try again", + words__really_wanna: "Do you really want to", + words__sign: "Sign", + words__title_check: "CHECK", + words__title_group: "GROUP", + words__title_information: "INFORMATION", + words__title_remember: "REMEMBER", + words__title_share: "SHARE", + words__title_shares: "SHARES", + words__title_success: "SUCCESS", + words__title_summary: "SUMMARY", + words__title_threshold: "THRESHOLD", + words__unknown: "Unknown", + words__warning: "Warning", +}; diff --git a/core/embed/rust/src/ui/translations/en.rs.mako b/core/embed/rust/src/ui/translations/en.rs.mako new file mode 100644 index 000000000..7fbd708b0 --- /dev/null +++ b/core/embed/rust/src/ui/translations/en.rs.mako @@ -0,0 +1,30 @@ +//! generated from en.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +<% +import json + +from pathlib import Path + +THIS = Path(local.filename).resolve() +SRCDIR = THIS.parent + +file = SRCDIR / "en.json" + +data = json.loads(file.read_text())["translations"] +items_to_write: list[tuple[str, str]] = [] +for section_name, section in data.items(): + for k, v in section.items(): + name = f"{section_name}__{k}" + items_to_write.append((name, v)) +items_to_write.sort(key=lambda x: x[0]) +%>\ +use super::general::TranslationsGeneral; + +#[rustfmt::skip] +pub const EN_TRANSLATIONS: TranslationsGeneral = TranslationsGeneral { +% for k, v in items_to_write: + ${k}: ${utf8_str(v)}, +% endfor +}; diff --git a/core/embed/rust/src/ui/translations/export.rs b/core/embed/rust/src/ui/translations/export.rs new file mode 100644 index 000000000..8ecf59bc6 --- /dev/null +++ b/core/embed/rust/src/ui/translations/export.rs @@ -0,0 +1,850 @@ +//! generated from export.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +use super::micropython::{language_name_obj, TR_OBJ}; +use crate::micropython::{map::Map, module::Module, qstr::Qstr}; + +#[no_mangle] +#[rustfmt::skip] +pub static mp_module_trezortranslate: Module = obj_module! { + // TODO: add function to get all the translations keys in order + // - so that client can validate it is sending correct keys in correct order + + /// def language_name() -> str: + /// """Get the name of the current language.""" + Qstr::MP_QSTR_language_name => obj_fn_0!(language_name_obj).as_obj(), + + /// class TR: + /// """Translation object with attributes.""" + /// addr_mismatch__contact_support: str + /// addr_mismatch__key_mismatch: str + /// addr_mismatch__mismatch: str + /// addr_mismatch__support_url: str + /// addr_mismatch__title: str + /// addr_mismatch__title_key_mismatch: str + /// addr_mismatch__wrong_derication_path: str + /// addr_mismatch__xpub_mismatch: str + /// address__address: str + /// address__public_key: str + /// address__title_cosigner: str + /// address__title_receive_address: str + /// address__title_yours: str + /// address_details__account: str + /// address_details__derivation_path: str + /// address_details__title_receive_address: str + /// address_details__title_receiving_to: str + /// authenticate__confirm_template: str + /// authenticate__header: str + /// auto_lock__change_template: str + /// auto_lock__title: str + /// backup__can_back_up_anytime: str + /// backup__it_should_be_backed_up: str + /// backup__it_should_be_backed_up_now: str + /// backup__new_wallet_created: str + /// backup__new_wallet_successfully_created: str + /// backup__recover_anytime: str + /// backup__title_backup_wallet: str + /// backup__title_skip: str + /// backup__want_to_skip: str + /// binance__buy: str + /// binance__confirm_cancel: str + /// binance__confirm_input: str + /// binance__confirm_order: str + /// binance__confirm_output: str + /// binance__order_id: str + /// binance__pair: str + /// binance__price: str + /// binance__quantity: str + /// binance__sell: str + /// binance__sender_address: str + /// binance__side: str + /// binance__unknown: str + /// bitcoin__commitment_data: str + /// bitcoin__confirm_locktime: str + /// bitcoin__create_proof_of_ownership: str + /// bitcoin__high_mining_fee_template: str + /// bitcoin__locktime_no_effect: str + /// bitcoin__locktime_set_to: str + /// bitcoin__locktime_set_to_blockheight: str + /// bitcoin__lot_of_change_outputs: str + /// bitcoin__multiple_accounts: str + /// bitcoin__new_fee_rate: str + /// bitcoin__simple_send_of: str + /// bitcoin__ticket_amount: str + /// bitcoin__title_confirm_details: str + /// bitcoin__title_finalize_transaction: str + /// bitcoin__title_high_mining_fee: str + /// bitcoin__title_meld_transaction: str + /// bitcoin__title_modify_amount: str + /// bitcoin__title_payjoin: str + /// bitcoin__title_proof_of_ownership: str + /// bitcoin__title_purchase_ticket: str + /// bitcoin__title_update_transaction: str + /// bitcoin__unknown_path: str + /// bitcoin__unknown_transaction: str + /// bitcoin__unusually_high_fee: str + /// bitcoin__unverified_external_inputs: str + /// bitcoin__valid_signature: str + /// bitcoin__voting_rights: str + /// buttons__abort: str + /// buttons__access: str + /// buttons__again: str + /// buttons__allow: str + /// buttons__back_up: str + /// buttons__cancel: str + /// buttons__change: str + /// buttons__check: str + /// buttons__check_again: str + /// buttons__close: str + /// buttons__confirm: str + /// buttons__continue: str + /// buttons__details: str + /// buttons__enable: str + /// buttons__enter: str + /// buttons__enter_share: str + /// buttons__export: str + /// buttons__format: str + /// buttons__go_back: str + /// buttons__hold_to_confirm: str + /// buttons__info: str + /// buttons__install: str + /// buttons__more_info: str + /// buttons__ok_i_understand: str + /// buttons__purchase: str + /// buttons__quit: str + /// buttons__restart: str + /// buttons__retry: str + /// buttons__select: str + /// buttons__set: str + /// buttons__show_all: str + /// buttons__show_words: str + /// buttons__skip: str + /// buttons__try_again: str + /// buttons__turn_off: str + /// buttons__turn_on: str + /// cardano__addr_base: str + /// cardano__addr_enterprise: str + /// cardano__addr_legacy: str + /// cardano__addr_pointer: str + /// cardano__addr_reward: str + /// cardano__address_no_staking: str + /// cardano__amount: str + /// cardano__amount_burned_decimals_unknown: str + /// cardano__amount_minted_decimals_unknown: str + /// cardano__amount_sent_decimals_unknown: str + /// cardano__anonymous_pool: str + /// cardano__asset_fingerprint: str + /// cardano__auxiliary_data_hash: str + /// cardano__block: str + /// cardano__catalyst: str + /// cardano__certificate: str + /// cardano__certificate_path: str + /// cardano__change_output: str + /// cardano__change_output_path: str + /// cardano__change_output_staking_path: str + /// cardano__check_all_items: str + /// cardano__choose_level_of_details: str + /// cardano__collateral_input_id: str + /// cardano__collateral_input_index: str + /// cardano__collateral_output_contains_tokens: str + /// cardano__collateral_return: str + /// cardano__confirm: str + /// cardano__confirm_signing_stake_pool: str + /// cardano__confirm_transaction: str + /// cardano__confirming_a_multisig_transaction: str + /// cardano__confirming_pool_registration: str + /// cardano__confirming_transction: str + /// cardano__cost: str + /// cardano__credential_mismatch: str + /// cardano__datum_hash: str + /// cardano__delegating_to: str + /// cardano__for_account_and_index_template: str + /// cardano__for_account_template: str + /// cardano__for_key_hash: str + /// cardano__for_script: str + /// cardano__inline_datum: str + /// cardano__input_id: str + /// cardano__input_index: str + /// cardano__intro_text_address: str + /// cardano__intro_text_change: str + /// cardano__intro_text_owned_by_device: str + /// cardano__intro_text_registration_payment: str + /// cardano__key_hash: str + /// cardano__margin: str + /// cardano__multisig_path: str + /// cardano__nested_scripts_template: str + /// cardano__network: str + /// cardano__no_output_tx: str + /// cardano__nonce: str + /// cardano__other: str + /// cardano__path: str + /// cardano__pledge: str + /// cardano__pointer: str + /// cardano__policy_id: str + /// cardano__pool_metadata_hash: str + /// cardano__pool_metadata_url: str + /// cardano__pool_owner: str + /// cardano__pool_owner_path: str + /// cardano__pool_reward_account: str + /// cardano__reference_input_id: str + /// cardano__reference_input_index: str + /// cardano__reference_script: str + /// cardano__required_signer: str + /// cardano__reward: str + /// cardano__reward_address: str + /// cardano__reward_eligibility_warning: str + /// cardano__rewards_go_to: str + /// cardano__script: str + /// cardano__script_all: str + /// cardano__script_any: str + /// cardano__script_data_hash: str + /// cardano__script_hash: str + /// cardano__script_invalid_before: str + /// cardano__script_invalid_hereafter: str + /// cardano__script_key: str + /// cardano__script_n_of_k: str + /// cardano__script_reward: str + /// cardano__sending: str + /// cardano__show_simple: str + /// cardano__sign_tx_path_template: str + /// cardano__stake_delegation: str + /// cardano__stake_deregistration: str + /// cardano__stake_pool_registration: str + /// cardano__stake_pool_registration_pool_id: str + /// cardano__stake_registration: str + /// cardano__staking_key_for_account: str + /// cardano__to_pool: str + /// cardano__token_minting_path: str + /// cardano__total_collateral: str + /// cardano__transaction: str + /// cardano__transaction_contains_minting_or_burning: str + /// cardano__transaction_contains_script_address_no_datum: str + /// cardano__transaction_fee: str + /// cardano__transaction_id: str + /// cardano__transaction_no_collateral_input: str + /// cardano__transaction_no_script_data_hash: str + /// cardano__transaction_output_contains_tokens: str + /// cardano__ttl: str + /// cardano__unknown: str + /// cardano__unknown_collateral_amount: str + /// cardano__unusual_path: str + /// cardano__valid_since: str + /// cardano__verify_script: str + /// cardano__vote_key_registration: str + /// cardano__vote_public_key: str + /// cardano__voting_purpose: str + /// cardano__warning: str + /// cardano__weight: str + /// cardano__withdrawal_for_address_template: str + /// cardano__witness_path: str + /// cardano__x_of_y_signatures_template: str + /// coinjoin__access_account: str + /// coinjoin__do_not_disconnect: str + /// coinjoin__max_mining_fee: str + /// coinjoin__max_rounds: str + /// coinjoin__title: str + /// coinjoin__title_do_not_disconnect: str + /// coinjoin__title_progress: str + /// coinjoin__waiting_for_others: str + /// confirm_total__account: str + /// confirm_total__fee_rate: str + /// confirm_total__sending_from_account: str + /// confirm_total__title_fee: str + /// confirm_total__title_sending_from: str + /// debug__loading_seed: str + /// debug__loading_seed_not_recommended: str + /// device_name__change_template: str + /// device_name__title: str + /// entropy__send: str + /// entropy__title: str + /// entropy__title_confirm: str + /// eos__about_to_sign_template: str + /// eos__account: str + /// eos__action_name: str + /// eos__amount: str + /// eos__arbitrary_data: str + /// eos__buy_ram: str + /// eos__bytes: str + /// eos__cancel_vote: str + /// eos__checksum: str + /// eos__code: str + /// eos__contract: str + /// eos__cpu: str + /// eos__creator: str + /// eos__delegate: str + /// eos__delete_auth: str + /// eos__from: str + /// eos__link_auth: str + /// eos__memo: str + /// eos__name: str + /// eos__net: str + /// eos__new_account: str + /// eos__no: str + /// eos__owner: str + /// eos__parent: str + /// eos__payer: str + /// eos__permission: str + /// eos__proxy: str + /// eos__receiver: str + /// eos__refund: str + /// eos__requirement: str + /// eos__sell_ram: str + /// eos__sender: str + /// eos__sign_transaction: str + /// eos__threshold: str + /// eos__to: str + /// eos__transfer: str + /// eos__type: str + /// eos__undelegate: str + /// eos__unlink_auth: str + /// eos__update_auth: str + /// eos__vote_for_producers: str + /// eos__vote_for_proxy: str + /// eos__voter: str + /// eos__yes: str + /// ethereum__amount_sent: str + /// ethereum__confirm_fee: str + /// ethereum__contract: str + /// ethereum__data_size_template: str + /// ethereum__gas_limit: str + /// ethereum__gas_price: str + /// ethereum__max_gas_price: str + /// ethereum__name_and_version: str + /// ethereum__new_contract: str + /// ethereum__no_message_field: str + /// ethereum__priority_fee: str + /// ethereum__show_full_array: str + /// ethereum__show_full_domain: str + /// ethereum__show_full_message: str + /// ethereum__show_full_struct: str + /// ethereum__sign_eip712: str + /// ethereum__title_confirm_data: str + /// ethereum__title_confirm_domain: str + /// ethereum__title_confirm_message: str + /// ethereum__title_confirm_struct: str + /// ethereum__title_confirm_typed_data: str + /// ethereum__title_signing_address: str + /// ethereum__units_template: str + /// ethereum__unknown_token: str + /// ethereum__valid_signature: str + /// experimental_mode__enable: str + /// experimental_mode__only_for_dev: str + /// experimental_mode__title: str + /// fido__already_registered: str + /// fido__device_already_registered: str + /// fido__device_already_registered_with_template: str + /// fido__device_not_registered: str + /// fido__does_not_belong: str + /// fido__erase_credentials: str + /// fido__export_credentials: str + /// fido__not_registered: str + /// fido__not_registered_with_template: str + /// fido__please_enable_pin_protection: str + /// fido__title_authenticate: str + /// fido__title_import_credential: str + /// fido__title_list_credentials: str + /// fido__title_register: str + /// fido__title_remove_credential: str + /// fido__title_reset: str + /// fido__title_u2f_auth: str + /// fido__title_u2f_register: str + /// fido__title_verify_user: str + /// fido__unable_to_verify_user: str + /// fido__wanna_erase_credentials: str + /// firmware_update__title: str + /// firmware_update__title_fingerprint: str + /// homescreen__click_to_connect: str + /// homescreen__click_to_unlock: str + /// homescreen__title_backup_failed: str + /// homescreen__title_backup_needed: str + /// homescreen__title_coinjoin_authorized: str + /// homescreen__title_experimental_mode: str + /// homescreen__title_hold_to_lock: str + /// homescreen__title_no_usb_connection: str + /// homescreen__title_pin_not_set: str + /// homescreen__title_seedless: str + /// homescreen__title_set: str + /// inputs__back: str + /// inputs__cancel: str + /// inputs__delete: str + /// inputs__enter: str + /// inputs__return: str + /// inputs__show: str + /// inputs__space: str + /// joint__title: str + /// joint__to_the_total_amount: str + /// joint__you_are_contributing: str + /// lockscreen__tap_to_connect: str + /// lockscreen__tap_to_unlock: str + /// lockscreen__title_locked: str + /// lockscreen__title_not_connected: str + /// misc__decrypt_value: str + /// misc__encrypt_value: str + /// misc__title_suite_labeling: str + /// modify_amount__address: str + /// modify_amount__decrease_amount: str + /// modify_amount__increase_amount: str + /// modify_amount__new_amount: str + /// modify_amount__title: str + /// modify_fee__decrease_fee: str + /// modify_fee__fee_rate: str + /// modify_fee__increase_fee: str + /// modify_fee__new_transaction_fee: str + /// modify_fee__no_change: str + /// modify_fee__title: str + /// modify_fee__transaction_fee: str + /// monero__confirm_export: str + /// monero__confirm_fee: str + /// monero__confirm_ki_sync: str + /// monero__confirm_refresh: str + /// monero__confirm_unlock_time: str + /// monero__hashing_inputs: str + /// monero__payment_id: str + /// monero__postprocessing: str + /// monero__processing: str + /// monero__processing_inputs: str + /// monero__processing_outputs: str + /// monero__signing: str + /// monero__signing_inputs: str + /// monero__unlock_time_set_template: str + /// monero__wanna_export_tx_der: str + /// monero__wanna_export_tx_key: str + /// monero__wanna_export_watchkey: str + /// monero__wanna_start_refresh: str + /// monero__wanna_sync_key_images: str + /// nem__absolute: str + /// nem__activate: str + /// nem__add: str + /// nem__confirm_action: str + /// nem__confirm_address: str + /// nem__confirm_creation_fee: str + /// nem__confirm_fee: str + /// nem__confirm_mosaic: str + /// nem__confirm_multisig_fee: str + /// nem__confirm_namespace: str + /// nem__confirm_payload: str + /// nem__confirm_properties: str + /// nem__confirm_rental_fee: str + /// nem__confirm_transfer_of: str + /// nem__convert_account_to_multisig: str + /// nem__cosign_transaction_for: str + /// nem__cosignatory: str + /// nem__create_mosaic: str + /// nem__create_namespace: str + /// nem__deactivate: str + /// nem__decrease: str + /// nem__description: str + /// nem__divisibility_and_levy_cannot_be_shown: str + /// nem__encrypted: str + /// nem__final_confirm: str + /// nem__immutable: str + /// nem__increase: str + /// nem__initial_supply: str + /// nem__initiate_transaction_for: str + /// nem__levy_divisibility: str + /// nem__levy_fee: str + /// nem__levy_fee_of: str + /// nem__levy_mosaic: str + /// nem__levy_namespace: str + /// nem__levy_recipient: str + /// nem__levy_type: str + /// nem__modify_supply_for: str + /// nem__modify_the_number_of_cosignatories_by: str + /// nem__mutable: str + /// nem__no: str + /// nem__of: str + /// nem__percentile: str + /// nem__raw_units_template: str + /// nem__remote_harvesting: str + /// nem__remove: str + /// nem__set_minimum_cosignatories_to: str + /// nem__sign_tx_fee_template: str + /// nem__supply_change: str + /// nem__supply_units_template: str + /// nem__transferable: str + /// nem__under_namespace: str + /// nem__unencrypted: str + /// nem__unknown_mosaic: str + /// nem__yes: str + /// passphrase__access_hidden_wallet: str + /// passphrase__always_on_device: str + /// passphrase__from_host_not_shown: str + /// passphrase__hidden_wallet: str + /// passphrase__hide: str + /// passphrase__next_screen_will_show_passphrase: str + /// passphrase__please_enter: str + /// passphrase__revoke_on_device: str + /// passphrase__title_confirm: str + /// passphrase__title_enter: str + /// passphrase__title_hide: str + /// passphrase__title_settings: str + /// passphrase__title_source: str + /// passphrase__turn_off: str + /// passphrase__turn_on: str + /// pin__change: str + /// pin__changed: str + /// pin__cursor_will_change: str + /// pin__diff_from_wipe_code: str + /// pin__disabled: str + /// pin__enabled: str + /// pin__enter: str + /// pin__enter_new: str + /// pin__entered_not_valid: str + /// pin__info: str + /// pin__invalid_pin: str + /// pin__last_attempt: str + /// pin__mismatch: str + /// pin__pin_mismatch: str + /// pin__please_check_again: str + /// pin__reenter_new: str + /// pin__reenter_to_confirm: str + /// pin__should_be_long: str + /// pin__title_check_pin: str + /// pin__title_settings: str + /// pin__title_wrong_pin: str + /// pin__tries_left: str + /// pin__turn_off: str + /// pin__turn_on: str + /// pin__wrong_pin: str + /// plurals__contains_x_keys: str + /// plurals__lock_after_x_hours: str + /// plurals__lock_after_x_milliseconds: str + /// plurals__lock_after_x_minutes: str + /// plurals__lock_after_x_seconds: str + /// plurals__sign_x_actions: str + /// plurals__transaction_of_x_operations: str + /// plurals__x_groups_needed: str + /// plurals__x_shares_needed: str + /// progress__authenticity_check: str + /// progress__done: str + /// progress__loading_transaction: str + /// progress__one_second_left: str + /// progress__please_wait: str + /// progress__processing: str + /// progress__refreshing: str + /// progress__signing_transaction: str + /// progress__syncing: str + /// progress__x_seconds_left_template: str + /// reboot_to_bootloader__restart: str + /// reboot_to_bootloader__title: str + /// reboot_to_bootloader__version_by_template: str + /// recovery__cancel_dry_run: str + /// recovery__check_dry_run: str + /// recovery__cursor_will_change: str + /// recovery__dry_run_bip39_valid_match: str + /// recovery__dry_run_bip39_valid_mismatch: str + /// recovery__dry_run_slip39_valid_match: str + /// recovery__dry_run_slip39_valid_mismatch: str + /// recovery__enter_any_share: str + /// recovery__enter_backup: str + /// recovery__enter_different_share: str + /// recovery__enter_share_from_diff_group: str + /// recovery__group_num_template: str + /// recovery__group_threshold_reached: str + /// recovery__invalid_seed_entered: str + /// recovery__invalid_share_entered: str + /// recovery__more_shares_needed: str + /// recovery__num_of_words: str + /// recovery__only_first_n_letters: str + /// recovery__progress_will_be_lost: str + /// recovery__select_num_of_words: str + /// recovery__share_already_entered: str + /// recovery__share_from_another_shamir: str + /// recovery__share_num_template: str + /// recovery__title: str + /// recovery__title_cancel_dry_run: str + /// recovery__title_cancel_recovery: str + /// recovery__title_dry_run: str + /// recovery__title_recover: str + /// recovery__title_remaining_shares: str + /// recovery__type_word_x_of_y_template: str + /// recovery__wallet_recovered: str + /// recovery__wanna_cancel_dry_run: str + /// recovery__wanna_cancel_recovery: str + /// recovery__word_count_template: str + /// recovery__word_x_of_y_template: str + /// recovery__x_more_items_starting_template_plural: str + /// recovery__x_more_shares_needed_template_plural: str + /// recovery__x_of_y_entered_template: str + /// recovery__you_have_entered: str + /// reset__advanced_group_threshold_info: str + /// reset__all_x_of_y_template: str + /// reset__any_x_of_y_template: str + /// reset__button_create: str + /// reset__button_recover: str + /// reset__by_continuing: str + /// reset__check_backup_title: str + /// reset__check_group_share_title_template: str + /// reset__check_seed_title: str + /// reset__check_share_title_template: str + /// reset__continue_with_next_share: str + /// reset__continue_with_share_template: str + /// reset__finished_verifying_group_template: str + /// reset__finished_verifying_seed: str + /// reset__finished_verifying_shares: str + /// reset__group_description: str + /// reset__group_info: str + /// reset__group_share_checked_successfully_template: str + /// reset__group_share_title_template: str + /// reset__more_info_at: str + /// reset__need_all_share_template: str + /// reset__need_any_share_template: str + /// reset__needed_to_form_a_group: str + /// reset__needed_to_recover_your_wallet: str + /// reset__never_make_digital_copy: str + /// reset__num_of_share_holders_template: str + /// reset__num_of_shares_advanced_info_template: str + /// reset__num_of_shares_basic_info: str + /// reset__num_shares_for_group_template: str + /// reset__number_of_shares_info: str + /// reset__one_share: str + /// reset__only_one_share_will_be_created: str + /// reset__recovery_seed_title: str + /// reset__recovery_share_title_template: str + /// reset__required_number_of_groups: str + /// reset__select_correct_word: str + /// reset__select_word_template: str + /// reset__select_word_x_of_y_template: str + /// reset__set_it_to_count_template: str + /// reset__share_checked_successfully_template: str + /// reset__share_words_title: str + /// reset__slip39_checklist_num_groups: str + /// reset__slip39_checklist_num_shares: str + /// reset__slip39_checklist_set_num_groups: str + /// reset__slip39_checklist_set_num_shares: str + /// reset__slip39_checklist_set_sizes: str + /// reset__slip39_checklist_set_sizes_longer: str + /// reset__slip39_checklist_set_threshold: str + /// reset__slip39_checklist_title: str + /// reset__slip39_checklist_write_down: str + /// reset__slip39_checklist_write_down_recovery: str + /// reset__the_threshold_sets_the_number_of_shares: str + /// reset__threshold_info: str + /// reset__title_backup_is_done: str + /// reset__title_create_wallet: str + /// reset__title_create_wallet_shamir: str + /// reset__title_group_threshold: str + /// reset__title_number_of_groups: str + /// reset__title_number_of_shares: str + /// reset__title_set_group_threshold: str + /// reset__title_set_number_of_groups: str + /// reset__title_set_number_of_shares: str + /// reset__title_set_threshold: str + /// reset__to_form_group_template: str + /// reset__tos_link: str + /// reset__total_number_of_shares_in_group_template: str + /// reset__use_your_backup: str + /// reset__write_down_words_template: str + /// reset__wrong_word_selected: str + /// reset__you_need_one_share: str + /// reset__your_backup_is_done: str + /// ripple__confirm_tag: str + /// ripple__destination_tag_template: str + /// rotation__change_template: str + /// rotation__east: str + /// rotation__north: str + /// rotation__south: str + /// rotation__title_change: str + /// rotation__west: str + /// safety_checks__approve_unsafe_always: str + /// safety_checks__approve_unsafe_temporary: str + /// safety_checks__enforce_strict: str + /// safety_checks__title: str + /// safety_checks__title_safety_override: str + /// sd_card__all_data_will_be_lost: str + /// sd_card__card_required: str + /// sd_card__disable: str + /// sd_card__disabled: str + /// sd_card__enable: str + /// sd_card__enabled: str + /// sd_card__error: str + /// sd_card__format_card: str + /// sd_card__insert_correct_card: str + /// sd_card__please_insert: str + /// sd_card__please_unplug_and_insert: str + /// sd_card__problem_accessing: str + /// sd_card__refresh: str + /// sd_card__refreshed: str + /// sd_card__restart: str + /// sd_card__title: str + /// sd_card__title_problem: str + /// sd_card__unknown_filesystem: str + /// sd_card__unplug_and_insert_correct: str + /// sd_card__use_different_card: str + /// sd_card__wanna_format: str + /// sd_card__wrong_sd_card: str + /// send__address_path: str + /// send__amount: str + /// send__confirm_sending: str + /// send__from_multiple_accounts: str + /// send__including_fee: str + /// send__maximum_fee: str + /// send__receiving_to_multisig: str + /// send__title_amount: str + /// send__title_confirm_sending: str + /// send__title_joint_transaction: str + /// send__title_receiving_to: str + /// send__title_recipient: str + /// send__title_sending: str + /// send__title_sending_amount: str + /// send__title_sending_to: str + /// send__to_the_total_amount: str + /// send__total_amount: str + /// send__transaction_id: str + /// send__you_are_contributing: str + /// share_words__words_in_order: str + /// share_words__wrote_down_all: str + /// sign_message__bytes_template: str + /// sign_message__confirm_address: str + /// sign_message__confirm_message: str + /// sign_message__message_size: str + /// sign_message__verify_address: str + /// stellar__account: str + /// stellar__account_merge: str + /// stellar__account_thresholds: str + /// stellar__add_signer: str + /// stellar__add_trust: str + /// stellar__all_will_be_sent_to: str + /// stellar__allow_trust: str + /// stellar__asset: str + /// stellar__bump_sequence: str + /// stellar__buying: str + /// stellar__clear_data: str + /// stellar__clear_flags: str + /// stellar__confirm_issuer: str + /// stellar__confirm_memo: str + /// stellar__confirm_network: str + /// stellar__confirm_operation: str + /// stellar__confirm_stellar: str + /// stellar__confirm_timebounds: str + /// stellar__create_account: str + /// stellar__debited_amount: str + /// stellar__delete: str + /// stellar__delete_passive_offer: str + /// stellar__delete_trust: str + /// stellar__destination: str + /// stellar__exchanges_require_memo: str + /// stellar__final_confirm: str + /// stellar__hash: str + /// stellar__high: str + /// stellar__home_domain: str + /// stellar__inflation: str + /// stellar__initial_balance: str + /// stellar__initialize_signing_with: str + /// stellar__issuer_template: str + /// stellar__key: str + /// stellar__limit: str + /// stellar__low: str + /// stellar__master_weight: str + /// stellar__medium: str + /// stellar__new_offer: str + /// stellar__new_passive_offer: str + /// stellar__no_memo_set: str + /// stellar__no_restriction: str + /// stellar__on_network_template: str + /// stellar__path_pay: str + /// stellar__path_pay_at_least: str + /// stellar__pay: str + /// stellar__pay_at_most: str + /// stellar__preauth_transaction: str + /// stellar__price_per_template: str + /// stellar__private_network: str + /// stellar__remove_signer: str + /// stellar__revoke_trust: str + /// stellar__selling: str + /// stellar__set_data: str + /// stellar__set_flags: str + /// stellar__set_sequence_to_template: str + /// stellar__sign_tx_count_template: str + /// stellar__sign_tx_fee_template: str + /// stellar__source_account: str + /// stellar__testnet_network: str + /// stellar__trusted_account: str + /// stellar__update: str + /// stellar__valid_from: str + /// stellar__valid_to: str + /// stellar__value_sha256: str + /// stellar__wanna_clean_value_key_template: str + /// stellar__your_account: str + /// tezos__address: str + /// tezos__amount: str + /// tezos__baker_address: str + /// tezos__balance: str + /// tezos__ballot: str + /// tezos__confirm_delegation: str + /// tezos__confirm_origination: str + /// tezos__delegator: str + /// tezos__fee: str + /// tezos__proposal: str + /// tezos__register_delegate: str + /// tezos__remove_delegation: str + /// tezos__submit_ballot: str + /// tezos__submit_proposal: str + /// tezos__submit_proposals: str + /// tutorial__middle_click: str + /// tutorial__press_and_hold: str + /// tutorial__ready_to_use: str + /// tutorial__scroll_down: str + /// tutorial__sure_you_want_skip: str + /// tutorial__title_hello: str + /// tutorial__title_screen_scroll: str + /// tutorial__title_skip: str + /// tutorial__title_tutorial_complete: str + /// tutorial__use_trezor: str + /// tutorial__welcome_press_right: str + /// u2f__get: str + /// u2f__set_template: str + /// u2f__title_get: str + /// u2f__title_set: str + /// wipe__info: str + /// wipe__title: str + /// wipe__want_to_wipe: str + /// wipe_code__change: str + /// wipe_code__changed: str + /// wipe_code__diff_from_pin: str + /// wipe_code__disabled: str + /// wipe_code__enabled: str + /// wipe_code__enter_new: str + /// wipe_code__info: str + /// wipe_code__invalid: str + /// wipe_code__mismatch: str + /// wipe_code__reenter: str + /// wipe_code__reenter_to_confirm: str + /// wipe_code__title_check: str + /// wipe_code__title_invalid: str + /// wipe_code__title_settings: str + /// wipe_code__turn_off: str + /// wipe_code__turn_on: str + /// wipe_code__wipe_code_mismatch: str + /// word_count__title: str + /// words__are_you_sure: str + /// words__array_of: str + /// words__buying: str + /// words__confirm: str + /// words__contains: str + /// words__continue_anyway: str + /// words__continue_with: str + /// words__error: str + /// words__from: str + /// words__keep_it_safe: str + /// words__know_what_your_doing: str + /// words__my_trezor: str + /// words__outputs: str + /// words__please_check_again: str + /// words__please_try_again: str + /// words__really_wanna: str + /// words__sign: str + /// words__title_check: str + /// words__title_group: str + /// words__title_information: str + /// words__title_remember: str + /// words__title_share: str + /// words__title_shares: str + /// words__title_success: str + /// words__title_summary: str + /// words__title_threshold: str + /// words__unknown: str + /// words__warning: str + Qstr::MP_QSTR_TR => TR_OBJ.as_obj(), +}; diff --git a/core/embed/rust/src/ui/translations/export.rs.mako b/core/embed/rust/src/ui/translations/export.rs.mako new file mode 100644 index 000000000..f9ad784f9 --- /dev/null +++ b/core/embed/rust/src/ui/translations/export.rs.mako @@ -0,0 +1,43 @@ +//! generated from export.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +<% +import json +from pathlib import Path + +THIS = Path(local.filename).resolve() +SRCDIR = THIS.parent + +en_file = SRCDIR / "en.json" +en_data = json.loads(en_file.read_text())["translations"] + +def get_all_json_keys(data: dict) -> set[str]: + keys: set[str] = set() + for section_name, section in data.items(): + for k, v in section.items(): + keys.add(f"{section_name}__{k}") + return keys + +en_keys = get_all_json_keys(en_data) +%>\ +use super::micropython::{language_name_obj, TR_OBJ}; +use crate::micropython::{map::Map, module::Module, qstr::Qstr}; + +#[no_mangle] +#[rustfmt::skip] +pub static mp_module_trezortranslate: Module = obj_module! { + // TODO: add function to get all the translations keys in order + // - so that client can validate it is sending correct keys in correct order + + /// def language_name() -> str: + /// """Get the name of the current language.""" + Qstr::MP_QSTR_language_name => obj_fn_0!(language_name_obj).as_obj(), + + /// class TR: + /// """Translation object with attributes.""" + % for name in sorted(en_keys): + /// ${name}: str + % endfor + Qstr::MP_QSTR_TR => TR_OBJ.as_obj(), +}; diff --git a/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_bold_8_cs.json b/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_bold_8_cs.json new file mode 100644 index 000000000..03470a832 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_bold_8_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "060707000718c7b3cffcc0" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0607070007cde7b3c33780" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0607070007cdefb3cf3f80" + }, + { + "char": "É", + "utf8": "C389", + "data": "060707000718cff0f30fc0" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "0607070007cdeff0f30fc0" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "0307050107786db0" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "0607070007cdecfbff7cc0" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "060707000718c7b3cf3780" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0607070007cdefb3cfecc0" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0607070007cde7b0783f80" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0607070007cdefcc30c300" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "060707000718ccf3cf3780" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "06070700077b3780cf3780" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "060707000718ccf378c300" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0607070007cdefc6318fc0" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "060707000718c7837f37c0" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0607070007cde7b3c33780" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "0b070d00070c618df3660cc1981f00" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "060707000718c7b3ff0780" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0607070007cde7b3ff0780" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "0307050107786db0" + }, + { + "char": "ň", + "utf8": "C588", + "data": "0607070007cdefb3cf3cc0" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "060707000718c7b3cf3780" + }, + { + "char": "ř", + "utf8": "C599", + "data": "0607070007cdecf7e30c00" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "0607070007cde7b0783f80" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "0a070b000700d83f998060180380" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "060707000718ccf3cf3780" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "06070700077b3780cf3780" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "060807000718ccf3cdf0de00" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "0607070007cdefc6318fc0" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_bold_8_fr.json b/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_bold_8_fr.json new file mode 100644 index 000000000..b3622f390 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_bold_8_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "060707000760c7b3cffcc0" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0707070007fd0de66cdfb300" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "0a070b00077ff30cc33cfc330cfc" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "06080700077b3c30cde19c00" + }, + { + "char": "È", + "utf8": "C388", + "data": "060707000760cff0f30fc0" + }, + { + "char": "É", + "utf8": "C389", + "data": "060707000718cff0f30fc0" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "06070700077b3ff0f30fc0" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "0607070007cc0ff0f30fc0" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "0507050007f4d8c63180" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "05070500079818c63180" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "0707070007fd0de66cd99e00" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "060707000760ccf3cf3780" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "06070700077b3033cf3780" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0607070007cc0cf3cf3780" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "0607070007cc0cf378c300" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "0a070b00077ff30cc33ccc3307fc" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "060707000760c7837f37c0" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "0707070007fd0de067d99f00" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "0a050b00057f8337ff307f80" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "06080700077b3c3378c19c00" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "060707000760c7b3ff0780" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "060707000718c7b3ff0780" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0707070007fd0de66fd81e00" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0607070007cc07b3ff0780" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "0507050007f4d8c63180" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "05070500079818c63180" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0707070007fd0de66cd99e00" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "060707000760ccf3cf3780" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "06070700077b3033cf3780" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "0607070007cc0cf3cf3780" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0608070007cc0cf3cdf0de00" + }, + { + "char": "œ", + "utf8": "C593", + "data": "0a050b00057fb33cff307f80" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_regular_8_cs.json b/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_regular_8_cs.json new file mode 100644 index 000000000..8ab9e8752 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_regular_8_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "0507060007111d18fe20" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0507060007511d1845c0" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0507060007513d18c7c0" + }, + { + "char": "É", + "utf8": "C389", + "data": "0507060007113f0e43e0" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "0507060007513f0e43e0" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "020704010762a8" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "050706000751239ace20" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "0507060007111d18c5c0" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0507060007513d18fa20" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0507060007511d0707c0" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0507060007513e421080" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "0507060007112318c5c0" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "0507060007228918c5c0" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "05070600071123151080" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0507060007513e2223e0" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "0507060007111c17c5e0" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0507060007511d1845c0" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "09070b000708845e51088441e0" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0507060007111d1fc1c0" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0507060007511d1fc1c0" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "020704010762a8" + }, + { + "char": "ň", + "utf8": "C588", + "data": "0507060007513d18c620" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "0507060007111d18c5c0" + }, + { + "char": "ř", + "utf8": "C599", + "data": "050706000751274c4200" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "0507060007511d0707c0" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "08070a00070141f24040403000" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "0507060007112318c5c0" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "0507060007228918c5c0" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "0508060007112318bc2e00" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "0507060007513e2223e0" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_regular_8_fr.json b/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_regular_8_fr.json new file mode 100644 index 000000000..d6f1a13a4 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_pixeloperator_regular_8_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "0507060007411d18fe20" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0507060007229d18fe20" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "09070a00077fc42211cf84423e" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "0508060007746108b84c00" + }, + { + "char": "È", + "utf8": "C388", + "data": "0507060007413f0e43e0" + }, + { + "char": "É", + "utf8": "C389", + "data": "0507060007113f0e43e0" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "050706000722bf0e43e0" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "0507060007503f0e43e0" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "0307040007542490" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "0307040007a12490" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "0507060007229d18c5c0" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "0507060007412318c5c0" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "0507060007228118c5c0" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0507060007502318c5c0" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "05070600075023151080" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "09070a00077fc42211c88441fe" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "0507060007411c17c5e0" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "0507060007229c17c5e0" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "09050a000577045ff10770" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "0508060007746117104c00" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "0507060007411d1fc1c0" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0507060007111d1fc1c0" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0507060007229d1fc1c0" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0507060007501d1fc1c0" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "0307040007542490" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "0307040007a12490" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0507060007229d18c5c0" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "0507060007412318c5c0" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "0507060007228118c5c0" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "0507060007502318c5c0" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0508060007502318bc2e00" + }, + { + "char": "œ", + "utf8": "C593", + "data": "09050a0005774463f10770" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_pixeloperatormono_regular_8_cs.json b/core/embed/rust/src/ui/translations/fonts/font_pixeloperatormono_regular_8_cs.json new file mode 100644 index 000000000..5827ebd6d --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_pixeloperatormono_regular_8_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "0507070007111d18fe20" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0507070007511d1845c0" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0507070007513d18c7c0" + }, + { + "char": "É", + "utf8": "C389", + "data": "0507070007113f0e43e0" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "0507070007513f0e43e0" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "0507070007113e4213e0" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "050707000751239ace20" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "0507070007111d18c5c0" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0507070007513d18fa20" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0507070007511d0707c0" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0507070007513e421080" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "0507070007112318c5c0" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "0507070007228918c5c0" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "05070700071123151080" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0507070007513e2223e0" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "0507070007111c17c5e0" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0507070007511d1845c0" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "0607070007249e88208e00" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0507070007111d1fc1c0" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0507070007511d1fc1c0" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "050707000711384213e0" + }, + { + "char": "ň", + "utf8": "C588", + "data": "0507070007513d18c620" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "0507070007111d18c5c0" + }, + { + "char": "ř", + "utf8": "C599", + "data": "050707000751274c4200" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "0507070007511d0707c0" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "0607070007061ea0820600" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "0507070007112318c5c0" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "0507070007228918c5c0" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "0508070007112318bc2e00" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "0507070007513e2223e0" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_pixeloperatormono_regular_8_fr.json b/core/embed/rust/src/ui/translations/fonts/font_pixeloperatormono_regular_8_fr.json new file mode 100644 index 000000000..ba854091e --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_pixeloperatormono_regular_8_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "0507070007411d18fe20" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0507070007229d18fe20" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "0607070007fc820ee083c0" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "0508070007746108b84c00" + }, + { + "char": "È", + "utf8": "C388", + "data": "0507070007413f0e43e0" + }, + { + "char": "É", + "utf8": "C389", + "data": "0507070007113f0e43e0" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "050707000722bf0e43e0" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "0507070007503f0e43e0" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "050707000722be4213e0" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "0507070007503e4213e0" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "0507070007229d18c5c0" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "0507070007412318c5c0" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "0507070007228118c5c0" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0507070007502318c5c0" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "05070700075023151080" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "0607070007fc820e208fc0" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "0507070007411c17c5e0" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "0507070007229c17c5e0" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "0605070005d89fc8d8" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "0508070007746117104c00" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "0507070007411d1fc1c0" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0507070007111d1fc1c0" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0507070007229d1fc1c0" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0507070007501d1fc1c0" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "050707000722b84213e0" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "050707000750384213e0" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0507070007229d18c5c0" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "0507070007412318c5c0" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "0507070007228118c5c0" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "0507070007502318c5c0" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0508070007502318bc2e00" + }, + { + "char": "œ", + "utf8": "C593", + "data": "0605070005d893c8d8" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_roboto_bold_20_cs.json b/core/embed/rust/src/ui/translations/fonts/font_roboto_bold_20_cs.json new file mode 100644 index 000000000..2053e13e7 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_roboto_bold_20_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "0e120d00120000002efd1000000000cfe20000000003be3000000000000000000000000dff40000000003fffa0000000009ffff100000000efeff600000005ff6ffc0000000bff1aff2000001ffc05ff8000007ff600ffe00000dff100aff40003fffffffffb0009ffffffffff100fff66666bff705ffb000004ffd0cff5000000eff3" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0d120d0012000bfb05ff3000000afcfe300000000afe30000000000000000000028dffc6000004fffffffc0001fffb78dffa009ff80000eff10eff100009ff50ffd0000000002ffd0000000002ffd0000000001ffd0000000000eff000008ff509ff70000eff202fffa77dffb0005fffffffc1000029effc7000" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0c120d01124ff31df9000004ffdf800000004ff8000000000000000000bffffeb50000bfffffffc100bff867dffc00bff3000bff60bff30002ffc0bff30000eff0bff30000dff1bff30000dff1bff30000fff0bff30003ffc0bff3000cff60bff757dffc00bfffffffc100bffffeb50000" + }, + { + "char": "É", + "utf8": "C389", + "data": "0a120b0112000009ff6000006ff70000009d90000000000000bffffffffcbffffffffcbff8666664bff3000000bff3000000bff3000000bfffffffd0bfffffffd0bff7444430bff3000000bff3000000bff7555554bffffffffcbffffffffc" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "0a120b011204ff40cfa0004ffdf9000003ff90000000000000bffffffffcbffffffffcbff8666664bff3000000bff3000000bff3000000bfffffffd0bfffffffd0bff7444430bff3000000bff3000000bff7555554bffffffffcbffffffffc" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "061206011200dfe20aff302be4000000008ff6008ff6008ff6008ff6008ff6008ff6008ff6008ff6008ff6008ff6008ff6008ff6008ff6008ff600" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "0c120e0112005ff31df8000005fedf800000005ff70000000000000000bff800001ffdbfff20001ffdbfffb0001ffdbffff5001ffdbffffe101ffdbff8ff901ffdbff3bff31ffdbff32ffd1ffdbff307ff8ffdbff300dffffdbff3004ffffdbff3000afffdbff30001effdbff300006ffd" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "0d120e00120000001dfe200000000bff3000000002be40000000000000000000017dfec6000003effffffc1000effc78dffb008ffa0000dff40dff200005ffa0fff000001ffd2ffd000000ffe2ffd000000ffe0fff000002ffd0dff200006ffa08ffa0000dff501effc77dffc0003effffffc1000017dfec6000" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0c120d011209fd14ff4000009fdff400000009ff400000000000000000bfffffec6000bffffffffb00bff8669fff60bff30006ffb0bff30002ffc0bff30004ffb0bff3003dff60bffffffffb00bfffffff9000bff868ffc000bff300cff500bff3003ffe00bff3000aff80bff30001fff2" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0c120c0012002ef60bfb000002efcfb00000002efb00000000000000000006cefea30001dfffffff6009ffd659fff20dff20008ff70cff6000133105fffc610000007fffffb30000029effff600000004bfff2277400009ff72ffd00007ff80cffc657fff501dfffffffa00006befeb500" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0c120c0012005ff31df9000005ffdf800000004ff800000000000000009fffffffffff9fffffffffff36669ffc666500005ffa000000005ffa000000005ffa000000005ffa000000005ffa000000005ffa000000005ffa000000005ffa000000005ffa000000005ffa000000005ffa0000" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "0c120d0112000004ffb00000001efc000000006cd10000000000000000dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000fff0aff60004ffd04fff968eff7008fffffffa00004befeb5000" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "0c130d01130000bec100000008a09900000008a09a00000001bec10000000000000000dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000fff0aff60004ffd04fff968eff7008fffffffa00004befeb5000" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "0d120c00120000009ff6000000006ff70000000009d9000000000000000000bff700002fff13ffe00009ff800aff6001fff1002ffd008ff80000aff50efe000002ffc7ff70000009ffffe00000001ffff6000000008ffd0000000005ffa0000000005ffa0000000005ffa0000000005ffa0000000005ffa00000" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0c120c0012004ff31df9000004ffdf800000004ff800000000000000004ffffffffff64ffffffffff51666666affc00000001eff20000000bff600000007ffb00000003ffe10000000dff400000009ff900000004ffd00000001eff30000000bffb55555534ffffffffff84ffffffffff8" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "0b0f0b000f000007ff90000003ffa00000008db000000000000000004befd810007ffffffd000ffe329ff600000003ff90006ceffffa009ffebbffa02ffc003ffa04ff8003ffa03ffd34dffa00cfffffffb0008efc3efe00" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0a0f0a000f04ff40cfa0004ffdf9000003ff900000000000000019efe91002efffffe10bff95aff91ffc000efd4ff80001225ff70000004ff80000001ffc000bdb0bff959ff902efffffe10019efe910" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "0e0f0d000f0000000aff25fb0000000aff26fa0000000aff2bf40000000aff28b0004cfe8aff200004ffffffff20000dffa58fff20002ffd000bff20004ff9000aff20005ff7000aff20004ff8000aff20001ffd000bff20000cffa58fff200003ffffffff2000003cfe97ff2000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0b0f0b000f000008ff80000004ff900000008da0000000000000000007dfea20000cffffff3009ff957ffd00ffc0009ff23fffffffff44fffeeeeee43ffb00000000fff20007100affe65afb001cffffff800007dffb4000" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0b0f0b000f03ef60bfb00002efcfb0000002efb0000000000000000007dfea20000cffffff3009ff957ffd00ffc0009ff23fffffffff44fffeeeeee43ffb00000000fff20007100affe65afb001cffffff800007dffb4000" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "060f05010f02ffd10dfe104be200000000bff200bff200bff200bff200bff200bff200bff200bff200bff200bff200bff200" + }, + { + "char": "ň", + "utf8": "C588", + "data": "0a0f0b010f0cfa07fe2000cfcfe200000bfe20000000000000ffb5cfe900ffffffff80fffa57ffe0ffe000cff1ffd000aff2ffd000aff2ffd000aff2ffd000aff2ffd000aff2ffd000aff2ffd000aff2" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "0b0f0b000f000003ffc0000001efd10000005cd1000000000000000018dfea30001dffffff400affb58ffe01ffd0009ff64ff90004ff95ff70003ffa4ff90004ff91ffd0009ff60bffb58ffe001dffffff400018dfea3000" + }, + { + "char": "ř", + "utf8": "C599", + "data": "080f07000f6fe22ef706feef60006ff600000000000efc5ef00efefff00efffdc00eff30000efe00000efe00000efe00000efe00000efe00000efe00000efe0000" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "0a0f0a000f04ff41df90004ffdf9000004ff80000000000000004befc70007ffffffc00ffd21aff51ffb0026630cffd9500001bffffe6000027bfff33992005ff83ffb108ff609ffffffd0005cffd710" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "0911070011000000cf4000000df2000002fd005ff72c4005ff7000005ff70000efffff400efffff40016ff8100005ff7000005ff7000005ff7000005ff7000005ff7000004ffc520001efff600003cfe4000" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "0a0f0b010f00003ffc000001efd0000005cd10000000000000ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2efe000bff2cff948fff25ffffffff206dfd78ff2" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "0a100b0110000aec1000007b08a000007b08a000000bec20000000000000ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2efe000bff2cff948fff25ffffffff206dfd78ff2" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "0b130a000f00000bff40000008ff50000000ae7000000000000000cff3002ffd07ff8007ff701ffc00cff200cff11ffc0006ff65ff70001ffbaff20000bffefc000006ffff6000001ffff1000000bffb00000005ff600000009ff10000047ffb000000dfff2000000bfc40000000" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "0a0f0a000f05ff31df80005fedf8000005ff700000000000002ffffffff52ffffffff404444bffb000003ffe100000dff5000009ff9000004ffd000001eff300000bffb444424ffffffff84ffffffff8" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_roboto_bold_20_fr.json b/core/embed/rust/src/ui/translations/fonts/font_roboto_bold_20_fr.json new file mode 100644 index 000000000..367c001f5 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_roboto_bold_20_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "0e120d00120006ff8000000000008ff3000000000009fd0000000000000000000000000dff40000000003fffa0000000009ffff100000000efeff600000005ff6ffc0000000bff1aff2000001ffc05ff8000007ff600ffe00000dff100aff40003fffffffffb0009ffffffffff100fff66666bff705ffb000004ffd0cff5000000eff3" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0e120d001200000afe4000000001cfcff600000008db04eb20000000000000000000000dff40000000003fffa0000000009ffff100000000efeff600000005ff6ffc0000000bff1aff2000001ffc05ff8000007ff600ffe00000dff100aff40003fffffffffb0009ffffffffff100fff66666bff705ffb000004ffd0cff5000000eff3" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "130e13000e00000009fffffffffa00000002ffffffffffa0000000affffd5555530000003ffaffd000000000000cff2ffd000000000005ff90efe00000000000dff10dffffffd000006ff700cffffffd00000effffffff5555400008fffffffff100000001ffe6666cff100000009ff70000aff64444412fff100009ffffffff3bff8000009ffffffff3" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "0d130d000e00028dffc6000004fffffffc0001fffb78dffa009ff80000eff10eff100009ff50ffd0000000002ffd0000000002ffd0000000001ffd0000000000eff000008ff509ff70000eff202fffa77dffb0005fffffffc1000029effc7000000006fd10000000005efd00000000004ff1000000003dfd0000000004ea200000" + }, + { + "char": "È", + "utf8": "C388", + "data": "0a120b011201dfe10000002efa00000003ff40000000000000bffffffffcbffffffffcbff8666664bff3000000bff3000000bff3000000bfffffffd0bfffffffd0bff7444430bff3000000bff3000000bff7555554bffffffffcbffffffffc" + }, + { + "char": "É", + "utf8": "C389", + "data": "0a120b0112000009ff6000006ff70000009d90000000000000bffffffffcbffffffffcbff8666664bff3000000bff3000000bff3000000bfffffffd0bfffffffd0bff7444430bff3000000bff3000000bff7555554bffffffffcbffffffffc" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "0a120b01120004ff9000007ffcfc1003be40bd700000000000bffffffffcbffffffffcbff8666664bff3000000bff3000000bff3000000bfffffffd0bfffffffd0bff7444430bff3000000bff3000000bff7555554bffffffffcbffffffffc" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "0a120b0112006400162007ff10bfd003ec007f900000000000bffffffffcbffffffffcbff8666664bff3000000bff3000000bff3000000bfffffffd0bfffffffd0bff7444430bff3000000bff3000000bff7555554bffffffffcbffffffffc" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "0812070012008ff5000bfdef806cc12ec400000000008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "081207001216200360bfc00ff87f800bf500000000008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600008ff600" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "0d120e0012000008ff500000000bfdef80000006cc12ec400000000000000000017dfec6000003effffffc1000effc78dffb008ffa0000dff40dff200005ffa0fff000001ffd2ffd000000ffe2ffd000000ffe0fff000002ffd0dff200006ffa08ffa0000dff501effc77dffc0003effffffc1000017dfec6000" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "0c120d0112009ff5000000000bfe1000000000cfa00000000000000000dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000fff0aff60004ffd04fff968eff7008fffffffa00004befeb5000" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "0c120d01120001cfd20000003efcfe4000009e807ea100000000000000dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000fff0aff60004ffd04fff968eff7008fffffffa00004befeb5000" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0c120d011200460005400001ff605ff20000ce302ed100000000000000dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000eff0dff10000fff0aff60004ffd04fff968eff7008fffffffa00004befeb5000" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "0d120c00120006400162000007ff10bfd000003ec007f90000000000000000bff700002fff13ffe00009ff800aff6001fff1002ffd008ff80000aff50efe000002ffc7ff70000009ffffe00000001ffff6000000008ffd0000000005ffa0000000005ffa0000000005ffa0000000005ffa0000000005ffa00000" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "130e13000e00028dffffffffffff4005fffffffffffffff403fff9558ffd66666610aff50003ffb00000000efe00003ffb00000000ffc00003ffb00000001ffc00003ffffffff501ffc00003ffffffff500ffc00003ffd44444100efe00003ffb00000000aff50003ffb000000002fff9547ffd5555551005fffffffffffffff500029dffffffffffff5" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "0b0f0b000f00bff200000000dfc000000001ef7000000000000000004befd810007ffffffd000ffe329ff600000003ff90006ceffffa009ffebbffa02ffc003ffa04ff8003ffa03ffd34dffa00cfffffffb0008efc3efe00" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "0b0f0b000f0002dfb1000005ffcfd20001ae609d90000000000000004befd810007ffffffd000ffe329ff600000003ff90006ceffffa009ffebbffa02ffc003ffa04ff8003ffa03ffd34dffa00cfffffffb0008efc3efe00" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "110c11000c00000000000000000004aefea26dffc50007fffffffffffff600eff749fffc56eff00011000fff1007ff4006cefffffffffff50bfffddfffeeeeee53ffd100eff00000005ff8000eff50000103ffd229ffff746ca00cffffffcffffffe0008dfeb305cffd920" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "0a100a000b0019efe91002efffffe10bff95aff91ffc000efd4ff80001225ff70000004ff80000001ffc000bdb0bff959ff902efffffe10019efe9100000bf900000009ff900000009fd0000006ff90000008e8000" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "0b0f0b000f00cff200000001dfb000000002ef60000000000000000007dfea20000cffffff3009ff957ffd00ffc0009ff23fffffffff44fffeeeeee43ffb00000000fff20007100affe65afb001cffffff800007dffb4000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0b0f0b000f000008ff80000004ff900000008da0000000000000000007dfea20000cffffff3009ff957ffd00ffc0009ff23fffffffff44fffeeeeee43ffb00000000fff20007100affe65afb001cffffff800007dffb4000" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0b0f0b000f0003efb0000005ffcfd20002be50ad800000000000000007dfea20000cffffff3009ff957ffd00ffc0009ff23fffffffff44fffeeeeee43ffb00000000fff20007100affe65afb001cffffff800007dffb4000" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0b0f0b000f0054000630005ff309ff0002ed105fb00000000000000007dfea20000cffffff3009ff957ffd00ffc0009ff23fffffffff44fffeeeeee43ffb00000000fff20007100affe65afb001cffffff800007dffb4000" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "080f06000f00afe4001cfcff608db04eb20000000000bff20000bff20000bff20000bff20000bff20000bff20000bff20000bff20000bff20000bff20000bff200" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "080f06000f26100450efa02ff6af600de30000000000bff20000bff20000bff20000bff20000bff20000bff20000bff20000bff20000bff20000bff20000bff200" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0b0f0b000f0001bfe2000002dfcff500009d906eb10000000000000018dfea30001dffffff400affb58ffe01ffd0009ff64ff90004ff95ff70003ffa4ff90004ff91ffd0009ff60bffb58ffe001dffffff400018dfea3000" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "0a0f0b010f09ff50000000afe10000000bfa00000000000000ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2efe000bff2cff948fff25ffffffff206dfd78ff2" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "0a0f0b010f001cfd200003efcff40009d906ea100000000000ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2efe000bff2cff948fff25ffffffff206dfd78ff2" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "0a0f0b010f03600055001ff704ff300ce402ed100000000000ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2ffd000bff2efe000bff2cff948fff25ffffffff206dfd78ff2" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0b130a000f0063002610008ff00cfb0005fb008f70000000000000cff3002ffd07ff8007ff701ffc00cff200cff11ffc0006ff65ff70001ffbaff20000bffefc000006ffff6000001ffff1000000bffb00000005ff600000009ff10000047ffb000000dfff2000000bfc40000000" + }, + { + "char": "œ", + "utf8": "C593", + "data": "120d12000c0000000000000000000007dfeb406dffb40000cffffffcffffff5008ffc57ffffc56ffe00eff1007fff1008ff31ffb0002fffffffff42ffa0001fffeeeeee41ffb0002fff00000000eff0007fff600000008ffc57fffff746a7000cffffffcffffffe00008dffb405cffd920000000000000000000" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_roboto_regular_20_cs.json b/core/embed/rust/src/ui/translations/fonts/font_roboto_regular_20_cs.json new file mode 100644 index 000000000..5a61805b5 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_roboto_regular_20_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "0d120d00120000000df3000000000af50000000002d7000000000000000000000007f80000000000dfe0000000004fdf500000000af2fb00000001fb0af10000006f504f7000000df000ed000003fa0009f400009f40003fa0000fffffffff1005fc88888bf600cf2000002fc02fc0000000bf38f600000005f9" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0c120d0112002e805f40000003f8f7000000005f9000000000000000000007dffc600002dfc9aefb000cf60000bf605fa000002fd0af4000000ac0cf1000000000df0000000000df0000000000cf2000000000af40000009b05fb000001fd00df50000af6002efc8aefa000018dffc6000" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0b120d011200bd01e9000000dbcb00000002ed10000000000000005ffffeb50005fc88aefb105f800008fb05f800000af45f8000003fa5f8000000fd5f8000000ee5f8000000ee5f8000000fd5f8000003fa5f800000af45f800008fb05fc88aefb105ffffeb5000" + }, + { + "char": "É", + "utf8": "C389", + "data": "0a120b0112000007fa0000003fc00000008d100000000000005ffffffff95fc88888855f800000005f800000005f800000005f800000005fffffffb05fc88888505f800000005f800000005f800000005f800000005fc88888855ffffffffb" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "0a120b011200ae10db00000ccbd0000001de100000000000005ffffffff95fc88888855f800000005f800000005f800000005f800000005fffffffb05fc88888505f800000005f800000005f800000005f800000005fc88888855ffffffffb" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "051205011200cf508f701c900000003fa003fa003fa003fa003fa003fa003fa003fa003fa003fa003fa003fa003fa003fa00" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "0c120e01120009e10dc0000000bcad100000000de200000000000000005fd0000004f95ff8000004f95fff300004f95fcfd00004f95f89f80004f95f81ef3004f95f804fd004f95f800af804f95f8001ef24f95f80004fc4f95f80000afcf95f800001eff95f8000004ff95f8000000af9" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "0c120e01120000009f8000000005fa000000000ab000000000000000000007dfec500001dfdaaefa000bf70000af804fc000000ef09f40000008f5bf20000005f7df00000004f9df00000004f9bf10000005f79f40000008f54fb000000ef00bf70000af8001dfdaaefb000007dfec6000" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0b120c011200bd01e9000000dbcb00000002ed10000000000000005fffffeb3005fc889bff605f800003ff05f800000bf45f8000009f45f800000df25f80002bfa05fffffffa005fc888fd0005f80009f6005f80001fe105f800007f905f800000df25f8000005fb" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0c120c00120009e10db0000000bcad100000001de200000000000000000007dffc700000cfd99dfd1008f900008f900bf200000ff00af60000045003ff92000000003dffd8200000004aeff90000000004df90053000001ff01fd000000df10bf700003fe001dfd99bff400007cffd9200" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0c120c0012000bd01ea0000000dbcc000000001ed100000000000000008ffffffffff748888ff8888400000fe0000000000fe0000000000fe0000000000fe0000000000fe0000000000fe0000000000fe0000000000fe0000000000fe0000000000fe0000000000fe0000000000fe00000" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "0b120d0112000001ef30000000bf400000003d6000000000000000af3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003fa9f4000004fa7f6000006f82fe20001ef207ffa9aff70004befeb300" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "0b130d01130000beb000000079087000000790880000000beb100000000000000af3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003fa9f4000004fa7f6000006f82fe20001ef207ffa9aff70004befeb3000" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "0c120c00120000007fa000000003fc0000000008c100000000000000009f80000009f81ff100001fe107f900009f6000df2002fd00005fa00af400000cf23fc0000003fbbf30000000affa000000002ff1000000000fe0000000000fe0000000000fe0000000000fe0000000000fe00000" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0c120c0012000ad11db0000000cbbc000000001ee100000000000000000fffffffffe008888888cfb000000001ef200000000bf6000000007fa000000002fe100000000df4000000009f8000000004fd000000001ef200000000af7000000006fb000000001ffa888888812ffffffffff3" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "09100b011000000cf5000008f7000001c9000000000000000000000029efd8002ff97bfb08e30009f30000006f6029dffff63fe8548f6bf20006f6ee00006f6cf3002ef65ffaafef705cfe84fa" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0a100a0010007f20bd00000ad9e1000000cf3000000000000000000000000008dfe91000cfc8bfd107f80006f80df00000ca0fc00000001fb00000000fc00000000df000008607f80005f800cfb8afd00008dfe800" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "0d0f0d000f00000000fd0ce00000000fd0ed00000000fd2f800000000fd3c1001aefb3fd00000dfc8bffd00008f90006fd0000df10000fd0000fd00000fd0001fc00000fd0000fd00000fd0000df10000fd00007f80006fd00000dfc8bffd000001aefc3dd0000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0a100b0010000001ef3000000bf40000002d6000000000000000000000000006dfe91000afb8bfd006f70007f70ce00000fb0ffffffffd1fd77777760fd00000000df200000007fb0000a300cfd88ef40007dfeb30" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0a100b0010003f707f300005f8f50000007f7000000000000000000000000006dfe91000afb8bfd006f70007f70ce00000fb0ffffffffd1fd77777760fd00000000df200000007fb0000a300cfd88ef40007dfeb30" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "051005011001ef20cf403d50000000000007f5007f5007f5007f5007f5007f5007f5007f5007f5007f5007f500" + }, + { + "char": "ň", + "utf8": "C588", + "data": "09100b011002f806f40004f8f6000006f8000000000000000000000af28efc40afdd9aff1afb0007f7af30002f9af20002faaf20002faaf20002faaf20002faaf20002faaf20002faaf20002fa" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "0b100b0010000000df400000009f600000001c8000000000000000000000000000007dfea20000bfc8afe3006f90003fd00cf10000af30fd000006f61fb000005f70fd000006f60df10000af306f90003fd000bfc79fe300007dfea200" + }, + { + "char": "ř", + "utf8": "C599", + "data": "07100700100bd11ea00cbcc0001ed10000000000000000af4bf60afeec50afb0000af30000af20000af20000af20000af20000af20000af20000af2000" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "0a100a0010003f607f300005f8f50000008f700000000000000000000000002aefc60002ff98df9009f4000df10af300024104ff940000003bfffa10000004afd00750000af40ef2000af305fe98bfc0003befd700" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "0810070010000006f4000008f300fd0ce000fd0b5000fd0000effffc0056fe640000fd000000fd000000fd000000fd000000fd000000fd000000ed000000bfa800002cfb00" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "09100b011000001df300000bf5000002d6000000000000000000000af20002faaf20002faaf20002faaf20002faaf20002faaf20002faaf20002fa9f30002fa7f70009fa1ffa9cffa03cffb3fa" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "09100b0110000beb100007908800007908800000beb100000000000af20002faaf20002faaf20002faaf20002faaf20002faaf20002faaf20002fa9f30002fa7f70009fa1ffa9cffa03cffb3fa" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "0a14090010000008f90000004fb00000009c0000000000000000000000009f50000df13fa0002fb00ef0007f5008f500cf0002fa01fa0000df06f500007f5bf000001fbfa000000bff40000006fe00000001f900000005f40000000dd0000019df5000002fe6000000" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "0a100a001000ad11db00000cbbc0000001ee1000000000000000000000000ffffffff0088888bfc0000001ef2000000bf50000007fa0000003fe0000000df30000009f80000004fc0000000ef97777712ffffffff4" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_roboto_regular_20_fr.json b/core/embed/rust/src/ui/translations/fonts/font_roboto_regular_20_fr.json new file mode 100644 index 000000000..9d033ec70 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_roboto_regular_20_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "0d120d00120002ef200000000003fc000000000005d4000000000000000000000007f80000000000dfe0000000004fdf500000000af2fb00000001fb0af10000006f504f7000000df000ed000003fa0009f400009f40003fa0000fffffffff1005fc88888bf600cf2000002fc02fc0000000bf38f600000005f9" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0d120d0012000007f90000000007f8f900000002c806d30000000000000000000007f80000000000dfe0000000004fdf500000000af2fb00000001fb0af10000006f504f7000000df000ed000003fa0009f400009f40003fa0000fffffffff1005fc88888bf600cf2000002fc02fc0000000bf38f600000005f9" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "140e14000e000000000dffffffffc0000000007fff7777775000000001fdcf000000000000000bf4bf100000000000004fa0af10000000000000ef20af20000000000008f8009fffffff0000002fe0008f977777000000cf50008f400000000006ffffffff50000000001efbbbbbdf60000000009f9000006f6000000003fe1000005fb77777720df60000004ffffffff5" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "0c120d010e0007dffc600002dfc9aefb000cf60000bf605fa000002fd0af4000000ac0cf1000000000df0000000000df0000000000cf2000000000af40000009b05fb000001fd00df50000af6002efc8aefa000018dffc600000001f800000000016e90000000002d9000000004ea10000" + }, + { + "char": "È", + "utf8": "C388", + "data": "0a120b0112009f900000000bf500000000ca000000000000005ffffffff95fc88888855f800000005f800000005f800000005f800000005fffffffb05fc88888505f800000005f800000005f800000005f800000005fc88888855ffffffffb" + }, + { + "char": "É", + "utf8": "C389", + "data": "0a120b0112000007fa0000003fc00000008d100000000000005ffffffff95fc88888855f800000005f800000005f800000005f800000005fffffffb05fc88888505f800000005f800000005f800000005f800000005fc88888855ffffffffb" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "0a120b01120001ee2000001ecbe200008d10c90000000000005ffffffff95fc88888855f800000005f800000005f800000005f800000005fffffffb05fc88888505f800000005f800000005f800000005f800000005fc88888855ffffffffb" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "0a120b0112000000000000ed00bf1000dc00ae1000000000005ffffffff95fc88888855f800000005f800000005f800000005f800000005fffffffb05fc88888505f800000005f800000005f800000005f800000005fc88888855ffffffffb" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "0712060012005fb0005f9eb00ba03d50000000003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "071206001200000004f801fb3f701eb0000000003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00003fa00" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "0c120e011200003fd100000003fadd1000000ac02d70000000000000000007dfec500001dfdaaefa000bf70000af804fc000000ef09f40000008f5bf20000005f7df00000004f9df00000004f9bf10000005f79f40000008f54fb000000ef00bf70000af8001dfdaaefb000007dfec6000" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "0b120d0112002fe1000000004fc0000000006d3000000000000000af3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003fa9f4000004fa7f6000006f82fe20001ef207ffa9aff70004befeb300" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "0b120d011200008f800000008f8f9000002d706d30000000000000af3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003fa9f4000004fa7f6000006f82fe20001ef207ffa9aff70004befeb300" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0b120d011200000000000007f504f800006f403f80000000000000af3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003faaf3000003fa9f4000004fa7f6000006f82fe20001ef207ffa9aff70004befeb300" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "0c120c0012000000000000000ed00be100000dc00be1000000000000009f80000009f81ff100001fe107f900009f6000df2002fd00005fa00af400000cf23fc0000003fbbf30000000affa000000002ff1000000000fe0000000000fe0000000000fe0000000000fe0000000000fe00000" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "110e13010e003aefffffffffff706fe978cfb88888842fd10007f700000009f400007f70000000df000007f70000000fd000007f70000000fd000007fffffff90fd000007fb8888850fd000007f70000000df000007f700000009f400007f700000002fd10007f7000000006fe978cfb8888885003aefffffffffff9" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "09100b011001df30000002fe10000004d5000000000000000000000029efd8002ff97bfb08e30009f30000006f6029dffff63fe8548f6bf20006f6ee00006f6cf3002ef65ffaafef705cfe84fa" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "09100b01100006fb000006f8eb0001c904d40000000000000000000029efd8002ff97bfb08e30009f30000006f6029dffff63fe8548f6bf20006f6ee00006f6cf3002ef65ffaafef705cfe84fa" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "110b11000b005befd603bffc40008fe98dfafd89ef500cd1000efc0001ee00000000bf40000af2007cffffffffffff309fd766cf766666611fe0000bf400000003fa0000bf800000001fc0003eff20001300bfc8aff7ff98afd0009efd9203befe9200" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "0a0f0a000b0008dfe91000cfc8bfd107f80006f80df00000ca0fc00000001fb00000000fc00000000df000008607f80005f800cfb8afd00008dfe80000008e3000000039f200000006f2000000bd6000" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "0a100b0010002fe100000004fc000000006d3000000000000000000000000006dfe91000afb8bfd006f70007f70ce00000fb0ffffffffd1fd77777760fd00000000df200000007fb0000a300cfd88ef40007dfeb30" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0a100b0010000001ef3000000bf40000002d6000000000000000000000000006dfe91000afb8bfd006f70007f70ce00000fb0ffffffffd1fd77777760fd00000000df200000007fb0000a300cfd88ef40007dfeb30" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0a100b001000008f80000008f8f900002d706d30000000000000000000000006dfe91000afb8bfd006f70007f70ce00000fb0ffffffffd1fd77777760fd00000000df200000007fb0000a300cfd88ef40007dfeb30" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0a100b00100000000000007f504f80006f403f80000000000000000000000006dfe91000afb8bfd006f70007f70ce00000fb0ffffffffd1fd77777760fd00000000df200000007fb0000a300cfd88ef40007dfeb30" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "0710060010009f80009f8f803d607d200000000000000007f500007f500007f500007f500007f500007f500007f500007f500007f500007f500007f500" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "071006001000000007f404f77f404f700000000000000007f500007f500007f500007f500007f500007f500007f500007f500007f500007f500007f500" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0b100b001000007fa00000007f8fa000001c805d40000000000000000000000000007dfea20000bfc8afe3006f90003fd00cf10000af30fd000006f61fb000005f70fd000006f60df10000af306f90003fd000bfc79fe300007dfea200" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "09100b011002ee10000004fc00000006d3000000000000000000000af20002faaf20002faaf20002faaf20002faaf20002faaf20002faaf20002fa9f30002fa7f70009fa1ffa9cffa03cffb3fa" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "09100b01100008f9000008f8f90002c706d30000000000000000000af20002faaf20002faaf20002faaf20002faaf20002faaf20002faaf20002fa9f30002fa7f70009fa1ffa9cffa03cffb3fa" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "09100b011000000000006f503f8006f503f80000000000000000000af20002faaf20002faaf20002faaf20002faaf20002faaf20002faaf20002fa9f30002fa7f70009fa1ffa9cffa03cffb3fa" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0a14090010000000000001ec00ce0001eb00ce00000000000000000000009f50000df13fa0002fb00ef0007f5008f500cf0002fa01fa0000df06f500007f5bf000001fbfa000000bff40000006fe00000001f900000005f40000000dd0000019df5000002fe6000000" + }, + { + "char": "œ", + "utf8": "C593", + "data": "120b12000b0007dfe91019efd70000bfc8afe3dfa8cf9006fa0004fff4000bf20cf10000cfb00005f60fd000008ffffffff70fc000007fc77777730fd000008fa00000000cf10000cfe000000006f90004fff700004000bfc79fe3efa79ef00007dfea1019efea30" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_robotomono_medium_20_cs.json b/core/embed/rust/src/ui/translations/fonts/font_robotomono_medium_20_cs.json new file mode 100644 index 000000000..b9df34dc5 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_robotomono_medium_20_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "0c120c0012000000177200000000afa000000005fa000000000571000000001ff6000000006ffc00000000cfff10000001ffcf60000006fb7fb000000bf72ff000001ff20df500006fd009fa0000bfb448ff0001ffffffff4006ffbbbbefa00bfa00006fe00ff600002ff45ff100000df9" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0c130c00120007d705d90000009fbfb000000007d90000000026640000001bffffe30000cff98dff2006ff2000dfa00cf900006fe00ff6000014401ff4000000001ff4000000001ff4000000001ff4000000000ff6000026600bfa00007fe004ff4002ef8000affccffd100008ffffa100000002200000" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0b120c011209d507d700000bfbf900000009d70000023332000000afffffa2000afebcfff400afb001bff20afb0000ef90afb00008fe0afb00005ff0afb00004ff1afb00004ff1afb00005ff0afb00009fd0afb0001ff80afb003dfe00affefffe200affffd71000" + }, + { + "char": "É", + "utf8": "C389", + "data": "0a120c0112000001772000000afa0000005fa00013336733326ffffffffc6ffbbbbbb86ff00000006ff00000006ff00000006ff44444306fffffffd06ffaaaaa806ff00000006ff00000006ff00000006ff00000006ffffffffb6ffffffffc" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "0a120c011201bc20ac40002decf6000001bc400013333333326ffffffffc6ffbbbbbb86ff00000006ff00000006ff00000006ff44444306fffffffd06ffaaaaa806ff00000006ff00000006ff00000006ff00000006ffffffffb6ffffffffc" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "0a120c0112000004760000002ff4000000cf400003347533303ffffffff32bbcffcbb20003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20002ffffffff23ffffffff3" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "0a120c011203ca02cc10005fcee3000004cc20002320000232bff0000bfabff7000bfabffe000bfabfff600bfabfdfd00bfabfabf50cfabfa4fd0cfabfa0cf4cfabfa05fccfabfa00dfefabfa006fffabfa000effabfa0006ffabfa0000efa" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "0c140c0013000000177200000000cf9000000007f90000000003300000000036620000001bffffb10000cffaaffc0006ff2003ff500cf90000afb00ff500006ff01ff400004ff11ff400004ff11ff400004ff11ff400004ff00ff600007ff00bfb0000bfa004ff5006ff4000affddff9000008fffe7000000002200000" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0b120c011203ca02cc100005fcee30000004cc20000133333000008ffffffc3008ffbbbfff308fd0001dfc08fd00006ff08fd00005ff08fd0000bfc08fe777cff408ffffffd4008fe77dfb0008fd004ff3008fd000cfb008fd0004ff408fd0000cfc08fd00004ff4" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0c140c0013001bc309d5000002dfcf600000001bd50000000000000000000025640000002cffffe60000efe98cff6006ff2000afe009fc00003ee206ff3000000000eff9300000002cfffe700000004afffd1000000006ffb0044100005ff10ef800002ff309ff30009ff001dffdbeff700008efffd500000002210000" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0c120c0012002cb11bc3000003eedf400000002cc300001333333333318ffffffffff86bbbcffcbbb600002ff2000000002ff2000000002ff2000000002ff2000000002ff2000000002ff2000000002ff2000000002ff2000000002ff2000000002ff2000000002ff2000000002ff20000" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "0a130c01120000007730000009fb0000004fb0002320230232bf900009fbbf900009fbbf900009fbbfa00009fbbfa00009fbbfa0000afbbfa0000afbbfa0000afbbfa0000afbbfa0000afb9fc0000df94ff5005ff40bffcdffb0009ffff8000000220000" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "0a150c011400002300000006ebc000000c40d3000009b7e10000006720002320000232bf900009fbbf900009fbbf900009fbbfa00009fbbfa00009fbbfa0000afbbfa0000afbbfa0000afbbfa0000afbbfa0000afb9fc0000df94ff5005ff40bffcdffb0009ffff8000000220000" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "0c120c0012000000277100000000cf8000000007f800001330033003323ff500004ff40bfc0000bfc003ff3002ff4000cfa009fc00004ff21ff500000cf87fd0000004ffef50000000cffd000000005ff6000000002ff2000000002ff2000000002ff2000000002ff2000000002ff20000" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0b120c0012000bc309d500001dfcf70000001bd5000033333333320fffffffffb0bbbbbbcff900000009fe10000003ff50000000dfa00000008fe10000003ff50000000cfb00000007ff10000002ff60000000cfc00000006ff20000000fffffffffe0ffffffffff" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "0a100c010f000002771000000df70000009f700000003200000049bb820009ffffff504ff612bff03760002ff30004677ff407fffffff45ff6101ff4afa0001ff4afc0008ff44ffdbefff506effe5ef80002200000" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0a100c010f01bc20ad40002dfcf6000001bc400000000000000018bb930003ffffff601efc329ff27ff0000df7afa0000231bf90000000bf900000008fd00005843ff5002ef509ffcbffc0007ffff9000000220000" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "0e110c00100000000000003300000007fe00ff00000007fe00ff00000007fe02fc00000007fe07f60018bb57fe09c001effffefe00000bff526ffe00001ff60008fe00004ff10007fe00005ff00007fe00005ff00007fe00002ff30007fe00000dfb001cfe000005ffeceffe0000006fffc7fe000000002200000000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0a100c010f000002771000000df70000009f700000003200000007bb930003efffff700efc328ff36ff1000bf9bfc44449fdcffffffffdcfc77777769fd00000003ff80003b108ffebcff6006dfffd500000131000" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0a100c010f03cb11bc20004fdee3000003cc200000000000000007bb930003efffff700efc328ff36ff1000bf9bfc44449fdcffffffffdcfc77777769fd00000003ff80003b108ffebcff6006dfffd500000131000" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "0a0f0c010f0000005750000005fe2000001ee200000013100009999950000fffffa0000666dfa0000000bfa0000000bfa0000000bfa0000000bfa0000000bfa0000000bfa0000ffffffffc0ffffffffd" + }, + { + "char": "ň", + "utf8": "C588", + "data": "0a0f0c010f03cb11cc20004fdee3000003cc2000000000000049507bb8007fccffffc07ffb338ff57fe0000df97fd0000bfa7fd0000bfa7fd0000bfa7fd0000bfa7fd0000bfa7fd0000bfa7fd0000bfa" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "0a100c010f000002771000000cf80000007f800000003300000028bb820005ffffff502ffb33bff29fd0000df9df800008fcef600007feef700007fdbfb0000bfb5ff4004ff50bffccffb0009ffff9000000220000" + }, + { + "char": "ř", + "utf8": "C599", + "data": "090f0c020f6d804ca0008fbfc100006da0000000000000099118bb60ff7efff90ffffa9a50ffd100000ff5000000ff5000000ff5000000ff5000000ff5000000ff5000000ff5000000" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "0a100c010f03cb11cc20004fdee3000003cc200000000000000028bba40006ffffffb01ff8105ff63ff10007950efc62000003dffffa2000038cfff32530001dfa7fe1000bfa1dfe99cff301affffc400000221000" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "0b130c011200000000131000000007f9000000007f8000000008f600022000de1005ff001d60005ff00000089bff999800effffffff00237ff333200005ff000000005ff000000005ff000000005ff000000005ff000000003ff500000000effcce300002dfffe40000002310000" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "0a100c010f000002771000000cf80000007f8000000033000039800007956fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf95ff0000cf93ff4003ff90dffccfef902cfff69f90002300000" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "0a110c011000002300000008dca000000f10f000000b98d000000077100039800007956fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf95ff0000cf93ff4003ff90dffccfef902cfff69f90002300000" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "0c140c000f0000000773000000009fb000000004fb00000000023000002992000008950ef900004ff308ff0000afc001ff6001ff50009fc007fe00002ff30df700000bf94ff1000004ffaf90000000dfff200000006ffb000000000ff4000000006fd000000001ef60000001befd00000004ffc2000000003300000000" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "0a0f0c010f00ad407d70001cfbf9000000ad7000000000000049999999958ffffffff9355555eff2000009ff4000006ff6000004ff8000003ffa000001efc000000cfe1000009fffeeeeecaffffffffe" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_robotomono_medium_20_fr.json b/core/embed/rust/src/ui/translations/fonts/font_robotomono_medium_20_fr.json new file mode 100644 index 000000000..8bcab496e --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_robotomono_medium_20_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "0c120c00120006730000000005fe10000000006fa0000000000671000000001ff6000000006ffc00000000cfff10000001ffcf60000006fb7fb000000bf72ff000001ff20df500006fd009fa0000bfb448ff0001ffffffff4006ffbbbbefa00bfa00006fe00ff600002ff45ff100000df9" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0c120c001200001bc400000002decf5000000bc209c40000000330000000001ff6000000006ffc00000000cfff10000001ffcf60000006fb7fb000000bf72ff000001ff20df500006fd009fa0000bfb448ff0001ffffffff4006ffbbbbefa00bfa00006fe00ff600002ff45ff100000df9" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "0c0f0c000f00000233333100000dfffff500003fffdbb300008fff90000000efcf90000004fcaf90000009f7afb440000ef2affff0004fd0afd99000afb6cf900000ffffff900005feaaef90000bf900af90001ff400affee86ff000affff9" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "0c140c000f000026640000001bffffe30000cff98dff2006ff2000dfa00cf900006fe00ff6000014401ff4000000001ff4000000001ff4000000001ff4000000000ff6000026600bfa00007fe004ff4002ef8000affccffd100008ffffa10000000ef40000000006ef2000000000cf300000000ffb0000000005300000" + }, + { + "char": "È", + "utf8": "C388", + "data": "0a120c01120067400000005ff100000006fb000013336733326ffffffffc6ffbbbbbb86ff00000006ff00000006ff00000006ff44444306fffffffd06ffaaaaa806ff00000006ff00000006ff00000006ff00000006ffffffffb6ffffffffc" + }, + { + "char": "É", + "utf8": "C389", + "data": "0a120c0112000001772000000afa0000005fa00013336733326ffffffffc6ffbbbbbb86ff00000006ff00000006ff00000006ff44444306fffffffd06ffaaaaa806ff00000006ff00000006ff00000006ff00000006ffffffffb6ffffffffc" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "0a120c01120001bc4000002decf50000bc209c4013333333326ffffffffc6ffbbbbbb86ff00000006ff00000006ff00000006ff44444306fffffffd06ffaaaaa806ff00000006ff00000006ff00000006ff00000006ffffffffb6ffffffffc" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "0a120c0112006500271004ff10cf8001b9006c3013333333326ffffffffc6ffbbbbbb86ff00000006ff00000006ff00000006ff44444306fffffffd06ffaaaaa806ff00000006ff00000006ff00000006ff00000006ffffffffb6ffffffffc" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "0a120c01120005da0000007fbfc10005d803cb0003333333303ffffffff32bbcffcbb20003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20002ffffffff23ffffffff3" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "0a120c011202710065000bfa04ff1005c401ba0003333333303ffffffff32bbcffcbb20003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20000003ff20002ffffffff23ffffffff3" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "0c140c001300001bc300000002eecf4000001cb10ac300000000000000000036620000001bffffb10000cffaaffc0006ff2003ff500cf90000afb00ff500006ff01ff400004ff11ff400004ff11ff400004ff11ff400004ff00ff600007ff00bfb0000bfa004ff5006ff4000affddff9000008fffe7000000002200000" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "0a130c01120067400000004ff200000005fc00002320230232bf900009fbbf900009fbbf900009fbbfa00009fbbfa00009fbbfa0000afbbfa0000afbbfa0000afbbfa0000afbbfa0000afb9fc0000df94ff5005ff40bffcdffb0009ffff8000000220000" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "0a130c01120001ac4000001dfbf60000bc309d402320000232bf900009fbbf900009fbbf900009fbbfa00009fbbfa00009fbbfa0000afbbfa0000afbbfa0000afbbfa0000afbbfa0000afb9fc0000df94ff5005ff40bffcdffb0009ffff8000000220000" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0a130c0112006500271003ff20bf9000aa005c402320000232bf900009fbbf900009fbbf900009fbbfa00009fbbfa00009fbbfa0000afbbfa0000afbbfa0000afbbfa0000afbbfa0000afb9fc0000df94ff5005ff40bffcdffb0009ffff8000000220000" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "0c120c0012000740037000006ff00ef600002b8008b2001330000003323ff500004ff40bfc0000bfc003ff3002ff4000cfa009fc00004ff21ff500000cf87fd0000004ffef50000000cffd000000005ff6000000002ff2000000002ff2000000002ff2000000002ff2000000002ff20000" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "0c100c000f00036653333100bffffffff808fe78ffaaa50ff303fe00003fe003fe00004fd003fe00004fd003ff44404fd003fffff04fd003ff88804fd003fe00004fd003fe00002ff003fe00000ef503fe000006ffccfffff9007ffffffffa000022100000" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "0a100c010f0177200000009fc000000009f7000000003300000049bb820009ffffff504ff612bff03760002ff30004677ff407fffffff45ff6101ff4afa0001ff4afc0008ff44ffdbefff506effe5ef80002200000" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "0a100c010f0003cb2000004fdee30002ca01bc1000000000000049bb820009ffffff504ff612bff03760002ff30004677ff407fffffff45ff6101ff4afa0001ff4afc0008ff44ffdbefff506effe5ef80002200000" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "0c0c0c000b008bb62ac9100cffffffffe04ff46ff82df615401ff108f900035ff44afa05effffffffa3ffb6ff666647fe01ff000008fd01ff500104ffbcfffcbf208ffe58fffe3001200013200" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "0a100c010b0018bb930003ffffff601efc329ff27ff0000df7afa0000231bf90000000bf900000008fd00005843ff5002ef509ffcbffc0007ffff9000000df500000006df30000000af5000000dfc0000000430000" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "0a100c010f0177200000008fc000000009f7000000003300000007bb930003efffff700efc328ff36ff1000bf9bfc44449fdcffffffffdcfc77777769fd00000003ff80003b108ffebcff6006dfffd500000131000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0a100c010f000002771000000df70000009f700000003200000007bb930003efffff700efc328ff36ff1000bf9bfc44449fdcffffffffdcfc77777769fd00000003ff80003b108ffebcff6006dfffd500000131000" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0a100c010f0003cc2000004fdde30002ca11bc2000000000000007bb930003efffff700efc328ff36ff1000bf9bfc44449fdcffffffffdcfc77777769fd00000003ff80003b108ffebcff6006dfffd500000131000" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0a100c010f017300460008fd00ff4003b6009b1000000000000007bb930003efffff700efc328ff36ff1000bf9bfc44449fdcffffffffdcfc77777769fd00000003ff80003b108ffebcff6006dfffd500000131000" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "0a0f0c010f00008d8000000afbfa00008d605d80000000000009999950000fffffa0000666dfa0000000bfa0000000bfa0000000bfa0000000bfa0000000bfa0000000bfa0000ffffffffc0ffffffffd" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "0a0f0c010f004700073000ff607fe0008b202c70000000000009999950000fffffa0000666dfa0000000bfa0000000bfa0000000bfa0000000bfa0000000bfa0000000bfa0000ffffffffc0ffffffffd" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0a100c010f0002cc2000003eddf40001cb11ac2000000000000028bb820005ffffff502ffb33bff29fd0000df9df800008fcef600007feef700007fdbfb0000bfb5ff4004ff50bffccffb0009ffff9000000220000" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "0a100c010f0177300000007fd000000008f80000000033000039800007956fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf95ff0000cf93ff4003ff90dffccfef902cfff69f90002300000" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "0a100c010f0002cc2000003eddf40001cb11ac20000000000039800007956fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf95ff0000cf93ff4003ff90dffccfef902cfff69f90002300000" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "0a100c010f007300370006ff00ff6002b7008b20000000000039800007956fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf96fe0000cf95ff0000cf93ff4003ff90dffccfef902cfff69f90002300000" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0c140c000f000650027100003ff20bfa00000aa005c4000000000000002992000008950ef900004ff308ff0000afc001ff6001ff50009fc007fe00002ff30df700000bf94ff1000004ffaf90000000dfff200000006ffb000000000ff4000000006fd000000001ef60000001befd00000004ffc2000000003300000000" + }, + { + "char": "œ", + "utf8": "C593", + "data": "0c0c0c000b007bb519c9100bffffefffd03ff47ffc3ef57fc00ff709f89fa00ef95bf99fa00efffffa9fa00ef966638fa00ef600005fe02ffb00000efcefffcbd003effc6effc0000220002200" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_tthoves_bold_17_cs.json b/core/embed/rust/src/ui/translations/fonts/font_tthoves_bold_17_cs.json new file mode 100644 index 000000000..135302e1a --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_tthoves_bold_17_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "0d100d001000000004760000000002ff4000000000bf70000000000000000000003ffff100000008ffff60000000dffffb0000003ff8aff1000008ff35ff600000efe00ffc00003ff900bff10009ffffffff6000effffffffc003ffb6666cff109ff500007ff70eff000002ffc" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0c100c00100004730671000002efbfa00000004ffc000000000000000000029cfd9200005fffffff4002fffdbeffe10cff70008ff80ffb000007863ff7000000003ff7000000000ffb000008970cff60008ff802fffdadffe1005fffffff4000029dfd9200" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0b100c011000672177000004fecf70000006ffa000000000000000cffffd92000cfffffff600cffaadfff30cfe0006ffd0cfe0000bff1cfe00006ff4cfe00006ff4cfe0000bff1cfe0006ffd0cff9adfff30cfffffff600cffffd92000" + }, + { + "char": "É", + "utf8": "C389", + "data": "09100a01100000277100000cf9000006fc000000000000cfffffff5cfffffff5cff777772cfe000000cfe000000cfffffff0cfffffff0cff666660cfe000000cff777772cfffffff5cfffffff5" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "09100a011005730671002ffbf900004ffc000000000000cfffffff5cfffffff5cff777772cfe000000cfe000000cfffffff0cfffffff0cff666660cfe000000cff777772cfffffff5cfffffff5" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "05100501100077307fd02fe2000000cfe00cfe00cfe00cfe00cfe00cfe00cfe00cfe00cfe00cfe00cfe00cfe00" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "0b100d011000473067100002efbfa0000004ffc000000000000000cffc0006ff4cfff4006ff4cfffc006ff4cffff406ff4cffffc06ff4cfe7ff46ff4cfe0efc6ff4cfe06ffdff4cfe00dffff4cfe005ffff4cfe000dfff4cfe0004fff4" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "0d100d001000000007720000000009fc0000000003fe10000000000000000000029cfc91000005ffffffe40002fffdbdfff100cff70008ffa00ffb00000dff03ff7000009ff23ff7000009ff20ffb00000dff00cff60008ffa002fffdadfff10005ffffffe40000029dfc91000" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0b100c01100276047400000bfafd1000001dff2000000000000000cfffffd9000cfffffffc00cff889eff50cfe0004ff90cfe0001ffa0cfe0008ff80cffffffff20cffffffd300cffaffc0000cfe0affa000cfe00bff900cfe000cff90" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0b100b001000474067200001efafa0000003ffd000000000000000004befd910006fffffff200fff97cffc03ff9000cff01ffe630000009fffffc600005beffff900000015eff06ff50009ff22fff868ffe007fffffff50003bdfda300" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0a100a00100067117600005fddf5000008ff80000000000000affffffffaaffffffffa6aacffcaa70005ff50000005ff50000005ff50000005ff50000005ff50000005ff50000005ff50000005ff50000005ff5000" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "0b100c01100000037600000001ef60000000af8000000000000000efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efd00009ff1cfe0000bff0aff6003ffd04fffcbfff7009ffffffc00004beec7000" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "0b110c0111000001000000005ef80000000ba7e00000005ff8000000000100000efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efd00009ff1cfe0000bff0aff6003ffd04fffcbfff7009ffffffc00004beec70000" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "0d100c001000000007720000000009fc0000000004fd1000000000000000001eff10002ffd007ff8000aff5000eff102ffc00006ff80aff400000eff3ffc0000006fffff30000000dfffb000000005fff3000000000efd0000000000efc0000000000efc0000000000efc00000" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0a100b00100057306710002ffbf9000004ffc00000000000003ffffffffc3ffffffffc2aaaaafffa000009ffe100005fff300002fff600001dff900000bffc000008ffe100003fffc999985ffffffffd5ffffffffd" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "090d0a000d00000476000002ff400000cf700000000000006dfea2008fffffe10cca27ff7000000ff903beffffa1ffebbffa5ff505ffa2fffffffa03cfe9afa0" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0a0d0a000d0067117600005fddf5000008ff80000000000000002befc40004ffffff600eff87eff13ff60039935ff20000003ff60038830eff87eff104ffffff60003befc400" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "0e0c0d000c0000000ff93ff10000000ff97fa00000000ff9ae40003bee7ff9000005fffffff900000eff87eff900003ff6003ff900005ff2000ff900003ff6003ff900000eff87eff9000005fffffff90000003bfe7bf90000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0a0d0a000d000003760000001ef5000000af80000000000000003beeb40004ffffff600efc21aff23ffa889ff65ffffffff73ff50000000efd229cc104ffffff60002beeb400" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0a0d0a000d0067117600005fddf5000008ff80000000000000003beeb40004ffffff600efc21aff23ffa889ff65ffffffff73ff50000000efd229cc104ffffff60002beeb400" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "050d05010d07fe12ff304740000000ff800ff800ff800ff800ff800ff800ff800ff800ff8000" + }, + { + "char": "ň", + "utf8": "C588", + "data": "090d0a010d05730771003febf800005ffb000000000000ff7bfe800fffffffa0fffb9fff2ffc007ff4ff9003ff5ff8003ff5ff8003ff5ff8003ff5ff8003ff50" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "0a0d0a000d000003770000000ef70000009f90000000000000002beeb40004ffffff700eff87eff23ff6003ff65ff2000ff83ff6003ff60eff87eff204ffffff70003beeb400" + }, + { + "char": "ř", + "utf8": "C599", + "data": "080d07000d0bf78fb001dffd0000277200000000000ffadf900fffff900fff96300ffb00000ff800000ff800000ff800000ff800000ff80000" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "090d09000d05730671002ffbf900004ffb00000000000003aefd6002ffffff806ff308bb05ffc8510009fffff6000146dff06dd107ff11effeffb002befd8000" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "0a0d09000d0000004dd002ff608fa002ff60cf4002ff6000007fffff50007fffff500037ffa6200002ff60000002ff60000002ff60000002ffb5200000ffff5000005dff5000" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "090d0a010d0000afc000004fe100000572000000000000ff8003ff5ff8003ff5ff8003ff5ff8003ff5ff9003ff5ffc006ff5cffb9fff55fffffff505dfd6ff50" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "090e0a010e000010000004ef900000ab6f000004ef9000000010000ff8003ff5ff8003ff5ff8003ff5ff8003ff5ff9003ff5ffc006ff5cffb9fff55fffffff505dfd6ff5" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "0b110a000d000002ff50000000cf700000002760000000000000000efc0007ff409ff200bfe003ff700ff9000dfd04ff40008ff29fe00002ff8dfa00000dffff4000007ffff0000002fffa0000000cff50000056dff000000ffff9000000ffea100000" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "080d09000d06fc3fe2009fff4000077400000000005ffffffe5ffffffe2666effa0009ffc0007ffd1005ffe2003fff95556fffffff6fffffff" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_tthoves_bold_17_fr.json b/core/embed/rust/src/ui/translations/fonts/font_tthoves_bold_17_fr.json new file mode 100644 index 000000000..5abf4fad2 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_tthoves_bold_17_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "0d100d0010000067400000000005ff100000000007fb00000000000000000000003ffff100000008ffff60000000dffffb0000003ff8aff1000008ff35ff600000efe00ffc00003ff900bff10009ffffffff6000effffffffc003ffb6666cff109ff500007ff70eff000002ffc" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0d100d0010000002771000000001dffc00000000cf68fa000000000000000000003ffff100000008ffff60000000dffffb0000003ff8aff1000008ff35ff600000efe00ffc00003ff900bff10009ffffffff6000effffffffc003ffb6666cff109ff500007ff70eff000002ffc" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "110c11000c000005ffffffffff800000cffffffffff800003ffbcff8777740000aff29ff1000000001ffb09ff1000000008ff509fffffff3000efe009fffffff3006ffffffff76666100dffffffff10000004ffc888dff8777730bff30009fffffff82ffd00009fffffff8" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "0c100c000c00029cfd9200005ffffffe3002fffdbeffe00cff70008ff70ffc000008973ff7000000004ff7000000001ffa000006760dff40005ff905fffa7afff2008fffffff400005cfffc40000000bf10000000008fc0000000017ef000000002fd60000" + }, + { + "char": "È", + "utf8": "C388", + "data": "09100a011004760000001ef50000003fe1000000000000cfffffff5cfffffff5cff777772cfe000000cfe000000cfffffff0cfffffff0cff666660cfe000000cff777772cfffffff5cfffffff5" + }, + { + "char": "É", + "utf8": "C389", + "data": "09100a01100000277100000cf9000006fc000000000000cfffffff5cfffffff5cff777772cfe000000cfe000000cfffffff0cfffffff0cff666660cfe000000cff777772cfffffff5cfffffff5" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "09100a011000077300000afff30007fb4fe10000000000cfffffff5cfffffff5cff777772cfe000000cfe000000cfffffff0cfffffff0cff666660cfe000000cff777772cfffffff5cfffffff5" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "09100a011000110110008fa2ff0008fa2ff00000000000cfffffff5cfffffff5cff777772cfe000000cfe000000cfffffff0cfffffff0cff666660cfe000000cff777772cfffffff5cfffffff5" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "0710060010005760005fff603fe3df4000000000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe00" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "071006001001101104ff0df54ff0df5000000000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe0000cfe00" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "0d100d0010000006750000000007fff500000004fd3ef3000000000000000000029cfc91000005ffffffe40002fffdbdfff100cff70008ffa00ffb00000dff03ff7000009ff23ff7000009ff20ffb00000dff00cff60008ffa002fffdadfff10005ffffffe40000029dfc91000" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "0b100c011000575000000003ff3000000005fd0000000000000000efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efd00009ff1cfe0000bff0aff6003ffd04fffcbfff7009ffffffc00004beec7000" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "0b100c011000017720000000cffe1000009f96fc00000000000000efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efd00009ff1cfe0000bff0aff6003ffd04fffcbfff7009ffffffc00004beec7000" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0b100c01100011001100000af84fe00000af84fe00000000000000efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efc00009ff1efd00009ff1cfe0000bff0aff6003ffd04fffcbfff7009ffffffc00004beec7000" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "0d100c001000001101100000005fd0ff30000005fd0ff300000000000000001eff10002ffd007ff8000aff5000eff102ffc00006ff80aff400000eff3ffc0000006fffff30000000dfffb000000005fff3000000000efd0000000000efc0000000000efc0000000000efc00000" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "120c12000c00029dfd79fffffff6005ffffffffffffff602fffa8cffff7777730bff50009fff0000000ffb00001fff0000003ff700000cfffffff14ff700000bfffffff11ffa00000eff6666600dff30007fff00000004fffa8bffff777773007ffffffffffffff60002adfd79fffffff6" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "090d0a000d006740000005fe10000008fb000000000000006dfea2008fffffe10cca27ff7000000ff903beffffa1ffebbffa5ff505ffa2fffffffa03cfe9afa0" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "090d0a000d000277200000dffd0000bf77fb0000000000006dfea2008fffffe10cca27ff7000000ff903beffffa1ffebbffa5ff505ffa2fffffffa03cfe9afa0" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "0f090f0009007dfd706dfc70008fffffefffffa00cc916fff817ff5003688fff555ef90affffffffffffc3ff933ffe3222225ff607fff715cc41ffffffdfffffb003cfeb307dfc8000" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "0a0d0a0009002aefc40004ffffff500eff87eff13ff60039935ff20000004ff50027720efe66dff106ffffff70005dffd6000000de000000009fb00000017fe0000004fc5000" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "0a0d0a000d0067400000004ff200000006fc00000000000000003beeb40004ffffff600efc21aff23ffa889ff65ffffffff73ff50000000efd229cc104ffffff60002beeb400" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0a0d0a000d000003760000001ef5000000af80000000000000003beeb40004ffffff600efc21aff23ffa889ff65ffffffff73ff50000000efd229cc104ffffff60002beeb400" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0a0d0a000d0002772000000dffd00000bf77fb000000000000003beeb40004ffffff600efc21aff23ffa889ff65ffffffff73ff50000000efd229cc104ffffff60002beeb400" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0a0d0a000d001100110000cf66fc0000cf66fc000000000000003beeb40004ffffff600efc21aff23ffa889ff65ffffffff73ff50000000efd229cc104ffffff60002beeb400" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "070d06000d03ffc002efbfa04740771000000000ff80000ff80000ff80000ff80000ff80000ff80000ff80000ff80000ff8000" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "070d06000d01101108fa1ff18fa1ff1000000000ff80000ff80000ff80000ff80000ff80000ff80000ff80000ff80000ff8000" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0a0d0a000d0001772000000cffe10000af96fc000000000000002beeb40004ffffff700eff87eff23ff6003ff65ff2000ff83ff6003ff60eff87eff204ffffff70003beeb400" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "090d0a010d07fe1000000afa0000000770000000000000ff8003ff5ff8003ff5ff8003ff5ff8003ff5ff9003ff5ffc006ff5cffb9fff55fffffff505dfd6ff50" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "090d0a010d005ffa00003febf800057307710000000000ff8003ff5ff8003ff5ff8003ff5ff8003ff5ff9003ff5ffc006ff5cffb9fff55fffffff505dfd6ff50" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "090d0a010d01110110009f93ff0009f93ff00000000000ff8003ff5ff8003ff5ff8003ff5ff8003ff5ff9003ff5ffc006ff5cffb9fff55fffffff505dfd6ff50" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0b110a000d00011011000002ff1bf700002ff1bf700000000000000efc0007ff409ff200bfe003ff700ff9000dfd04ff40008ff29fe00002ff8dfa00000dffff4000007ffff0000002fffa0000000cff50000056dff000000ffff9000000ffea100000" + }, + { + "char": "œ", + "utf8": "C593", + "data": "1009100009003beea119eeb40005fffffeefffff600efe78fffe31aff13ff5008ffc889ff55ff2005ffffffff63ff6009ff90000000eff79ffff419cc004fffffeefffff60003beea12aeeb500" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_tthoves_demibold_21_cs.json b/core/embed/rust/src/ui/translations/fonts/font_tthoves_demibold_21_cs.json new file mode 100644 index 000000000..8017d39eb --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_tthoves_demibold_21_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "0f140e001400000000144000000000003fe200000000001ef40000000000058500000000000000000000000000efff60000000004ffffc000000000afffff100000000fff9ff700000005ffa2ffc0000000aff50dff2000001fff008ff8000006ffa003ffd00000bff5000eff30001fff9888dff80006ffffffffffe000cfffffffffff402ffe0000007ff907ff90000001ffe0dff30000000bff4" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0f130f001300006fe14ff2000000008fcff40000000000bff600000000000000000000000007cffd810000003dffffffe400003fffebbefff4000dff800008ffe007ff90000009ff60cff100000015530ffd000000000000ffb000000000000ffd000000000000cff1000000166407ff90000009ff500dff800008ffd0003fffebaefff400003dffffffe400000008cffd810000" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0e140f0114000ef709fc00000004ff5ff300000000afff80000000001efd000000000000000000006fffffec7000006ffffffffd30006ffb9abffff3006ff400009ffd006ff4000009ff706ff4000001ffc06ff4000000dff06ff4000000bff06ff4000000dff06ff4000001ffc06ff4000009ff706ff400008ffd006ffb99befff3006ffffffffd30006ffffffc700000" + }, + { + "char": "É", + "utf8": "C389", + "data": "0b140d011400000003500000001ef50000000bf800000002870000000000000006fffffffff66fffffffff66ffa88888836ff400000006ff400000006ff400000006ffa88888806fffffffff06fffffffff06ff400000006ff400000006ff400000006ffa88888836fffffffff66fffffffff6" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "0b140d011400aa3037700002fe4ef5000006fff800000007870000000000000006fffffffff66fffffffff66ffa88888836ff400000006ff400000006ff400000006ffa88888806fffffffff06fffffffff06ff400000006ff400000006ff400000006ffa88888836fffffffff66fffffffff6" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "0614060114000242008fc005fd100782000000006ff4006ff4006ff4006ff4006ff4006ff4006ff4006ff4006ff4006ff4006ff4006ff4006ff4006ff4006ff400" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "0d140f01140008a402771000001ef4df700000003fffa0000000005880000000000000000006fff700000ffb6ffff10000ffb6ffff80000ffb6fffff1000ffb6ffcff8000ffb6ff4dff100ffb6ff45ff900ffb6ff40cff10ffb6ff404ff90ffb6ff400cff2ffb6ff4003ffcffb6ff4000bffffb6ff40003ffffb6ff40000bfffb6ff400003fffb" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "10141000140000000000000000000000005ff5000000000001ef7000000000000afa0000000000000000000000000007cffd8100000003dffffffe5000003fffebbefff60000dff800007ffe1007ff90000007ff900cff10000000efe00ffd00000000aff10ffb000000008ff30ffd00000000aff20cff10000000eff007ff90000006ffa000dff800006ffe10003fffebbefff6000003dffffffe5000000008cffd810000" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0d140e0114007fe02ff4000000cf9bfa00000003fffe1000000008ff60000000000000000006fffffffc50006fffffffff9006ffb999bfff306ff400005ffa06ff400000efc06ff400000efd06ff400004ffa06ffa8889fff506fffffffffa006fffffffc60006ff46ffe200006ff407ffe10006ff4008ffd1006ff40008ffd106ff400009ffc0" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0d130d0013000afb08fd0000000cfbfe100000001eff3000000000000000000004bdfdb500000afffffffb0006fffa9bfff800cff20002ffe00efd000008dd10cff60000000005ffffc961000007fffffffa10000047adfffb0000000002dff32cc7000006ff50ffe20000aff308fffb89dffd000bfffffffe200004adfec80000" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0c140c0014002c910575000006fb5fe1000000bfff30000000188400000000000000009fffffffffff9fffffffffff5999bffd999900002ff8000000002ff8000000002ff8000000002ff8000000002ff8000000002ff8000000002ff8000000002ff8000000002ff8000000002ff8000000002ff8000000002ff80000" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "0d140f011400000000000000000001efa0000000009fc0000000004fe1000000000000000008ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff3000001ffa6ff4000003ff83ff9000008ff50eff60005fff106fffdacfff70008fffffffa000003adfda4000" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "0d150f0115000000300000000001efe2000000007d1c9000000006e5e8000000000afb1000000000000000008ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff3000001ffa6ff4000003ff83ff9000008ff50eff60005fff106fffdacfff70008fffffffa000003adfda40000" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "0f140e001400000000035000000000001ef500000000000bf8000000000002870000000000000000000000eff1000001ffe006ffa00000aff6000dff20002ffd00005ffb000bff500000cff403ffc0000003ffc0cff30000000affbffa000000002fffff20000000009fff900000000001fff100000000000dfd000000000000dfd000000000000dfd000000000000dfd000000000000dfd000000" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0c140d0014000ba20476000003fd4ff40000008fff60000000078600000000000000002ffffffffffd2ffffffffffd1999999afffb00000007ffe10000003fff40000001eff80000000bffc00000007ffe20000003fff50000001eff90000000bffc00000007fff20000002fffd99999994fffffffffff4fffffffffff" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "0b100c001000000000000000000afe10000004ff30000000ef5000000000000000007cfda20000cffffff4007ff935efe005770006ff3004befffff406ffffffff40ffe2006ff41ff80009ff40ffe427fff408fffffeff4006cfe91ff4" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0c0f0c000f002ef51df6000004ffcf900000006ffb00000000000000000003adfc7000007ffffffd1003fff97bffa00cff3000bff10ffa000001102ff7000000000ffa000012200cff2000bff104fff97bffa0007ffffffd100003adfc7000" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "100f0f000f000000001ff80cf8000000001ff80ff3000000001ff84fd0000000001ff849500005bee92ff80000009fffffeff8000004ffe87cfff800000cff2000bff800000ff900003ff800002ff700001ff800000ff900003ff800000dff1000aff8000005ffe87cfff8000000afffffeff800000005bfea1df80000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0c100c00100000000000000000003ff600000000df9000000008fb00000000000000000003adfc7000008ffffffd1004ffc548ffb00dfe00008ff30ffeccccdff71ffffffffff70ff9000000000bfe1000355003ffd549ffc0006ffffffd200003adfc8000" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0c0f0c000f002ef51df6000004ffcf900000006ffb00000000000000000003adfc7000008ffffffd1004ffc548ffb00dfe00008ff30ffeccccdff71ffffffffff70ff9000000000bfe1000355003ffd549ffc0006ffffffd200003adfc8000" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "061005011000035000df700afa00187000000000aff000aff000aff000aff000aff000aff000aff000aff000aff000aff000aff000" + }, + { + "char": "ň", + "utf8": "C588", + "data": "0b0f0d010f00df80bfa00001efbfc0000003ffe100000000000000afd4dfeb200affffffff30afff97cffc0aff5000cff0aff00007ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff30" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "0c100d00100000000000000000002ff800000000cfb000000006fd00000000000000000003adfc8000007ffffffd2003fff97cffc00cff30009ff50ffa00001ff82ff700000efb0ffa00001ff80cff20009ff504fff97bffc0007ffffffd200003adfc8000" + }, + { + "char": "ř", + "utf8": "C599", + "data": "0810080110afc05ff11ef6ef7006fffc0000bff30000000000afa9ffd0afffffd0afff9870aff30000aff00000aff00000aff00000aff00000aff00000aff00000aff00000" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "0a0f0b000f01ef60df70003ffcfa000005ffc0000000000000005ceec60009ffffffb02ffc439ff55ff60007741fffc9620004efffffb0000369dff82771000efc2ffb315ffb08fffffff3005cefd920" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "0b100a00100000000188100000006fe000cfd009f8000cfd00df3000cfd0000007ffffff30007ffffff300048efe88100000cfd00000000cfd00000000cfd00000000cfd00000000cfd00000000cff541000009ffff3000001afff3000" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "0b100d011000000000000000002ff80000000cfb00000006fd000000000000000aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff39ff00007ff37ff5000bff33fff97cfff309fffffeff3006cfd74ff3" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "0b110d0111000013000000004ffc0000000ba2f5000000ac6f40000002cf9000000000000000aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff39ff00007ff37ff5000bff33fff97cfff309fffffeff3006cfd74ff30" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "0c140b0010000000000000000000bfd000000006fe200000001ff30000000000000000cfe00000cfe06ff40002ff901ffa0007ff300aff000dfe0004ff602ff80000efc08ff300008ff2dfd000002ffdff7000000cffff20000006fffc00000001fff700000000bff100000467ffc000000bffff5000000bffe7000000" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "0a100a001003c8007730009f98fd00000dffe200000288300000000000004ffffffff34ffffffff316666affe100001eff400000dff600000bff9000008ffb000005ffd000003fff8666625ffffffff55ffffffff5" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_tthoves_demibold_21_fr.json b/core/embed/rust/src/ui/translations/fonts/font_tthoves_demibold_21_fr.json new file mode 100644 index 000000000..f48d54b5f --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_tthoves_demibold_21_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "0f140e00140000254000000000000cff1000000000000cfb000000000000088100000000000000000000000000efff60000000004ffffc000000000afffff100000000fff9ff700000005ffa2ffc0000000aff50dff2000001fff008ff8000006ffa003ffd00000bff5000eff30001fff9888dff80006ffffffffffe000cfffffffffff402ffe0000007ff907ff90000001ffe0dff30000000bff4" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0f140e0014000004eb500000000001efff3000000000bfc5fe2000000028810685000000000000000000000000efff60000000004ffffc000000000afffff100000000fff9ff700000005ffa2ffc0000000aff50dff2000001fff008ff8000006ffa003ffd00000bff5000eff30001fff9888dff80006ffffffffffe000cfffffffffff402ffe0000007ff907ff90000001ffe0dff30000000bff4" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "140f15000f0000005ffffffffffffc000000cffffffffffffc000003ffd9ffd8888886000009ff51ffa000000000001ffe01ffa000000000007ff801ffa00000000000eff101fffffffff50005ffa001fffffffff5000cff3001ffd8888883002fffbbbbffa0000000009fffffffffa000000001fffaaaabffa000000007ff900001ffd88888860eff200001fffffffffc5ffb000001fffffffffc" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "0f140f000f000007cefd810000003dffffffe400003ffffbbefff3000dff900008ffd006ffa000000aff50cff100000016640ffd000000000000ffb000000000000efc000000000000cff0000000022108ff60000007ff701fff400003fff1007fffa659fff700008ffffffff90000004bffffe60000000003fb2000000000006fd30000000000002fe00000000000bffb00000000000696000000" + }, + { + "char": "È", + "utf8": "C388", + "data": "0b140d011400145000000008ff5000000008fe1000000006840000000000000006fffffffff66fffffffff66ffa88888836ff400000006ff400000006ff400000006ffa88888806fffffffff06fffffffff06ff400000006ff400000006ff400000006ffa88888836fffffffff66fffffffff6" + }, + { + "char": "É", + "utf8": "C389", + "data": "0b140d011400000003500000001ef50000000bf800000002870000000000000006fffffffff66fffffffff66ffa88888836ff400000006ff400000006ff400000006ffa88888806fffffffff06fffffffff06ff400000006ff400000006ff400000006ffa88888836fffffffff66fffffffff6" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "0b140d01140001db70000000bfff7000007fe4ef50000883038700000000000006fffffffff66fffffffff66ffa88888836ff400000006ff400000006ff400000006ffa88888806fffffffff06fffffffff06ff400000006ff400000006ff400000006ffa88888836fffffffff66fffffffff6" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "0b140d01140000000110000afc0cfa0000afc0cfa0000465023200000000000006fffffffff66fffffffff66ffa88888836ff400000006ff400000006ff400000006ffa88888806fffffffff06fffffffff06ff400000006ff400000006ff400000006ffa88888836fffffffff66fffffffff6" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "0814070014009da20004fffd101ef8afb04870088300000000006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "0814070014000000203ff35ff13ff35ff11661133000000000006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400006ff400" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "10131000130000007ffa000000000005fedf70000000003ff32ef500000000000000000000000007cffd8100000003dffffffe5000003fffebbefff60000dff800007ffe1007ff90000007ff900cff10000000efe00ffd00000000aff10ffb000000008ff30ffd00000000aff20cff10000000eff007ff90000006ffa000dff800006ffe10003fffebbefff6000003dffffffe5000000008cffd810000" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "0d140f01140000000000000000afe00000000000cf900000000001ef40000000000000000008ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff3000001ffa6ff4000003ff83ff9000008ff50eff60005fff106fffdacfff70008fffffffa000003adfda4000" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "0d140f011400000efe1000000008fffa00000003ff5ff4000000cf907fd00000000000000008ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff3000001ffa6ff4000003ff83ff9000008ff50eff60005fff106fffdacfff70008fffffffa000003adfda4000" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0d130f01130009fc0bfb0000009fc0bfb000000465046400000000000000008ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff2000001ffa8ff3000001ffa6ff4000003ff83ff9000008ff50eff60005fff106fffdacfff70008fffffffa000003adfda40000" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "0f140e00140000000001100000000afc0cfa00000000afc0cfa00000000465023200000000000000000000eff1000001ffe006ffa00000aff6000dff20002ffd00005ffb000bff500000cff403ffc0000003ffc0cff30000000affbffa000000002fffff20000000009fff900000000001fff100000000000dfd000000000000dfd000000000000dfd000000000000dfd000000000000dfd000000" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "160f17000f000008dfeb50fffffffffd0003dffffffcfffffffffd003fffc89dffffd888888700dff60000afffa000000006ff9000000effa00000000cff10000007ffa00000000ffd00000003ffd88888830ffb00000001fffffffff60ffc00000002fffffffff60dff00000005ffa000000008ff6000000cffa000000001eff400008fffa0000000005fffb78dffffd88888860005effffffbfffffffffd000019dfec50fffffffffd" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "0b100c001000000000000005ff4000000008fe000000000bf90000000000000000007cfda20000cffffff4007ff935efe005770006ff3004befffff406ffffffff40ffe2006ff41ff80009ff40ffe427fff408fffffeff4006cfe91ff4" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "0b0f0c000f0000cff5000000afcff300007fd06fe10000000000000007cfda20000cffffff4007ff935efe005770006ff3004befffff406ffffffff40ffe2006ff41ff80009ff40ffe427fff408fffffeff4006cfe91ff40" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "130b13000b0008dfd9103beeb400000dfffffe7ffffff90007ff946efff9339ff6005760005ffa0000dfd00029cddeffeddddfff105ffffffffffffffff10efe3004ff8000000001ff90008ffd000055400ffd426ffffc45cff6008ffffff99ffffffa00006cfeb5005beeb50000" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "0c100c000b0003adfc7000007ffffffd1003fff97bffa00cff3000bff10ffa000001102ff7000000000ff9000000000dfe00008ee206ffc426ffd000cfffffff300008ffffc30000000df2000000001ff70000000000cf300000006fff1000000039720000" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "0c100c0010000000000000002ef90000000003ff30000000006fd000000000000000000003adfc7000008ffffffd1004ffc548ffb00dfe00008ff30ffeccccdff71ffffffffff70ff9000000000bfe1000355003ffd549ffc0006ffffffd200003adfc8000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0c100c00100000000000000000003ff600000000df9000000008fb00000000000000000003adfc7000008ffffffd1004ffc548ffb00dfe00008ff30ffeccccdff71ffffffffff70ff9000000000bfe1000355003ffd549ffc0006ffffffd200003adfc8000" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0c0f0c000f00006ffb00000003ffcf9000002ef51ef7000000000000000003adfc7000008ffffffd1004ffc548ffb00dfe00008ff30ffeccccdff71ffffffffff70ff9000000000bfe1000355003ffd549ffc0006ffffffd200003adfc8000" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0c0f0c000f000ff71ff500000ff71ff5000006630662000000000000000003adfc7000008ffffffd1004ffc548ffb00dfe00008ff30ffeccccdff71ffffffffff70ff9000000000bfe1000355003ffd549ffc0006ffffffd200003adfc8000" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "081006001000cc900008fffb003ff5cf80685018810000000000aff00000aff00000aff00000aff00000aff00000aff00000aff00000aff00000aff00000aff00000aff000" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "071006001000000126ff08fd6ff08fd2660133000000000aff0000aff0000aff0000aff0000aff0000aff0000aff0000aff0000aff0000aff0000aff00" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0c0f0d000f00004ffd00000002ffcfb000001ef70df8000000000000000003adfc8000007ffffffd2003fff97cffc00cff30009ff50ffa00001ff82ff700000efb0ffa00001ff80cff20009ff504fff97bffc0007ffffffd200003adfc8000" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "0b100d01100000000000000cfc000000001ef7000000003ff2000000000000000aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff39ff00007ff37ff5000bff33fff97cfff309fffffeff3006cfd74ff3" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "0b100d01100003ffc0000000cfff6000007fe6fe10001ff50cfa0000000000000aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff39ff00007ff37ff5000bff33fff97cfff309fffffeff3006cfd74ff3" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "0b0f0d010f00df80ff60000df80ff6000056306620000000000000aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff3aff00006ff39ff00007ff37ff5000bff33fff97cfff309fffffeff3006cfd74ff30" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0c130b000f007fe09fc000007fe09fc000003660465000000000000000cfe00000cfe06ff40002ff901ffa0007ff300aff000dfe0004ff602ff80000efc08ff300008ff2dfd000002ffdff7000000cffff20000006fffc00000001fff700000000bff100000467ffc000000bffff5000000bffe7000000" + }, + { + "char": "œ", + "utf8": "C593", + "data": "140b14000b0004beeb4004beeb5000008ffffff89ffffffa0005ffe87dffffb54aff600dfe1000dffc0000dfe00ff900006ffdaaaadff11ff800005ffffffffff20ffb00008ff9000000000bff3001fffe0000764003fff88effffc55bff70007ffffff9affffffb000003aefc5005beeb6000" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_tthoves_regular_21_cs.json b/core/embed/rust/src/ui/translations/fonts/font_tthoves_regular_21_cs.json new file mode 100644 index 000000000..87b11cfde --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_tthoves_regular_21_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "0e130e001300000004f800000000000db000000000009d10000000000000000000000009ff40000000000fefa0000000005f5bf000000000bf05f500000001f900fb00000007f3009f1000000dd0003f7000003f70000dd000009f100007f30000ee88888af90005ffffffffff000bf00000005f501fa00000000fb07f400000000af1de0000000005f7" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "0e130f01130001ea01eb000000003f7bd00000000006ff2000000000000000000000007cfec81000003dfebacff50002ff500003df500cf20000001ee06f7000000006e5bf100000000000de000000000000ec000000000000de000000000000bf1000000000006f7000000007f50cf20000001ee003fe500003df40003dfea9cff50000007cfec81000" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "0d130f01130009e109e20000000cc6f4000000001ef70000000000000000005fffffeb500005fb999cffb1005f5000008fd005f50000006f805f50000000cf15f500000006f65f500000003f85f500000001f95f500000003f85f500000006f65f50000000cf15f50000006f805f5000008fd005fb889befb1005fffffeb500000" + }, + { + "char": "É", + "utf8": "C389", + "data": "0b130c0113000000af200000005f500000001e80000000000000005fffffffff15fb999999915f5000000005f5000000005f5000000005f5000000005fb888888605ffffffffb05f5000000005f5000000005f5000000005f5000000005f5000000005fb888888815fffffffff10" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "0b130c0113007f306f400000ae5f70000000cfa0000000000000005fffffffff15fb999999915f5000000005f5000000005f5000000005f5000000005fb888888605ffffffffb05f5000000005f5000000005f5000000005f5000000005f5000000005fb888888815fffffffff10" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "0513050113009f403f700da00000005f5005f5005f5005f5005f5005f5005f5005f5005f5005f5005f5005f5005f5005f5005f5000" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "0d130f01130006f405f400000009e4f7000000000cfa0000000000000000005ffc0000007f35fff4000007f35f7fc000007f35f57f400007f35f50ec00007f35f507f40007f35f500ed0007f35f5007f5007f35f5000ed007f35f50006f507f35f50000ed07f35f500006f57f35f500000dd7f35f5000006fff35f5000000dff30" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "0f1310011300000001ec000000000000ae1000000000005f300000000000000000000000007cffd810000003dfebadfe400002ff500004ef5000cf20000001ee006f7000000005f80bf1000000000ed0de0000000000bf0ec00000000009f1de0000000000bf0bf1000000000ed06f7000000005f800cf20000001ee0003fe500003ef500003dfeaadfe400000007cffd8100000" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "0c130d0113002f802f80000005f5db000000008fd100000000000000005ffffffea2005fb9999aff305f5000002fc05f50000009f15f50000007f35f50000009f15f5000002fc05fb8888aff305ffffffeb2005f51df3000005f501df300005f5001df30005f50001df3005f500001de305f5000002ee2" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "0c130d00130007f306f4000000ae5f700000000cfa00000000000000000004befc9200009feb9cff6005fa00002df309f1000003f80bf00000007507f70000000000dfc85200000008dfffe91000000036bfe10000000005f90750000000ec0dd0000000fb07f9000009f600afeb9aefb00004adfdb500" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "0c130c0013000ae10ae1000000dc6f400000002ef600000000000000007ffffffffffd49999df9999700000af0000000000af0000000000af0000000000af0000000000af0000000000af0000000000af0000000000af0000000000af0000000000af0000000000af0000000000af0000000000af00000" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "0d130e01130000000de10000000008f30000000002f50000000000000000008f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af07f30000000ce04f60000000eb01fd0000006f7006fa10005fd00009feb9dfd3000004adfc800000" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "0d140e011400001ce60000000008a3f000000000992f0000000002df70000000000000000008f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af07f30000000ce04f60000000eb01fd0000006f7006fa10005fd00009feb9dfd3000004adfc80000" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "0d130c00130000000dd10000000008f20000000003f5000000000000000000bf10000000bf12fa0000004f8009f300000de0001ec00006f500006f5000ec000000dd008f30000004f72fa00000000bfcf1000000002ff80000000000bf00000000000af00000000000af00000000000af00000000000af00000000000af0000000" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "0c130c0013000dc00cc0000002e9ae100000004ff300000000000000002ffffffffff3199999999ff1000000008f6000000003fb000000000de1000000009f5000000004fa000000001ee100000000bf4000000006f9000000002fd000000000cf3000000007f7000000002ff9888888824ffffffffff5" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "090f0b010f000008f4000003f7000000db000000000000008dfd8000cfb8bfc06f50007f53600000f80035777fa1cffffffaaf61000faea00002fadc0000bfa6fd89defa05cfe91ca0" + }, + { + "char": "č", + "utf8": "C48D", + "data": "0b0f0b000f001ea01db000003f7bd10000006ff2000000000000000005beeb300009fd99ef5005f90000bf10cd000002930f9000000002f7000000000f9000000000dd000002a305f90000bf0009fd88df500005beeb3000" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "0f0f0d000f000000000db08f2000000000db0bc0000000000db0e60000000000db00000005bfe80db0000008fd9afaeb000003fa0003efb00000be000007fb00000f9000001fb00001f7000000eb00000f8000000fb00000dd000004fb000006f80002dfb000000bfc89fbeb00000006cfc80bb00000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0b0f0c000f0000004f800000001eb00000000ad0000000000000000004beda200008fd99ef4004f90001de00cd000004f60f8000000f81fffffffffa0fb777777740db0000000006f60000cf100afc89ef600005beeb4000" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "0b0f0c000f002f901e9000004f6cc00000007fe1000000000000000004beda200008fd99ef4004f90001de00cd000004f60f8000000f81fffffffffa0fb777777740db0000000006f60000cf100afc89ef600005beeb4000" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "051005011000ce207f402f60000000000008f0008f0008f0008f0008f0008f0008f0008f0008f0008f0008f000" + }, + { + "char": "ň", + "utf8": "C588", + "data": "0a0f0c010f00ae10ae10000dc6f4000002ef600000000000008e19dfc4008fee99ef608fd1000cf18f400004f48f000001f78f000001f78f000001f78f000001f78f000001f78f000001f78f000001f7" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "0b0f0c000f0000003f900000000dc000000008e1000000000000000005beda200009fd99ef5005f90001cf10cd000002f80f9000000db2f7000000bd0f9000000db0dd000002f805f90001cf1009fd89ef500005beea2000" + }, + { + "char": "ř", + "utf8": "C599", + "data": "07100701107f306f40ae5f7000cfa00000000000000008e5dfe08ffa7708f600008f100008f000008f000008f000008f000008f000008f000008f00000" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "090f0a000f01ea01ea0003f7cd000006fe200000000000007dfda200afb89ef21f90001fa1f90000330cfa6300001afffe80000025af81410000dd3f80000dc0afb88df6006cfeb400" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "090f08000f0000004f600cc008f000cc00ba000cc000007fffff20037ee7710000cc0000000cc0000000cc0000000cc0000000cc0000000cc0000000cc0000000bf77100003cff2000" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "0a100c0110000002fb0000000cd10000006f2000000000000000000000008f000001f78f000001f78f000001f78f000001f78f000001f78f000001f78f000001f75f300004f72fc0001df707fd89eef7005cfd91f7" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "0a110c011100000000000004ee4000000d66c000000c67c0000004ee300000000000008f000001f78f000001f78f000001f78f000001f78f000001f78f000001f78f000001f75f300004f72fc0001df707fd89eef7005cfd91f7" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "0b140a0010000000bf200000006f400000001f7000000000000000000000000009f100000af03f600000fa00dc00005f4008f2000ae0002f8000f90000ce004f400006f40ae000000f90f8000000af5f30000004ffd00000000ef8000000009f200000000cc000000479f6000000affa000000" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "091009001005f505f50008f4f800000bfb0000000000000000000005fffffff6277777ef4000005f9000001fd000000cf2000008f6000003fb000001ee100000af4000005fd7777737fffffff7" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_tthoves_regular_21_fr.json b/core/embed/rust/src/ui/translations/fonts/font_tthoves_regular_21_fr.json new file mode 100644 index 000000000..9ce847710 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_tthoves_regular_21_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "0e130e00130001dd0000000000002f80000000000005f300000000000000000000000009ff40000000000fefa0000000005f5bf000000000bf05f500000001f900fb00000007f3009f1000000dd0003f7000003f70000dd000009f100007f30000ee88888af90005ffffffffff000bf00000005f501fa00000000fb07f400000000af1de0000000005f7" + }, + { + "char": "Â", + "utf8": "C382", + "data": "0e130e0013000006fe20000000004f7cd000000001ea01ea000000000000000000000009ff40000000000fefa0000000005f5bf000000000bf05f500000001f900fb00000007f3009f1000000dd0003f7000003f70000dd000009f100007f30000ee88888af90005ffffffffff000bf00000005f501fa00000000fb07f400000000af1de0000000005f7" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "140f14000f0000001ffffffffffff40000007faafc99999992000000dd02f700000000000004f602f70000000000000bf002f70000000000002f8002f70000000000009f2002fc888888700000fb0002ffffffffd00007f40002f700000000000df9888af700000000004ffffffff70000000000be000002f70000000002f8000002f70000000009f2000002fc888888820fc0000002fffffffff4" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "0e130f010f00007cfec81000003dfebacff50002ff500003df500cf20000001ee06f7000000006e5bf100000000000de000000000000ec000000000000ce0000000000009f1000000000006f7000000007f50df20000001ee004fe500003df50003efea9cff60000017cffc81000000000e50000000000008e4000000000001c900000000006fb200000" + }, + { + "char": "È", + "utf8": "C388", + "data": "0b130c0113005f70000000008f2000000000bc00000000000000005fffffffff15fb999999915f5000000005f5000000005f5000000005f5000000005fb888888605ffffffffb05f5000000005f5000000005f5000000005f5000000005f5000000005fb888888815fffffffff10" + }, + { + "char": "É", + "utf8": "C389", + "data": "0b130c0113000000af200000005f500000001e80000000000000005fffffffff15fb999999915f5000000005f5000000005f5000000005f5000000005fb888888605ffffffffb05f5000000005f5000000005f5000000005f5000000005f5000000005fb888888815fffffffff10" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "0b130c01130000cfa0000000ae5f7000007f306f400000000000005fffffffff15fb999999915f5000000005f5000000005f5000000005f5000000005fb888888605ffffffffb05f5000000005f5000000005f5000000005f5000000005f5000000005fb888888815fffffffff10" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "0b130c0113004c508c100005f70af10000000000000000000000005fffffffff15fb999999915f5000000005f5000000005f5000000005f5000000005fb888888605ffffffffb05f5000000005f5000000005f5000000005f5000000005f5000000005fb888888815fffffffff10" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "071306001300bfb0008f5f805f504f50000000005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f5000" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "07130600132c706c23f808f300000000000000005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f500005f5000" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "0f13100113000003ff500000000001eb8f2000000000cd00bd1000000000000000000000007cffd810000003dfebadfe400002ff500004ef5000cf20000001ee006f7000000005f80bf1000000000ed0de0000000000bf0ec00000000009f1de0000000000bf0bf1000000000ed06f7000000005f800cf20000001ee0003fe500003ef500003dfeaadfe400000007cffd8100000" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "0d130e01130008f400000000000be100000000000da00000000000000000008f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af07f30000000ce04f60000000eb01fd0000006f7006fa10005fd00009feb9dfd3000004adfc800000" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "0d130e011300001ef7000000000cc6f400000009e209e200000000000000008f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af07f30000000ce04f60000000eb01fd0000006f7006fa10005fd00009feb9dfd3000004adfc800000" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "0d130e01130006c30ac00000007f40cf0000000000000000000000000000008f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af08f20000000af07f30000000ce04f60000000eb01fd0000006f7006fa10005fd00009feb9dfd3000004adfc800000" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "0d130c00130006c20ab00000008f30de000000000000000000000000000000bf10000000bf12fa0000004f8009f300000de0001ec00006f500006f5000ec000000dd008f30000004f72fa00000000bfcf1000000002ff80000000000bf00000000000af00000000000af00000000000af00000000000af00000000000af0000000" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "150f17010f00029dfea408ffffffffe005ffc9bef98fa999999805fc200009fef200000000ed0000000aff200000007f500000002ff20000000cf000000000cf20000000ed000000000afa8888884ec0000000008ffffffff8de000000000bf20000000af100000000df200000005f700000004ff200000000cf1000000cff2000000003fe30001cfcf20000000004efdacff78fa888888800019dfea308ffffffffe0" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "090f0b010f03fa00000006f400000009e1000000000000008dfd8000cfb8bfc06f50007f53600000f80035777fa1cffffffaaf61000faea00002fadc0000bfa6fd89defa05cfe91ca0" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "090f0b010f000afc000007f5e90004f604f60000000000008dfd8000cfb8bfc06f50007f53600000f80035777fa1cffffffaaf61000faea00002fadc0000bfa6fd89defa05cfe91ca0" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "110b13010b018dfd8003beeb3000dfa8afa3fd99df607f40005ff90000af21300000fe000001f70057888fd777777fa1dffeeefffffffffbaf50000fc00000000ea00003ff10000131cd0000cffb0000df15fc89ef46fd99ef6005cfda2003beec4000" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "0b0f0b000b0005beeb300009fd99ef5005f90000bf10dd000002930f9000000002f7000000000f9000000000dd000002b305f90000bf100afd99ef500006cffb30000001f40000000018e3000000001d700000007fb10000" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "0b0f0c000f001ec0000000003f70000000006f20000000000000000004beda200008fd99ef4004f90001de00cd000004f60f8000000f81fffffffffa0fb777777740db0000000006f60000cf100afc89ef600005beeb4000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "0b0f0c000f0000004f800000001eb00000000ad0000000000000000004beda200008fd99ef4004f90001de00cd000004f60f8000000f81fffffffffa0fb777777740db0000000006f60000cf100afc89ef600005beeb4000" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "0b0f0c000f00007fe10000004f6cc000002e902e900000000000000004beda200008fd99ef4004f90001de00cd000004f60f8000000f81fffffffffa0fb777777740db0000000006f60000cf100afc89ef600005beeb4000" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "0b0f0c000f000ca03c600000fd04f70000000000000000000000000004beda200008fd99ef4004f90001de00cd000004f60f8000000f81fffffffffa0fb777777740db0000000006f60000cf100afc89ef600005beeb4000" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "071006001001df8000bd6f508f208f300000000000000008f000008f000008f000008f000008f000008f000008f000008f000008f000008f000008f000" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "060f06000f8f30de6c30ab000000000000008f00008f00008f00008f00008f00008f00008f00008f00008f00008f00008f00" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "0b0f0c000f00005ff20000003f8bd100001eb01db00000000000000005beda200009fd99ef5005f90001cf10cd000002f80f9000000db2f7000000bd0f9000000db0dd000002f805f90001cf1009fd89ef500005beea2000" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "0a100c011000cf100000001eb000000003f60000000000000000000000008f000001f78f000001f78f000001f78f000001f78f000001f78f000001f78f000001f75f300004f72fc0001df707fd89eef7005cfd91f7" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "0a100c01100004ff3000002e9ae10000dc00cc00000000000000000000008f000001f78f000001f78f000001f78f000001f78f000001f78f000001f78f000001f75f300004f72fc0001df707fd89eef7005cfd91f7" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "0a0f0c010f00bf01fb00009c00c800000000000000000000008f000001f78f000001f78f000001f78f000001f78f000001f78f000001f78f000001f75f300004f72fc0001df707fd89eef7005cfd91f7" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "0b130a000f006f60bf100004c508c00000000000000000000000009f100000af03f600000fa00dc00005f4008f2000ae0002f8000f90000ce004f400006f40ae000000f90f8000000af5f30000004ffd00000000ef8000000009f200000000cc000000479f6000000affa0000000" + }, + { + "char": "œ", + "utf8": "C593", + "data": "130b14000b0006bfe91004beea200009fc8bfd16fd99ef4004f90004fcfa0001de00ce000009ff100004f50f9000004fb000000f81f7000002fffffffffa0f8000003fd777777740dc000007fe0000000006f70002fef80000cf100bfc8afd16fc89ef600006cfe91004cfeb4000" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_unifont_bold_16_cs.json b/core/embed/rust/src/ui/translations/fonts/font_unifont_bold_16_cs.json new file mode 100644 index 000000000..3ed2c93a6 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_unifont_bold_16_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "000008000000" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "000008000000" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "000008000000" + }, + { + "char": "É", + "utf8": "C389", + "data": "000008000000" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "000008000000" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "000008000000" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "000008000000" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "000008000000" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "000008000000" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "000008000000" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "000008000000" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "000008000000" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "000008000000" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "000008000000" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "000008000000" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "000008000000" + }, + { + "char": "č", + "utf8": "C48D", + "data": "000008000000" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "000008000000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "000008000000" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "000008000000" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "000008000000" + }, + { + "char": "ň", + "utf8": "C588", + "data": "000008000000" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "000008000000" + }, + { + "char": "ř", + "utf8": "C599", + "data": "000008000000" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "000008000000" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "000008000000" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "000008000000" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "000008000000" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "000008000000" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "000008000000" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_unifont_bold_16_fr.json b/core/embed/rust/src/ui/translations/fonts/font_unifont_bold_16_fr.json new file mode 100644 index 000000000..62f2eb23f --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_unifont_bold_16_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "000008000000" + }, + { + "char": "Â", + "utf8": "C382", + "data": "000008000000" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "000008000000" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "000008000000" + }, + { + "char": "È", + "utf8": "C388", + "data": "000008000000" + }, + { + "char": "É", + "utf8": "C389", + "data": "000008000000" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "000008000000" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "000008000000" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "000008000000" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "000008000000" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "000008000000" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "000008000000" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "000008000000" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "000008000000" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "000008000000" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "000008000000" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "000008000000" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "000008000000" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "000008000000" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "000008000000" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "000008000000" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "000008000000" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "000008000000" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "000008000000" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "000008000000" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "000008000000" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "000008000000" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "000008000000" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "000008000000" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "000008000000" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "000008000000" + }, + { + "char": "œ", + "utf8": "C593", + "data": "000008000000" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_unifont_regular_16_cs.json b/core/embed/rust/src/ui/translations/fonts/font_unifont_regular_16_cs.json new file mode 100644 index 000000000..8d5444331 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_unifont_regular_16_cs.json @@ -0,0 +1,152 @@ +[ + { + "char": "Á", + "utf8": "C381", + "data": "060e07000e1980003124a187f8618610" + }, + { + "char": "Č", + "utf8": "C48C", + "data": "060e07000e48c0007a186082082185e0" + }, + { + "char": "Ď", + "utf8": "C48E", + "data": "060e07000e918000f228618618618bc0" + }, + { + "char": "É", + "utf8": "C389", + "data": "060e07000e198000fe0820fa082083f0" + }, + { + "char": "Ě", + "utf8": "C49A", + "data": "060e07000e48c000fe0820fa082083f0" + }, + { + "char": "Í", + "utf8": "C38D", + "data": "050e07010e36000f90842108427c" + }, + { + "char": "Ň", + "utf8": "C587", + "data": "060e07000e48c000871c69a659638e10" + }, + { + "char": "Ó", + "utf8": "C393", + "data": "060e07000e1980007a186186186185e0" + }, + { + "char": "Ř", + "utf8": "C598", + "data": "060e07000e48c000fa1861fa48a28610" + }, + { + "char": "Š", + "utf8": "C5A0", + "data": "060e07000e48c0007a186060606185e0" + }, + { + "char": "Ť", + "utf8": "C5A4", + "data": "070e07000e4860000fe20408102040810200" + }, + { + "char": "Ú", + "utf8": "C39A", + "data": "060e07000e19800086186186186185e0" + }, + { + "char": "Ů", + "utf8": "C5AE", + "data": "060e07000e31230086186186186185e0" + }, + { + "char": "Ý", + "utf8": "C39D", + "data": "070e07000e18c00008305122282040810200" + }, + { + "char": "Ž", + "utf8": "C5BD", + "data": "060e07000e48c000fc104210842083f0" + }, + { + "char": "á", + "utf8": "C3A1", + "data": "060c07000c1980007a105f8618dd00" + }, + { + "char": "č", + "utf8": "C48D", + "data": "060c07000c48c0007a182082085e00" + }, + { + "char": "ď", + "utf8": "C48F", + "data": "060e07000e48c0010417638618618dd0" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "060c07000c1980007a187f82085e00" + }, + { + "char": "ě", + "utf8": "C49B", + "data": "060c07000c48c0007a187f82085e00" + }, + { + "char": "í", + "utf8": "C3AD", + "data": "050c07010c36000610842109f0" + }, + { + "char": "ň", + "utf8": "C588", + "data": "060c07000c48c000bb186186186100" + }, + { + "char": "ó", + "utf8": "C3B3", + "data": "060c07000c1980007a186186185e00" + }, + { + "char": "ř", + "utf8": "C599", + "data": "060c07000c48c000bb186082082000" + }, + { + "char": "š", + "utf8": "C5A1", + "data": "060c07000c48c0007a181818185e00" + }, + { + "char": "ť", + "utf8": "C5A5", + "data": "050e07000e498002109f2108420c" + }, + { + "char": "ú", + "utf8": "C3BA", + "data": "060c07000c1980008618618618dd00" + }, + { + "char": "ů", + "utf8": "C5AF", + "data": "060c07000c3123008618618618dd00" + }, + { + "char": "ý", + "utf8": "C3BD", + "data": "060e07000c19800086186185334105e0" + }, + { + "char": "ž", + "utf8": "C5BE", + "data": "060c07000c48c000fc108421083f00" + } +] diff --git a/core/embed/rust/src/ui/translations/fonts/font_unifont_regular_16_fr.json b/core/embed/rust/src/ui/translations/fonts/font_unifont_regular_16_fr.json new file mode 100644 index 000000000..edaacdf08 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fonts/font_unifont_regular_16_fr.json @@ -0,0 +1,162 @@ +[ + { + "char": "À", + "utf8": "C380", + "data": "060e07000e6060003124a187f8618610" + }, + { + "char": "Â", + "utf8": "C382", + "data": "060e07000e3120003124a187f8618610" + }, + { + "char": "Æ", + "utf8": "C386", + "data": "070a07000a3ea2448ff22448913c" + }, + { + "char": "Ç", + "utf8": "C387", + "data": "060c07000a7a186082082185e11800" + }, + { + "char": "È", + "utf8": "C388", + "data": "060e07000e606000fe0820fa082083f0" + }, + { + "char": "É", + "utf8": "C389", + "data": "060e07000e198000fe0820fa082083f0" + }, + { + "char": "Ê", + "utf8": "C38A", + "data": "060e07000e312000fe0820fa082083f0" + }, + { + "char": "Ë", + "utf8": "C38B", + "data": "060e07000e492000fe0820fa082083f0" + }, + { + "char": "Î", + "utf8": "C38E", + "data": "050e07010e64800f90842108427c" + }, + { + "char": "Ï", + "utf8": "C38F", + "data": "050e07010e94800f90842108427c" + }, + { + "char": "Ô", + "utf8": "C394", + "data": "060e07000e3120007a186186186185e0" + }, + { + "char": "Ù", + "utf8": "C399", + "data": "060e07000e60600086186186186185e0" + }, + { + "char": "Û", + "utf8": "C39B", + "data": "060e07000e31200086186186186185e0" + }, + { + "char": "Ü", + "utf8": "C39C", + "data": "060e07000e49200086186186186185e0" + }, + { + "char": "Ÿ", + "utf8": "C5B8", + "data": "070e07000e48900008305122282040810200" + }, + { + "char": "Œ", + "utf8": "C592", + "data": "070a07000a6f224489d2244890dc" + }, + { + "char": "à", + "utf8": "C3A0", + "data": "060c07000c6060007a105f8618dd00" + }, + { + "char": "â", + "utf8": "C3A2", + "data": "060c07000c3120007a105f8618dd00" + }, + { + "char": "æ", + "utf8": "C3A6", + "data": "07080700087d244bf91224be00" + }, + { + "char": "ç", + "utf8": "C3A7", + "data": "060a0700087a182082085e1180" + }, + { + "char": "è", + "utf8": "C3A8", + "data": "060c07000c6060007a187f82085e00" + }, + { + "char": "é", + "utf8": "C3A9", + "data": "060c07000c1980007a187f82085e00" + }, + { + "char": "ê", + "utf8": "C3AA", + "data": "060c07000c3120007a187f82085e00" + }, + { + "char": "ë", + "utf8": "C3AB", + "data": "060c07000c4920007a187f82085e00" + }, + { + "char": "î", + "utf8": "C3AE", + "data": "050c07010c64800610842109f0" + }, + { + "char": "ï", + "utf8": "C3AF", + "data": "050c07010c94800610842109f0" + }, + { + "char": "ô", + "utf8": "C3B4", + "data": "060c07000c3120007a186186185e00" + }, + { + "char": "ù", + "utf8": "C3B9", + "data": "060c07000c6060008618618618dd00" + }, + { + "char": "û", + "utf8": "C3BB", + "data": "060c07000c3120008618618618dd00" + }, + { + "char": "ü", + "utf8": "C3BC", + "data": "060c07000c4920008618618618dd00" + }, + { + "char": "ÿ", + "utf8": "C3BF", + "data": "060e07000c49200086186185334105e0" + }, + { + "char": "œ", + "utf8": "C593", + "data": "07080700086d264cf91224b600" + } +] diff --git a/core/embed/rust/src/ui/translations/fr.json b/core/embed/rust/src/ui/translations/fr.json new file mode 100644 index 000000000..757f44872 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fr.json @@ -0,0 +1,957 @@ +{ + "font": { + "Safe 3": { + "1_FONT_NORMAL": "font_pixeloperator_regular_8_fr.json", + "2_FONT_BOLD": "font_pixeloperator_bold_8_fr.json", + "3_FONT_MONO": "font_pixeloperatormono_regular_8_fr.json", + "4_FONT_BIG": "font_unifont_regular_16_fr.json", + "5_FONT_DEMIBOLD": "font_unifont_bold_16_fr.json" + }, + "T": { + "1_FONT_NORMAL": "font_tthoves_regular_21_fr.json", + "2_FONT_BOLD": "font_tthoves_bold_17_fr.json", + "3_FONT_MONO": "font_robotomono_medium_20_fr.json", + "4_FONT_BIG": null, + "5_FONT_DEMIBOLD": "font_tthoves_demibold_21_fr.json" + } + }, + "header": { + "change_language_prompt": "Changer de langue en francais?", + "change_language_title": "CHANGER LA LANGUE", + "language": "fr", + "version": "2.6.4" + }, + "translations": { + "addr_mismatch": { + "contact_support": "Veuillez contacter le support Trezor à", + "key_mismatch": "Déliachance clé?", + "mismatch": "ADRESSER L'AFFICATION?", + "support_url": "trezor.io/support", + "title": "ADRESSER L'AFFICATION?", + "title_key_mismatch": "DÉLIACHANCE CLÉ?", + "wrong_derication_path": "Mauvais chemin de dérivation pour le compte sélectionné.", + "xpub_mismatch": "Xpub inadéquat?" + }, + "address": { + "address": "Adresse:", + "public_key": "Confirmer la clé publique", + "title_cosigner": "COSIGNATAIRE", + "title_receive_address": "RECEVOIR L'ADRESSE", + "title_yours": "LE VÔTRE" + }, + "address_details": { + "account": "Compte:", + "derivation_path": "Chemin de dérivation:", + "title_receive_address": "RECEVOIR L'ADRESSE", + "title_receiving_to": "RECEVOIR" + }, + "authenticate": { + "confirm_template": "Autoriser l'ordinateur connecté pour confirmer que votre {} est authentique?", + "header": "Authentifier l'appareil" + }, + "auto_lock": { + "change_template": "Verrouiller automatiquement votre trezor après {} d'inactivité?", + "title": "RETARD DE VERROUILLAGE AUTOMATIQUE" + }, + "backup": { + "can_back_up_anytime": "Vous pouvez sauvegarder votre Trezor une fois, à tout moment.", + "it_should_be_backed_up": "Vous devriez sauvegarder votre nouveau portefeuille.", + "it_should_be_backed_up_now": "Il devrait être sauvegardé maintenant!", + "new_wallet_created": "Nouveau portefeuille créé.", + "new_wallet_successfully_created": "Un nouveau portefeuille a créé avec succès.", + "recover_anytime": "Vous pouvez utiliser votre sauvegarde pour récupérer votre portefeuille à tout moment.", + "title_backup_wallet": "PORTEFEUILLE DE SAUVEGARDE", + "title_skip": "SAUTER LA SAUVEGARDE", + "want_to_skip": "Êtes-vous sûr de vouloir sauter la sauvegarde?" + }, + "binance": { + "buy": "Acheter", + "confirm_cancel": "Confirmer l'annulation", + "confirm_input": "Confirmer l'entrée", + "confirm_order": "Confirmer la commande", + "confirm_output": "Confirmer la sortie", + "order_id": "Numéro de commande:", + "pair": "Paire:", + "price": "Prix:", + "quantity": "Quantité:", + "sell": "Vendre", + "sender_address": "Adresse de l'expéditeur:", + "side": "Côté:", + "unknown": "Inconnu" + }, + "bitcoin": { + "commitment_data": "Données d'engagement:", + "confirm_locktime": "Confirmer le verrouillage", + "create_proof_of_ownership": "Voulez-vous créer une preuve de propriété?", + "high_mining_fee_template": "Les frais d'extraction de\n{}\nest de façon inattendue.", + "locktime_no_effect": "Locktime est réglé mais n'aura aucun effet.", + "locktime_set_to": "Locktime réglé sur:", + "locktime_set_to_blockheight": "Locktime réglé sur BlockHeight:", + "lot_of_change_outputs": "Beaucoup de sorties de changement.", + "multiple_accounts": "Plusieurs comptes", + "new_fee_rate": "Nouveau taux de frais:", + "simple_send_of": "Envoi simple de", + "ticket_amount": "Montant de la billette:", + "title_confirm_details": "CONFIRMER LES DÉTAILS", + "title_finalize_transaction": "FINALISER LA TRANSACTION", + "title_high_mining_fee": "FRAIS D'EXTRACTION ÉLEVÉS", + "title_meld_transaction": "TRANSACTION FUSION", + "title_modify_amount": "MODIFIER LE MONTANT", + "title_payjoin": "VERSER DES RENDEZ-VOUS", + "title_proof_of_ownership": "PREUVE DE PROPRIÉTÉ", + "title_purchase_ticket": "BILLET D'ACHAT", + "title_update_transaction": "METTRE À JOUR LA TRANSACTION", + "unknown_path": "Chemin inconnu", + "unknown_transaction": "Transaction inconnue", + "unusually_high_fee": "Frais inhabituellement élevés.", + "unverified_external_inputs": "La transaction contient des entrées externes non vérifiées.", + "valid_signature": "La signature est valide.", + "voting_rights": "Droits de vote sur:" + }, + "buttons": { + "abort": "AVORTER", + "access": "ACCÉDER", + "again": "ENCORE", + "allow": "PERMETTRE", + "back_up": "SAUVEGARDE", + "cancel": "ANNULER", + "change": "CHANGEMENT", + "check": "VÉRIFIER", + "check_again": "REVÉRIFIER", + "close": "FERMER", + "confirm": "CONFIRMER", + "continue": "CONTINUER", + "details": "DÉTAILS", + "enable": "ACTIVER", + "enter": "ENTRER", + "enter_share": "ENTRER LA PART", + "export": "EXPORTER", + "format": "FORMAT", + "go_back": "RETOURNER", + "hold_to_confirm": "TENIR POUR CONFIRMER", + "info": "INFO", + "install": "INSTALLER", + "more_info": "PLUS D'INFORMATIONS", + "ok_i_understand": "OK, JE COMPRENDS", + "purchase": "ACHAT", + "quit": "QUITTER", + "restart": "REDÉMARRAGE", + "retry": "RECOMMENCEZ", + "select": "SÉLECTIONNER", + "set": "ENSEMBLE", + "show_all": "AFFICHER TOUT", + "show_words": "MONTRER DES MOTS", + "skip": "SAUTER", + "try_again": "ESSAYER À NOUVEAU", + "turn_off": "ÉTEINDRE", + "turn_on": "ALLUMER" + }, + "cardano": { + "addr_base": "Base", + "addr_enterprise": "Entreprise", + "addr_legacy": "Héritage", + "addr_pointer": "Aiguille", + "addr_reward": "Récompense", + "address_no_staking": "Adresse - Pas de récompenses de jalonnement.", + "amount": "Montant:", + "amount_burned_decimals_unknown": "Montant brûlé:", + "amount_minted_decimals_unknown": "Montant frappé:", + "amount_sent_decimals_unknown": "Montant envoyé (décimales inconnues):", + "anonymous_pool": "La piscine n'a pas de métadonnées (piscine anonyme)", + "asset_fingerprint": "Empreinte digitale de l'actif:", + "auxiliary_data_hash": "Hachage des données auxiliaires:", + "block": "Bloc", + "catalyst": "Catalyseur", + "certificate": "Certificat", + "certificate_path": "Chemin de certificat", + "change_output": "Modifier la sortie", + "change_output_path": "Modifier le chemin de sortie", + "change_output_staking_path": "Modifier le chemin de la sortie de sortie", + "check_all_items": "Vérifiez soigneusement tous les articles.", + "choose_level_of_details": "Choisissez le niveau de détails:", + "collateral_input_id": "ID d'entrée collatérale:", + "collateral_input_index": "Index d'entrée collatérale:", + "collateral_output_contains_tokens": "La sortie de retour collatérale contient des jetons.", + "collateral_return": "Retour collatéral", + "confirm": "Confirmer:", + "confirm_signing_stake_pool": "Confirmez la signature de l'enregistrement du pool de pommes en tant que propriétaire.", + "confirm_transaction": "Confirmer la transaction", + "confirming_a_multisig_transaction": "Confirmant une transaction multisig.", + "confirming_pool_registration": "Confirmer l'inscription au pool en tant que propriétaire.", + "confirming_transction": "Confirmant une transaction.", + "cost": "Coût", + "credential_mismatch": "Les informations d'identification ne correspondent pas aux informations d'identification de paiement.", + "datum_hash": "Hash de Datum:", + "delegating_to": "Déléguer à:", + "for_account_and_index_template": "pour le compte {} et index {}:", + "for_account_template": "pour le compte {}:", + "for_key_hash": "pour le hachage clé:", + "for_script": "pour script:", + "inline_datum": "Date en ligne", + "input_id": "ID d'entrée:", + "input_index": "Index d'entrée:", + "intro_text_address": "Adresse", + "intro_text_change": "L'adresse suivante est une adresse de changement.C'est", + "intro_text_owned_by_device": "L'adresse suivante appartient à cet appareil.C'est", + "intro_text_registration_payment": "L'adresse de paiement de l'enregistrement des clés de vote appartient à cet appareil.C'est", + "key_hash": "hachage clé", + "margin": "Marge", + "multisig_path": "chemin multi-sige", + "nested_scripts_template": "Contient des scripts imbriqués {}.", + "network": "Réseau:", + "no_output_tx": "La transaction n'a pas de sorties, le réseau ne peut pas être vérifié.", + "nonce": "Nonce:", + "other": "autre", + "path": "chemin", + "pledge": "Gage", + "pointer": "aiguille", + "policy_id": "ID de politique:", + "pool_metadata_hash": "Hash de métadonnées de la piscine:", + "pool_metadata_url": "URL des métadonnées de la piscine:", + "pool_owner": "Propriétaire de la piscine:", + "pool_owner_path": "Path de jalonnement du propriétaire de la piscine", + "pool_reward_account": "Compte de récompense de piscine:", + "reference_input_id": "ID d'entrée de référence:", + "reference_input_index": "Index d'entrée de référence:", + "reference_script": "Script de référence", + "required_signer": "Signataire requis", + "reward": "récompense", + "reward_address": "L'adresse est une adresse de récompense.", + "reward_eligibility_warning": "AVERTISSEMENT: L'adresse n'est pas une adresse de paiement, elle n'est pas éligible pour les récompenses.", + "rewards_go_to": "Les récompenses vont à:", + "script": "scénario", + "script_all": "Tout", + "script_any": "N'importe lequel", + "script_data_hash": "Hash de données de script:", + "script_hash": "Hash de script:", + "script_invalid_before": "Invalide avant", + "script_invalid_hereafter": "Invalide ci-après", + "script_key": "Clé", + "script_n_of_k": "N de k", + "script_reward": "récompense de script", + "sending": "Envoi en cours", + "show_simple": "Montrer simple", + "sign_tx_path_template": "Signer la transaction avec {}:", + "stake_delegation": "Délégation", + "stake_deregistration": "Déréglage des clés", + "stake_pool_registration": "Enregistrement de StagePool", + "stake_pool_registration_pool_id": "Enregistrement du pool de pommes\nID de piscine:", + "stake_registration": "Enregistrement clé des parties", + "staking_key_for_account": "Clé de jalonnement pour le compte", + "to_pool": "À la piscine:", + "token_minting_path": "Path de ponte de jeton", + "total_collateral": "Total collatéral:", + "transaction": "Transaction", + "transaction_contains_minting_or_burning": "La transaction contient la frappe ou la combustion de jetons.", + "transaction_contains_script_address_no_datum": "La sortie de transaction suivante contient une adresse de script, mais ne contient pas de référence.", + "transaction_fee": "Frais de transaction:", + "transaction_id": "Identifiant de transaction:", + "transaction_no_collateral_input": "La transaction ne contient aucune entrée collatérale.Le script Plutus ne pourra pas s'exécuter.", + "transaction_no_script_data_hash": "La transaction ne contient aucun hachage de données de script.Le script Plutus ne pourra pas s'exécuter.", + "transaction_output_contains_tokens": "La sortie de transaction suivante contient des jetons.", + "ttl": "TTL:", + "unknown": "Inconnu", + "unknown_collateral_amount": "Montant collatéral inconnu.", + "unusual_path": "Le chemin est inhabituel.", + "valid_since": "Valable depuis:", + "verify_script": "Vérifier le script", + "vote_key_registration": "Voter l'inscription clé (CIP-36)", + "vote_public_key": "Votez la clé publique:", + "voting_purpose": "Objectif de vote:", + "warning": "Avertissement", + "weight": "Poids:", + "withdrawal_for_address_template": "Confirmer le retrait de l'adresse {}:", + "witness_path": "Chemin du témoin", + "x_of_y_signatures_template": "Nécessite {} des signatures {}." + }, + "coinjoin": { + "access_account": "Accéder à votre compte Coinjoin?", + "do_not_disconnect": "Ne déconnectez pas votre Trezor!", + "max_mining_fee": "Frais d'extraction maximale:", + "max_rounds": "Rounds max:", + "title": "AUTORISER COINJOIN", + "title_do_not_disconnect": "NE DÉCONNECTEZ PAS VOTRE TREZOR!", + "title_progress": "COINJOIN EN COURS", + "waiting_for_others": "Attendre les autres" + }, + "confirm_total": { + "account": "Compte:", + "fee_rate": "Taux de frais:", + "sending_from_account": "Envoi à partir du compte:", + "title_fee": "INFORMATIONS SUR LES FRAIS", + "title_sending_from": "ENVOI DE" + }, + "debug": { + "loading_seed": "Chargement des semences", + "loading_seed_not_recommended": "Le chargement des semences privées n'est pas recommandé." + }, + "device_name": { + "change_template": "Changer le nom du périphérique en {}?", + "title": "NOM DE L'APPAREIL" + }, + "entropy": { + "send": "Voulez-vous vraiment envoyer l'entropie?", + "title": "ENTROPIE INTERNE", + "title_confirm": "CONFIRMER L'ENTROPIE" + }, + "eos": { + "about_to_sign_template": "Vous êtes sur le point de signer {}.", + "account": "Compte:", + "action_name": "Nom d'action:", + "amount": "Montant:", + "arbitrary_data": "Données arbitraires", + "buy_ram": "Acheter Ram", + "bytes": "Octets:", + "cancel_vote": "Annuler le vote", + "checksum": "Vérification:", + "code": "Code:", + "contract": "Contracter:", + "cpu": "CPU:", + "creator": "Créateur", + "delegate": "Déléguer", + "delete_auth": "Supprimer", + "from": "Depuis:", + "link_auth": "Auth lien", + "memo": "Note", + "name": "Nom:", + "net": "FILET:", + "new_account": "Nouveau compte", + "no": "Non", + "owner": "Propriétaire:", + "parent": "Parent:", + "payer": "Payeur:", + "permission": "Autorisation:", + "proxy": "Procuration:", + "receiver": "Destinataire:", + "refund": "Remboursement", + "requirement": "Exigence:", + "sell_ram": "Vendre Ram", + "sender": "Expéditeur:", + "sign_transaction": "Signer une transaction", + "threshold": "Seuil:", + "to": "À:", + "transfer": "Transfert:", + "type": "Taper:", + "undelegate": "Indépendant", + "unlink_auth": "AUTH AUCHANT", + "update_auth": "Mettre à jour l'authe", + "vote_for_producers": "Voter pour les producteurs", + "vote_for_proxy": "Votez pour procuration", + "voter": "Électeur:", + "yes": "Oui" + }, + "ethereum": { + "amount_sent": "Montant envoyé:", + "confirm_fee": "Confirmer les frais", + "contract": "Contracter:", + "data_size_template": "Taille: {} octets", + "gas_limit": "Limite de gaz:", + "gas_price": "Prix du gaz:", + "max_gas_price": "Prix de gaz maximum:", + "name_and_version": "Nom et version", + "new_contract": "nouveau contrat?", + "no_message_field": "Aucun champ de message", + "priority_fee": "Frais de priorité:", + "show_full_array": "Afficher le tableau complet", + "show_full_domain": "Afficher le domaine complet", + "show_full_message": "Afficher le message complet", + "show_full_struct": "Montrer une structure complète", + "sign_eip712": "Signer vraiment les données typées EIP-712?", + "title_confirm_data": "CONFIRMER LES DONNÉES", + "title_confirm_domain": "CONFIRMER LE DOMAINE", + "title_confirm_message": "CONFIRMER LE MESSAGE", + "title_confirm_struct": "CONFIRMER LA STRUCTURE", + "title_confirm_typed_data": "CONFIRMER LES DONNÉES TYPÉES", + "title_signing_address": "ADRESSE DE SIGNATURE", + "units_template": "{} unités", + "unknown_token": "Token inconnu", + "valid_signature": "La signature est valide." + }, + "experimental_mode": { + "enable": "Activer les fonctionnalités expérimentales?", + "only_for_dev": "Seulement pour le développement et les tests bêta!", + "title": "MODE EXPÉRIMENTAL" + }, + "fido": { + "already_registered": "Déjà enregistré", + "device_already_registered": "Cet appareil est déjà enregistré avec cette application.", + "device_already_registered_with_template": "Cet appareil est déjà enregistré avec {}.", + "device_not_registered": "Cet appareil n'est pas enregistré avec cette application.", + "does_not_belong": "Les informations d'identification que vous essayez d'importer\npas appartenir à cet authentificateur.", + "erase_credentials": "Effacer toutes les informations d'identification?", + "export_credentials": "Exporter des informations sur les informations d'identification stockées sur cet appareil?", + "not_registered": "Non enregistré", + "not_registered_with_template": "Cet appareil n'est pas enregistré avec\n{}.", + "please_enable_pin_protection": "Veuillez activer la protection des broches.", + "title_authenticate": "FIDO2 AUTHENTIFIE", + "title_import_credential": "IMPORTER DES DIPLÔMES", + "title_list_credentials": "RÉPERTORIER LES RÉFÉRENCES", + "title_register": "REGISTRE FIDO2", + "title_remove_credential": "SUPPRIMER LES INFORMATIONS D'IDENTIFICATION", + "title_reset": "RÉINITIALISATION DE FIDO2", + "title_u2f_auth": "U2F AUTHENTIFIE", + "title_u2f_register": "REGISTRE U2F", + "title_verify_user": "FIDO2 VÉRIFIEZ L'UTILISATEUR", + "unable_to_verify_user": "Impossible de vérifier l'utilisateur.", + "wanna_erase_credentials": "Voulez-vous vraiment effacer toutes les informations d'identification?" + }, + "firmware_update": { + "title": "MISE À JOUR DU FIRMWARE", + "title_fingerprint": "EMPREINTE DIGITALE FW" + }, + "homescreen": { + "click_to_connect": "Cliquez pour se connecter", + "click_to_unlock": "Cliquez pour déverrouiller", + "title_backup_failed": "LA SAUVEGARDE A ÉCHOUÉ", + "title_backup_needed": "SAUVEGARDE NÉCESSAIRE", + "title_coinjoin_authorized": "COINJOIN AUTORISÉ", + "title_experimental_mode": "MODE EXPÉRIMENTAL", + "title_hold_to_lock": "TENIR À VERROUILLER", + "title_no_usb_connection": "AUCUNE CONNEXION USB", + "title_pin_not_set": "ÉPINGLE NON RÉGLÉE", + "title_seedless": "SANS PÉPINS", + "title_set": "FAIRE UN ÉCRAN D'ACCUEIL" + }, + "inputs": { + "back": "DOS", + "cancel": "ANNULER", + "delete": "SUPPRIMER", + "enter": "ENTRER", + "return": "RETOUR", + "show": "MONTRER", + "space": "ESPACE" + }, + "joint": { + "title": "TRANSACTION CONJOINTE", + "to_the_total_amount": "Au montant total:", + "you_are_contributing": "Vous contribuez:" + }, + "lockscreen": { + "tap_to_connect": "Appuyez pour se connecter", + "tap_to_unlock": "Appuyez pour déverrouiller", + "title_locked": "FERMÉ À CLÉ", + "title_not_connected": "PAS CONNECTÉ" + }, + "misc": { + "decrypt_value": "Decrypt la valeur", + "encrypt_value": "Valeur de crypte", + "title_suite_labeling": "ÉTIQUETAGE DE LA SUITE" + }, + "modify_amount": { + "address": "Adresse:", + "decrease_amount": "Diminue le montant de:", + "increase_amount": "Augmenter le montant de:", + "new_amount": "Nouveau montant:", + "title": "MODIFIER LE MONTANT" + }, + "modify_fee": { + "decrease_fee": "Diminuer les frais de:", + "fee_rate": "Taux de frais:", + "increase_fee": "Augmenter les frais de:", + "new_transaction_fee": "Nouveaux frais de transaction:", + "no_change": "Vos frais n'ont pas changé.", + "title": "MODIFIER LES FRAIS", + "transaction_fee": "Frais de transaction:" + }, + "monero": { + "confirm_export": "Confirmer l'exportation", + "confirm_fee": "Confirmer les frais", + "confirm_ki_sync": "Confirmer Ki Sync", + "confirm_refresh": "Confirmer Rafraîchissement", + "confirm_unlock_time": "Confirmer le temps de déverrouillage", + "hashing_inputs": "Entrées de hachage", + "payment_id": "ID de paiement", + "postprocessing": "Post-traitement ...", + "processing": "Traitement...", + "processing_inputs": "Traitement des entrées", + "processing_outputs": "Traitement des sorties", + "signing": "Signer ...", + "signing_inputs": "Signature des entrées", + "unlock_time_set_template": "Déverrouiller le temps pour cette transaction est défini sur {}", + "wanna_export_tx_der": "Voulez-vous vraiment exporter tx_der\npour TX_proof?", + "wanna_export_tx_key": "Voulez-vous vraiment exporter TX_KEY?", + "wanna_export_watchkey": "Voulez-vous vraiment exporter des informations d'identification de montre uniquement?", + "wanna_start_refresh": "Voulez-vous vraiment\ncommencer à actualiser?", + "wanna_sync_key_images": "Voulez-vous vraiment\nsynchroniser les images clés?" + }, + "nem": { + "absolute": "Absolue", + "activate": "Activer", + "add": "Ajouter", + "confirm_action": "Confirmer l'action", + "confirm_address": "confirmer l'adresse", + "confirm_creation_fee": "Confirmer les frais de création", + "confirm_fee": "Confirmer les frais", + "confirm_mosaic": "Confirmer la mosaïque", + "confirm_multisig_fee": "Confirmer les frais multisig", + "confirm_namespace": "Confirmer l'espace de noms", + "confirm_payload": "Confirmer la charge utile", + "confirm_properties": "Confirmer les propriétés", + "confirm_rental_fee": "Confirmer les frais de location", + "confirm_transfer_of": "Confirmer le transfert de", + "convert_account_to_multisig": "Convertir le compte en compte multisig?", + "cosign_transaction_for": "Transaction cosignale pour", + "cosignatory": "cosignataire", + "create_mosaic": "Créer de la mosaïque", + "create_namespace": "Créer un espace de noms", + "deactivate": "Désactiver", + "decrease": "Diminuer", + "description": "Description:", + "divisibility_and_levy_cannot_be_shown": "La divisibilité et le prélèvement ne peuvent pas être montrés pour des mosaïques inconnues", + "encrypted": "Crypté:", + "final_confirm": "Confirmation finale", + "immutable": "immuable", + "increase": "Augmenter", + "initial_supply": "Alimentation initiale:", + "initiate_transaction_for": "Initier la transaction pour", + "levy_divisibility": "Divisibilité de prélèvement:", + "levy_fee": "Frais de prélèvement:", + "levy_fee_of": "Confirmer les frais de prélèvement en mosaïque de", + "levy_mosaic": "Mosaïque de prélèvement:", + "levy_namespace": "Espace de noms de prélèvement:", + "levy_recipient": "Récipiendaire de prélèvement:", + "levy_type": "Type de prélèvement:", + "modify_supply_for": "Modifier l'offre pour", + "modify_the_number_of_cosignatories_by": "Modifier le nombre de cosignatoires par", + "mutable": "mutable", + "no": "Non", + "of": "de", + "percentile": "centile", + "raw_units_template": "{} unités brutes", + "remote_harvesting": "Récolte à distance?", + "remove": "Retirer", + "set_minimum_cosignatories_to": "Définir des cosignataires minimums sur", + "sign_tx_fee_template": "Signer cette transaction\net payer {}\nPour les frais de réseau?", + "supply_change": "Changement de matériel", + "supply_units_template": "{} fourniture par {} unités entières?", + "transferable": "Transférable?", + "under_namespace": "sous l'espace de noms", + "unencrypted": "Non crypté:", + "unknown_mosaic": "Mosaïque inconnue!", + "yes": "Oui" + }, + "passphrase": { + "access_hidden_wallet": "Accès à un portefeuille caché?", + "always_on_device": "Voulez-vous vraiment entrer en phrase secrète toujours sur l'appareil?", + "from_host_not_shown": "La phrase de passe fournie par l'hôte sera utilisée mais ne sera pas affichée en raison des paramètres de l'appareil.", + "hidden_wallet": "Portefeuille caché", + "hide": "Masquer la phrase de passe provenant de l'hôte?", + "next_screen_will_show_passphrase": "Le prochain écran affichera la phrase secrète.", + "please_enter": "Veuillez saisir votre phrase secrète.", + "revoke_on_device": "Voulez-vous révoquer la phrase secrète sur le réglage de l'appareil?", + "title_confirm": "CONFIRMER LA PHRASE PASSANTE", + "title_enter": "ENTREZ EN PHRASE DE PASSE", + "title_hide": "MASQUER LA PHRASE SECRÈTE", + "title_settings": "PARAMÈTRES DE PHRASE DE PASSE", + "title_source": "SOURCE EN PHRASE DE PASSE", + "turn_off": "Désactiver la protection de la phrase secrète?", + "turn_on": "Activer la protection des phrases de passe?" + }, + "pin": { + "change": "Changer la broche?", + "changed": "Pin a changé.", + "cursor_will_change": "La position du curseur changera entre les entrées pour une sécurité améliorée.", + "diff_from_wipe_code": "PIN doit être différente wipe.", + "disabled": "Protection contre les broches\néteindre.", + "enabled": "Protection contre les broches\nallumé.", + "enter": "Entrer la broche", + "enter_new": "Entrez la nouvelle épingle", + "entered_not_valid": "La broche n'est pas valide.", + "info": "La broche sera nécessaire pour accéder à cet appareil.", + "invalid_pin": "La broche n'est pas valide.", + "last_attempt": "Dernière tentative", + "mismatch": "Les épingles entrées ne pas!", + "pin_mismatch": "Non-concordance", + "please_check_again": "Veuillez vérifier à nouveau.", + "reenter_new": "Rentrez à la nouvelle épingle", + "reenter_to_confirm": "Veuillez réintégrer la broche pour confirmer.", + "should_be_long": "La broche doit avoir 4 à 50 chiffres de long.", + "title_check_pin": "ÉPINGLE", + "title_settings": "PARAMÈTRES DE BROCHE", + "title_wrong_pin": "MAUVAISE ÉPINGLE", + "tries_left": "essaie à gauche", + "turn_off": "Êtes-vous sûr de vouloir désactiver la protection des broches?", + "turn_on": "Activer la protection des broches?", + "wrong_pin": "Mauvaise épingle" + }, + "plurals": { + "contains_x_keys": "clé|clés", + "lock_after_x_hours": "heure|heures", + "lock_after_x_milliseconds": "milliseconde|millisecondes", + "lock_after_x_minutes": "minute|minutes", + "lock_after_x_seconds": "seconde|secondes", + "sign_x_actions": "action|actiones", + "transaction_of_x_operations": "opération|opérations", + "x_groups_needed": "groupe|groupes", + "x_shares_needed": "partage|partages" + }, + "progress": { + "authenticity_check": "Vérification de l'authenticité ...", + "done": "Fait", + "loading_transaction": "Transaction de chargement ...", + "one_second_left": "1 seconde à gauche", + "please_wait": "S'IL VOUS PLAÎT, ATTENDEZ", + "processing": "TRAITEMENT", + "refreshing": "RAFRAÎCHISSANTE", + "signing_transaction": "Transaction de signature ...", + "syncing": "Synchronisation ...", + "x_seconds_left_template": "{} secondes à gauche" + }, + "reboot_to_bootloader": { + "restart": "Voulez-vous redémarrer Trezor en mode chargeur de démarrage?", + "title": "ALLER AU CHARGEUR DE DÉMARRAGE", + "version_by_template": "Version du firmware {}\npar {}" + }, + "recovery": { + "cancel_dry_run": "Annuler le chèque de sauvegarde", + "check_dry_run": "Vérifiez votre sauvegarde?", + "cursor_will_change": "La position du curseur changera entre les entrées pour une sécurité améliorée.", + "dry_run_bip39_valid_match": "Récupération entrée est valide et correspond à celle de l'appareil.", + "dry_run_bip39_valid_mismatch": "Récupération entrée est valide mais ne correspond pas à celle de l'appareil.", + "dry_run_slip39_valid_match": "Récupération entrées sont valides et correspondent qui est actuellement.", + "dry_run_slip39_valid_mismatch": "Récupération entrées sont valides mais ne correspondent qui est actuellement.", + "enter_any_share": "Entrez n'importe quelle part", + "enter_backup": "Entrez votre sauvegarde.", + "enter_different_share": "Veuillez saisir une part différente.", + "enter_share_from_diff_group": "Entrez Share à partir d'un groupe différent.", + "group_num_template": "Groupe {}", + "group_threshold_reached": "Le seuil de groupe atteint.", + "invalid_seed_entered": "Les graines de récupération non valides sont entrées.", + "invalid_share_entered": "Part de récupération non valide entré.", + "more_shares_needed": "Plus d'actions nécessaires", + "num_of_words": "Sélectionnez le nombre de mots dans votre sauvegarde.", + "only_first_n_letters": "Vous n'aurez qu'à sélectionner les 2-4 premières lettres de chaque mot.", + "progress_will_be_lost": "Tous les progrès seront perdus.", + "select_num_of_words": "Sélectionnez le nombre de mots dans votre sauvegarde.", + "share_already_entered": "Partager déjà entré.", + "share_from_another_shamir": "Vous êtes entré dans une part d'une autre sauvegarde Shamir.", + "share_num_template": "Partager {}", + "title": "RÉCUPÉRER LE PORTEFEUILLE", + "title_cancel_dry_run": "ANNULER LE CHÈQUE DE SAUVEGARDE", + "title_cancel_recovery": "ANNULER LA RÉCUPÉRATION", + "title_dry_run": "CHÈQUE DE SAUVEGARDE", + "title_recover": "RÉCUPÉRER LE PORTEFEUILLE", + "title_remaining_shares": "ACTIONS RESTANTES", + "type_word_x_of_y_template": "Type Word {} de {}", + "wallet_recovered": "Le portefeuille a récupéré avec succès", + "wanna_cancel_dry_run": "Êtes-vous sûr que vous souhaitez annuler le chèque de sauvegarde?", + "wanna_cancel_recovery": "Êtes-vous sûr de souhaiter annuler le processus de récupération?", + "word_count_template": "({} mots)", + "word_x_of_y_template": "MOT DE {}", + "x_more_items_starting_template_plural": "{count} plus {plural} startes", + "x_more_shares_needed_template_plural": "{count} plus {plural} nécessaires.", + "x_of_y_entered_template": "{} des actions {} entrées avec succès.", + "you_have_entered": "Vous êtes entré" + }, + "reset": { + "advanced_group_threshold_info": "Le seuil de groupe spécifie le nombre de groupes nécessaires pour récupérer votre portefeuille.", + "all_x_of_y_template": "Tous {} de {} partages", + "any_x_of_y_template": "Toute {} de {} partage", + "button_create": "CRÉER UN PORTEFEUILLE", + "button_recover": "RÉCUPÉRER LE PORTEFEUILLE", + "by_continuing": "En vous poursuivant, vous acceptez les termes et conditions de Trezor Company.", + "check_backup_title": "VÉRIFIEZ LA SAUVEGARDE", + "check_group_share_title_template": "VÉRIFIER G {} - PART {}", + "check_seed_title": "VÉRIFIER LES GRAINES", + "check_share_title_template": "VÉRIFIER LE PARTAGE # {}", + "continue_with_next_share": "Continuez avec la prochaine part.", + "continue_with_share_template": "Continuez avec Share # {}.", + "finished_verifying_group_template": "Vous avez terminé la vérification de vos parts de récupération pour le groupe {}.", + "finished_verifying_seed": "Vous avez fini de vérifier vos graines de récupération.", + "finished_verifying_shares": "Vous avez fini de vérifier vos parts de récupération.", + "group_description": "Un groupe est composé de partages de recouvrement.", + "group_info": "Chaque groupe a un nombre défini d'actions et son propre seuil.Dans les prochaines étapes, vous définissez le nombre d'actions et les seuils.", + "group_share_checked_successfully_template": "Groupe {} - partager {} vérifié avec succès.", + "group_share_title_template": "GROUPE {} - PARTAGER {}", + "more_info_at": "Plus d'informations à", + "need_all_share_template": "Pour la récupération, vous avez besoin de tout {} des actions.", + "need_any_share_template": "Pour la récupération, vous avez besoin de {} des actions.", + "needed_to_form_a_group": "nécessaire pour former un groupe.", + "needed_to_recover_your_wallet": "nécessaire pour récupérer votre portefeuille.", + "never_make_digital_copy": "Ne faites jamais une copie numérique de votre sauvegarde ou téléchargez-la en ligne!", + "num_of_share_holders_template": "{} Les personnes ou les emplacements tiendront chacun une part.", + "num_of_shares_advanced_info_template": "Chaque part de récupération est une séquence de 20 mots.Ensuite, vous choisirez le nombre de seuils d'actions nécessaires pour former le groupe {}.", + "num_of_shares_basic_info": "Chaque part de récupération est une séquence de 20 mots.Ensuite, vous choisissez le nombre d'actions dont vous avez besoin pour récupérer votre portefeuille.", + "num_shares_for_group_template": "Le nombre requis d'actions pour former le groupe {}.", + "number_of_shares_info": "= Nombre total de listes de mots uniques utilisées pour le sauvegarde.", + "one_share": "1 partage", + "only_one_share_will_be_created": "Une seule part sera créée.", + "recovery_seed_title": "GRAINES DE RÉCUPÉRATION", + "recovery_share_title_template": "PARTAGE DE RÉCUPÉRATION # {}", + "required_number_of_groups": "Le nombre requis de groupes pour la récupération.", + "select_correct_word": "Sélectionnez le mot correct pour chaque position.", + "select_word_template": "SÉLECTIONNER {} MOT", + "select_word_x_of_y_template": "Word {} de {}:", + "set_it_to_count_template": "Définissez-le sur {} et vous aurez besoin", + "share_checked_successfully_template": "Partage de récupération # {} vérifié avec succès.", + "share_words_title": "SAUVEGARDE STANDARD", + "slip39_checklist_num_groups": "Nombre de groupes", + "slip39_checklist_num_shares": "Nombre de partages", + "slip39_checklist_set_num_groups": "Définir le nombre de groupes", + "slip39_checklist_set_num_shares": "Définir le nombre d'actions", + "slip39_checklist_set_sizes": "Régler les tailles et les seuils", + "slip39_checklist_set_sizes_longer": "Définir les tailles et les seuils pour chaque groupe", + "slip39_checklist_set_threshold": "Définir le seuil", + "slip39_checklist_title": "LISTE DE CONTRÔLE DE SAUVEGARDE", + "slip39_checklist_write_down": "Notez et vérifiez toutes les partages", + "slip39_checklist_write_down_recovery": "Notez et vérifiez toutes les parts de récupération", + "the_threshold_sets_the_number_of_shares": "Le seuil définit le nombre d'actions", + "threshold_info": "= Nombre minimum de listes de mots uniques utilisées pour la récupération.", + "title_backup_is_done": "LA SAUVEGARDE EST TERMINÉE", + "title_create_wallet": "CRÉER UN PORTEFEUILLE", + "title_create_wallet_shamir": "CRÉER UN PORTEFEUILLE (SHAMIR)", + "title_group_threshold": "SEUIL DE GROUPE", + "title_number_of_groups": "NOMBRE DE GROUPES", + "title_number_of_shares": "NOMBRE DE PARTAGES", + "title_set_group_threshold": "DÉFINIR LE SEUIL DE GROUPE", + "title_set_number_of_groups": "DÉFINIR LE NOMBRE DE GROUPES", + "title_set_number_of_shares": "DÉFINIR LE NOMBRE D'ACTIONS", + "title_set_threshold": "DÉFINIR LE SEUIL", + "to_form_group_template": "pour former le groupe {}.", + "tos_link": "trezor.io/tos", + "total_number_of_shares_in_group_template": "Définissez le nombre total d'actions dans le groupe {}.", + "use_your_backup": "Utilisez votre sauvegarde lorsque vous devez récupérer votre portefeuille.", + "write_down_words_template": "Notez tous les mots {} dans l'ordre.", + "wrong_word_selected": "Mauvais mot sélectionné!", + "you_need_one_share": "Pour la récupération, vous avez besoin de 1 part.", + "your_backup_is_done": "Votre sauvegarde est terminée." + }, + "ripple": { + "confirm_tag": "Confirmer la balise", + "destination_tag_template": "Tag de destination:\n{}" + }, + "rotation": { + "change_template": "Voulez-vous modifier la rotation des périphériques en {}?", + "east": "est", + "north": "nord", + "south": "sud", + "title_change": "CHANGER LA ROTATION", + "west": "Ouest" + }, + "safety_checks": { + "approve_unsafe_always": "Trezor vous permettra d'approuver certaines actions qui pourraient être dangereuses.", + "approve_unsafe_temporary": "Trezor vous permettra temporairement d'approuver certaines actions qui pourraient être dangereuses.", + "enforce_strict": "Voulez-vous vraiment appliquer des contrôles de sécurité stricts (recommandés)?", + "title": "CONTRÔLES DE SÉCURITÉ", + "title_safety_override": "REMPLIR DE LA SÉCURITÉ" + }, + "sd_card": { + "all_data_will_be_lost": "Toutes les données sur la carte SD seront perdues.", + "card_required": "Carte SD requise.", + "disable": "Voulez-vous vraiment supprimer la protection des cartes SD de votre appareil?", + "disabled": "Vous avez une protection SD handicapée avec succès.", + "enable": "Voulez-vous vraiment sécuriser votre appareil avec une protection de carte SD?", + "enabled": "Vous avez réussi à activer la protection SD.", + "error": "Erreur de carte SD", + "format_card": "Carte SD de format", + "insert_correct_card": "Veuillez insérer la carte SD correcte pour cet appareil.", + "please_insert": "Veuillez insérer votre carte SD.", + "please_unplug_and_insert": "Veuillez débrancher l'appareil et insérer votre carte SD.", + "problem_accessing": "Il y avait un problème à accéder à la carte SD.", + "refresh": "Voulez-vous vraiment remplacer le secret de la carte SD actuel par un secret nouvellement généré?", + "refreshed": "Vous avez réussi à rafraîchir la protection SD.", + "restart": "Voulez-vous redémarrer Trezor en mode chargeur de démarrage?", + "title": "PROTECTION DES CARTES SD", + "title_problem": "PROBLÈME DE CARTE SD", + "unknown_filesystem": "Système de fichiers inconnu", + "unplug_and_insert_correct": "Veuillez débrancher l'appareil et insérer la carte SD correcte.", + "use_different_card": "Utilisez une carte différente ou formate la carte SD au système de fichiers FAT32", + "wanna_format": "Voulez-vous vraiment formater la carte SD?", + "wrong_sd_card": "Mauvaise carte SD" + }, + "send": { + "address_path": "chemin d'adresse", + "amount": "Montant:", + "confirm_sending": "Confirm_sending", + "from_multiple_accounts": "Envoi à partir de plusieurs comptes.", + "including_fee": "Y compris les frais:", + "maximum_fee": "Frais maximaux:", + "receiving_to_multisig": "Recevoir une adresse multisig.", + "title_amount": "MONTANT", + "title_confirm_sending": "CONFIRMER L'ENVOI", + "title_joint_transaction": "TRANSACTION CONJOINTE", + "title_receiving_to": "RECEVOIR", + "title_recipient": "DESTINATAIRE", + "title_sending": "ENVOI EN COURS", + "title_sending_amount": "MONTANT", + "title_sending_to": "ENVOI À", + "to_the_total_amount": "Au montant total:", + "total_amount": "Montant total:", + "transaction_id": "Identifiant de transaction:", + "you_are_contributing": "Vous contribuez:" + }, + "share_words": { + "words_in_order": "mots dans l'ordre.", + "wrote_down_all": "J'ai écrit tout" + }, + "sign_message": { + "bytes_template": "{} Octets", + "confirm_address": "Adresse de signature", + "confirm_message": "Confirmer le message", + "message_size": "Taille du message:", + "verify_address": "Vérifier l'adresse" + }, + "stellar": { + "account": "Compte", + "account_merge": "Fusion du compte", + "account_thresholds": "Seuils de compte", + "add_signer": "Ajouter le signataire", + "add_trust": "Ajouter la confiance", + "all_will_be_sent_to": "Tous les xlm seront envoyés à:", + "allow_trust": "Autoriser la confiance", + "asset": "Actif", + "bump_sequence": "Séquence de bosse", + "buying": "Achat:", + "clear_data": "Effacer les données", + "clear_flags": "Drapeaux clairs", + "confirm_issuer": "Confirmer l'émetteur", + "confirm_memo": "Confirmer la note", + "confirm_network": "Confirmer le réseau", + "confirm_operation": "Confirmation de l'opération", + "confirm_stellar": "Confirmer stellaire", + "confirm_timebounds": "Confirmer les temps", + "create_account": "Créer un compte", + "debited_amount": "Montant débité", + "delete": "Supprimer", + "delete_passive_offer": "Supprimer l'offre passive", + "delete_trust": "Supprimer la confiance", + "destination": "Destination:", + "exchanges_require_memo": "IMPORTANT: De nombreux échanges nécessitent un mémo lors du dépôt", + "final_confirm": "Confirmation finale", + "hash": "Hacher:", + "high": "Haut:", + "home_domain": "Domaine de la maison", + "inflation": "Inflation", + "initial_balance": "Balance initiale", + "initialize_signing_with": "Initialiser la signature avec", + "issuer_template": "{} émetteur:", + "key": "Clé:", + "limit": "Limite:", + "low": "Faible:", + "master_weight": "Poids maître:", + "medium": "Moyen:", + "new_offer": "Nouvelle offre", + "new_passive_offer": "Nouvelle offre passive", + "no_memo_set": "Pas de mémo!", + "no_restriction": "[pas de réstriction]", + "on_network_template": "La transaction est sur {}", + "path_pay": "PATH PAY", + "path_pay_at_least": "Le chemin payant le moins", + "pay": "Payer:", + "pay_at_most": "Payer tout au plus:", + "preauth_transaction": "Transaction pré-UTH:", + "price_per_template": "Prix par {}:", + "private_network": "Réseau privé", + "remove_signer": "Supprimer le signataire", + "revoke_trust": "Révoquer la confiance", + "selling": "Vente:", + "set_data": "Définir les données", + "set_flags": "Définir les drapeaux", + "set_sequence_to_template": "Définir la séquence sur {}?", + "sign_tx_count_template": "Signer cette transaction composée de {}", + "sign_tx_fee_template": "et payer {}\npour les frais?", + "source_account": "Compte source:", + "testnet_network": "réseau Testnet", + "trusted_account": "Compte de confiance", + "update": "Mise à jour", + "valid_from": "Valide de (UTC)", + "valid_to": "Valable à (UTC)", + "value_sha256": "Valeur (SHA-256):", + "wanna_clean_value_key_template": "Voulez-vous effacer la clé de valeur {}?", + "your_account": "Votre compte" + }, + "tezos": { + "address": "Adresse:", + "amount": "Montant:", + "baker_address": "Adresse boulanger:", + "balance": "Équilibre:", + "ballot": "Bulletin de vote:", + "confirm_delegation": "Confirmer la délégation", + "confirm_origination": "Confirmer l'origine", + "delegator": "Délégant:", + "fee": "Frais:", + "proposal": "Proposition", + "register_delegate": "Enregistrer le délégué", + "remove_delegation": "Supprimer la délégation", + "submit_ballot": "Soumettre un bulletin de vote", + "submit_proposal": "Soumettre la proposition", + "submit_proposals": "Soumettre des propositions" + }, + "tutorial": { + "middle_click": "Appuyez à gauche et à droite à la même chose. Il est temps de confirmer.", + "press_and_hold": "Appuyez et maintenez le bouton droit pour approuver des opérations.", + "ready_to_use": "Tu es prêt à. Utilisez Trezor.", + "scroll_down": "Appuyez à droite pour faire défiler vers le bas pour lire tout le contenu lorsque le texte ne tient pas. Appuyez à gauche pour faire défiler vers le haut.", + "sure_you_want_skip": "Est-tu sûr que tu. Vous voulez sauter le tutoriel?", + "title_hello": "BONJOUR", + "title_screen_scroll": "PARCHEMIN D'ÉCRAN", + "title_skip": "PASSER LE TUTORIEL", + "title_tutorial_complete": "TUTORIEL COMPLET", + "use_trezor": "Utiliser Trezor par. Cliquez sur les boutons gauche et droit. Continuer tout droit.", + "welcome_press_right": "Bienvenue à Trezor. Appuyez sur le droit de continuer." + }, + "u2f": { + "get": "Augmenter et récupérer le compteur U2F?", + "set_template": "Définissez le compteur U2F sur {}?", + "title_get": "OBTENEZ UN COMPTEUR U2F", + "title_set": "DÉFINIR LE COMPTEUR U2F" + }, + "wipe": { + "info": "Toutes les données seront effacées.", + "title": "EFFACER L'APPAREIL", + "want_to_wipe": "Voulez-vous vraiment essuyer l'appareil?" + }, + "wipe_code": { + "change": "Changer de code d'essuyage?", + "changed": "Effacer le code modifié.", + "diff_from_pin": "Le code d'essuyage doit être différent de votre broche.\nVeuillez réessayer.", + "disabled": "Essuyez le code désactivé.", + "enabled": "Effacer le code activé.", + "enter_new": "Entrez le nouveau code d'essuyage", + "info": "Le code peut pour effacer toutes les données.", + "invalid": "Code d'essuyage non valide", + "mismatch": "Les codes d'essuyage entrés ne correspondent pas!", + "reenter": "Rentrer le code d'essuyage", + "reenter_to_confirm": "Veuillez réintégrer le code d'essuyage pour confirmer.", + "title_check": "VÉRIFIER LE CODE D'ESSUYAGE", + "title_invalid": "CODE D'ESSUYAGE NON VALIDE", + "title_settings": "EFFACER DU CODE", + "turn_off": "Désactiver la protection du code d'essuyage?", + "turn_on": "Activer la protection du code d'essuyage?", + "wipe_code_mismatch": "Effacer le décalage du code" + }, + "word_count": { + "title": "NOMBRE DE MOTS" + }, + "words": { + "are_you_sure": "Es-tu sûr?", + "array_of": "Tableau de", + "buying": "Achat", + "confirm": "Confirmer", + "contains": "contient", + "continue_anyway": "Continuer de toute façon?", + "continue_with": "Continue avec", + "error": "Erreur", + "from": "depuis", + "keep_it_safe": "Garde-le en sécurité!", + "know_what_your_doing": "Continuez uniquement si vous savez ce que vous faites!", + "my_trezor": "Mon Trezor", + "outputs": "les sorties", + "please_check_again": "Veuillez vérifier à nouveau", + "please_try_again": "Veuillez réessayer", + "really_wanna": "Voulez-vous vraiment", + "sign": "Signe", + "title_check": "VÉRIFIER", + "title_group": "GROUPE", + "title_information": "INFORMATION", + "title_remember": "SOUVIENS-TOI", + "title_share": "PARTAGER", + "title_shares": "ACTIONS", + "title_success": "SUCCÈS", + "title_summary": "RÉSUMÉ", + "title_threshold": "SEUIL", + "unknown": "Inconnu", + "warning": "Avertissement" + } + } +} diff --git a/core/embed/rust/src/ui/translations/fr.rs b/core/embed/rust/src/ui/translations/fr.rs new file mode 100644 index 000000000..a676e87f2 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fr.rs @@ -0,0 +1,841 @@ +//! generated from fr.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +// NOTE: not used as a code, only for +// documentation purposes + +use super::general::TranslationsGeneral; + +#[rustfmt::skip] +pub const TRANSLATIONS: TranslationsGeneral = TranslationsGeneral { + addr_mismatch__contact_support: "Veuillez contacter le support Trezor à", + addr_mismatch__key_mismatch: "Déliachance clé?", + addr_mismatch__mismatch: "ADRESSER L'AFFICATION?", + addr_mismatch__support_url: "trezor.io/support", + addr_mismatch__title: "ADRESSER L'AFFICATION?", + addr_mismatch__title_key_mismatch: "DÉLIACHANCE CLÉ?", + addr_mismatch__wrong_derication_path: "Mauvais chemin de dérivation pour le compte sélectionné.", + addr_mismatch__xpub_mismatch: "Xpub inadéquat?", + address__address: "Adresse:", + address__public_key: "Confirmer la clé publique", + address__title_cosigner: "COSIGNATAIRE", + address__title_receive_address: "RECEVOIR L'ADRESSE", + address__title_yours: "LE VÔTRE", + address_details__account: "Compte:", + address_details__derivation_path: "Chemin de dérivation:", + address_details__title_receive_address: "RECEVOIR L'ADRESSE", + address_details__title_receiving_to: "RECEVOIR", + authenticate__confirm_template: "Autoriser l'ordinateur connecté pour confirmer que votre {} est authentique?", + authenticate__header: "Authentifier l'appareil", + auto_lock__change_template: "Verrouiller automatiquement votre trezor après {} d'inactivité?", + auto_lock__title: "RETARD DE VERROUILLAGE AUTOMATIQUE", + backup__can_back_up_anytime: "Vous pouvez sauvegarder votre Trezor une fois, à tout moment.", + backup__it_should_be_backed_up: "Vous devriez sauvegarder votre nouveau portefeuille.", + backup__it_should_be_backed_up_now: "Il devrait être sauvegardé maintenant!", + backup__new_wallet_created: "Nouveau portefeuille créé.", + backup__new_wallet_successfully_created: "Un nouveau portefeuille a créé avec succès.", + backup__recover_anytime: "Vous pouvez utiliser votre sauvegarde pour récupérer votre portefeuille à tout moment.", + backup__title_backup_wallet: "PORTEFEUILLE DE SAUVEGARDE", + backup__title_skip: "SAUTER LA SAUVEGARDE", + backup__want_to_skip: "Êtes-vous sûr de vouloir sauter la sauvegarde?", + binance__buy: "Acheter", + binance__confirm_cancel: "Confirmer l'annulation", + binance__confirm_input: "Confirmer l'entrée", + binance__confirm_order: "Confirmer la commande", + binance__confirm_output: "Confirmer la sortie", + binance__order_id: "Numéro de commande:", + binance__pair: "Paire:", + binance__price: "Prix:", + binance__quantity: "Quantité:", + binance__sell: "Vendre", + binance__sender_address: "Adresse de l'expéditeur:", + binance__side: "Côté:", + binance__unknown: "Inconnu", + bitcoin__commitment_data: "Données d'engagement:", + bitcoin__confirm_locktime: "Confirmer le verrouillage", + bitcoin__create_proof_of_ownership: "Voulez-vous créer une preuve de propriété?", + bitcoin__high_mining_fee_template: "Les frais d'extraction de\n{}\nest de façon inattendue.", + bitcoin__locktime_no_effect: "Locktime est réglé mais n'aura aucun effet.", + bitcoin__locktime_set_to: "Locktime réglé sur:", + bitcoin__locktime_set_to_blockheight: "Locktime réglé sur BlockHeight:", + bitcoin__lot_of_change_outputs: "Beaucoup de sorties de changement.", + bitcoin__multiple_accounts: "Plusieurs comptes", + bitcoin__new_fee_rate: "Nouveau taux de frais:", + bitcoin__simple_send_of: "Envoi simple de", + bitcoin__ticket_amount: "Montant de la billette:", + bitcoin__title_confirm_details: "CONFIRMER LES DÉTAILS", + bitcoin__title_finalize_transaction: "FINALISER LA TRANSACTION", + bitcoin__title_high_mining_fee: "FRAIS D'EXTRACTION ÉLEVÉS", + bitcoin__title_meld_transaction: "TRANSACTION FUSION", + bitcoin__title_modify_amount: "MODIFIER LE MONTANT", + bitcoin__title_payjoin: "VERSER DES RENDEZ-VOUS", + bitcoin__title_proof_of_ownership: "PREUVE DE PROPRIÉTÉ", + bitcoin__title_purchase_ticket: "BILLET D'ACHAT", + bitcoin__title_update_transaction: "METTRE À JOUR LA TRANSACTION", + bitcoin__unknown_path: "Chemin inconnu", + bitcoin__unknown_transaction: "Transaction inconnue", + bitcoin__unusually_high_fee: "Frais inhabituellement élevés.", + bitcoin__unverified_external_inputs: "La transaction contient des entrées externes non vérifiées.", + bitcoin__valid_signature: "La signature est valide.", + bitcoin__voting_rights: "Droits de vote sur:", + buttons__abort: "AVORTER", + buttons__access: "ACCÉDER", + buttons__again: "ENCORE", + buttons__allow: "PERMETTRE", + buttons__back_up: "SAUVEGARDE", + buttons__cancel: "ANNULER", + buttons__change: "CHANGEMENT", + buttons__check: "VÉRIFIER", + buttons__check_again: "REVÉRIFIER", + buttons__close: "FERMER", + buttons__confirm: "CONFIRMER", + buttons__continue: "CONTINUER", + buttons__details: "DÉTAILS", + buttons__enable: "ACTIVER", + buttons__enter: "ENTRER", + buttons__enter_share: "ENTRER LA PART", + buttons__export: "EXPORTER", + buttons__format: "FORMAT", + buttons__go_back: "RETOURNER", + buttons__hold_to_confirm: "TENIR POUR CONFIRMER", + buttons__info: "INFO", + buttons__install: "INSTALLER", + buttons__more_info: "PLUS D'INFORMATIONS", + buttons__ok_i_understand: "OK, JE COMPRENDS", + buttons__purchase: "ACHAT", + buttons__quit: "QUITTER", + buttons__restart: "REDÉMARRAGE", + buttons__retry: "RECOMMENCEZ", + buttons__select: "SÉLECTIONNER", + buttons__set: "ENSEMBLE", + buttons__show_all: "AFFICHER TOUT", + buttons__show_words: "MONTRER DES MOTS", + buttons__skip: "SAUTER", + buttons__try_again: "ESSAYER À NOUVEAU", + buttons__turn_off: "ÉTEINDRE", + buttons__turn_on: "ALLUMER", + cardano__addr_base: "Base", + cardano__addr_enterprise: "Entreprise", + cardano__addr_legacy: "Héritage", + cardano__addr_pointer: "Aiguille", + cardano__addr_reward: "Récompense", + cardano__address_no_staking: "Adresse - Pas de récompenses de jalonnement.", + cardano__amount: "Montant:", + cardano__amount_burned_decimals_unknown: "Montant brûlé:", + cardano__amount_minted_decimals_unknown: "Montant frappé:", + cardano__amount_sent_decimals_unknown: "Montant envoyé (décimales inconnues):", + cardano__anonymous_pool: "La piscine n'a pas de métadonnées (piscine anonyme)", + cardano__asset_fingerprint: "Empreinte digitale de l'actif:", + cardano__auxiliary_data_hash: "Hachage des données auxiliaires:", + cardano__block: "Bloc", + cardano__catalyst: "Catalyseur", + cardano__certificate: "Certificat", + cardano__certificate_path: "Chemin de certificat", + cardano__change_output: "Modifier la sortie", + cardano__change_output_path: "Modifier le chemin de sortie", + cardano__change_output_staking_path: "Modifier le chemin de la sortie de sortie", + cardano__check_all_items: "Vérifiez soigneusement tous les articles.", + cardano__choose_level_of_details: "Choisissez le niveau de détails:", + cardano__collateral_input_id: "ID d'entrée collatérale:", + cardano__collateral_input_index: "Index d'entrée collatérale:", + cardano__collateral_output_contains_tokens: "La sortie de retour collatérale contient des jetons.", + cardano__collateral_return: "Retour collatéral", + cardano__confirm: "Confirmer:", + cardano__confirm_signing_stake_pool: "Confirmez la signature de l'enregistrement du pool de pommes en tant que propriétaire.", + cardano__confirm_transaction: "Confirmer la transaction", + cardano__confirming_a_multisig_transaction: "Confirmant une transaction multisig.", + cardano__confirming_pool_registration: "Confirmer l'inscription au pool en tant que propriétaire.", + cardano__confirming_transction: "Confirmant une transaction.", + cardano__cost: "Coût", + cardano__credential_mismatch: "Les informations d'identification ne correspondent pas aux informations d'identification de paiement.", + cardano__datum_hash: "Hash de Datum:", + cardano__delegating_to: "Déléguer à:", + cardano__for_account_and_index_template: "pour le compte {} et index {}:", + cardano__for_account_template: "pour le compte {}:", + cardano__for_key_hash: "pour le hachage clé:", + cardano__for_script: "pour script:", + cardano__inline_datum: "Date en ligne", + cardano__input_id: "ID d'entrée:", + cardano__input_index: "Index d'entrée:", + cardano__intro_text_address: "Adresse", + cardano__intro_text_change: "L'adresse suivante est une adresse de changement.C'est", + cardano__intro_text_owned_by_device: "L'adresse suivante appartient à cet appareil.C'est", + cardano__intro_text_registration_payment: "L'adresse de paiement de l'enregistrement des clés de vote appartient à cet appareil.C'est", + cardano__key_hash: "hachage clé", + cardano__margin: "Marge", + cardano__multisig_path: "chemin multi-sige", + cardano__nested_scripts_template: "Contient des scripts imbriqués {}.", + cardano__network: "Réseau:", + cardano__no_output_tx: "La transaction n'a pas de sorties, le réseau ne peut pas être vérifié.", + cardano__nonce: "Nonce:", + cardano__other: "autre", + cardano__path: "chemin", + cardano__pledge: "Gage", + cardano__pointer: "aiguille", + cardano__policy_id: "ID de politique:", + cardano__pool_metadata_hash: "Hash de métadonnées de la piscine:", + cardano__pool_metadata_url: "URL des métadonnées de la piscine:", + cardano__pool_owner: "Propriétaire de la piscine:", + cardano__pool_owner_path: "Path de jalonnement du propriétaire de la piscine", + cardano__pool_reward_account: "Compte de récompense de piscine:", + cardano__reference_input_id: "ID d'entrée de référence:", + cardano__reference_input_index: "Index d'entrée de référence:", + cardano__reference_script: "Script de référence", + cardano__required_signer: "Signataire requis", + cardano__reward: "récompense", + cardano__reward_address: "L'adresse est une adresse de récompense.", + cardano__reward_eligibility_warning: "AVERTISSEMENT: L'adresse n'est pas une adresse de paiement, elle n'est pas éligible pour les récompenses.", + cardano__rewards_go_to: "Les récompenses vont à:", + cardano__script: "scénario", + cardano__script_all: "Tout", + cardano__script_any: "N'importe lequel", + cardano__script_data_hash: "Hash de données de script:", + cardano__script_hash: "Hash de script:", + cardano__script_invalid_before: "Invalide avant", + cardano__script_invalid_hereafter: "Invalide ci-après", + cardano__script_key: "Clé", + cardano__script_n_of_k: "N de k", + cardano__script_reward: "récompense de script", + cardano__sending: "Envoi en cours", + cardano__show_simple: "Montrer simple", + cardano__sign_tx_path_template: "Signer la transaction avec {}:", + cardano__stake_delegation: "Délégation", + cardano__stake_deregistration: "Déréglage des clés", + cardano__stake_pool_registration: "Enregistrement de StagePool", + cardano__stake_pool_registration_pool_id: "Enregistrement du pool de pommes\nID de piscine:", + cardano__stake_registration: "Enregistrement clé des parties", + cardano__staking_key_for_account: "Clé de jalonnement pour le compte", + cardano__to_pool: "À la piscine:", + cardano__token_minting_path: "Path de ponte de jeton", + cardano__total_collateral: "Total collatéral:", + cardano__transaction: "Transaction", + cardano__transaction_contains_minting_or_burning: "La transaction contient la frappe ou la combustion de jetons.", + cardano__transaction_contains_script_address_no_datum: "La sortie de transaction suivante contient une adresse de script, mais ne contient pas de référence.", + cardano__transaction_fee: "Frais de transaction:", + cardano__transaction_id: "Identifiant de transaction:", + cardano__transaction_no_collateral_input: "La transaction ne contient aucune entrée collatérale.Le script Plutus ne pourra pas s'exécuter.", + cardano__transaction_no_script_data_hash: "La transaction ne contient aucun hachage de données de script.Le script Plutus ne pourra pas s'exécuter.", + cardano__transaction_output_contains_tokens: "La sortie de transaction suivante contient des jetons.", + cardano__ttl: "TTL:", + cardano__unknown: "Inconnu", + cardano__unknown_collateral_amount: "Montant collatéral inconnu.", + cardano__unusual_path: "Le chemin est inhabituel.", + cardano__valid_since: "Valable depuis:", + cardano__verify_script: "Vérifier le script", + cardano__vote_key_registration: "Voter l'inscription clé (CIP-36)", + cardano__vote_public_key: "Votez la clé publique:", + cardano__voting_purpose: "Objectif de vote:", + cardano__warning: "Avertissement", + cardano__weight: "Poids:", + cardano__withdrawal_for_address_template: "Confirmer le retrait de l'adresse {}:", + cardano__witness_path: "Chemin du témoin", + cardano__x_of_y_signatures_template: "Nécessite {} des signatures {}.", + coinjoin__access_account: "Accéder à votre compte Coinjoin?", + coinjoin__do_not_disconnect: "Ne déconnectez pas votre Trezor!", + coinjoin__max_mining_fee: "Frais d'extraction maximale:", + coinjoin__max_rounds: "Rounds max:", + coinjoin__title: "AUTORISER COINJOIN", + coinjoin__title_do_not_disconnect: "NE DÉCONNECTEZ PAS VOTRE TREZOR!", + coinjoin__title_progress: "COINJOIN EN COURS", + coinjoin__waiting_for_others: "Attendre les autres", + confirm_total__account: "Compte:", + confirm_total__fee_rate: "Taux de frais:", + confirm_total__sending_from_account: "Envoi à partir du compte:", + confirm_total__title_fee: "INFORMATIONS SUR LES FRAIS", + confirm_total__title_sending_from: "ENVOI DE", + debug__loading_seed: "Chargement des semences", + debug__loading_seed_not_recommended: "Le chargement des semences privées n'est pas recommandé.", + device_name__change_template: "Changer le nom du périphérique en {}?", + device_name__title: "NOM DE L'APPAREIL", + entropy__send: "Voulez-vous vraiment envoyer l'entropie?", + entropy__title: "ENTROPIE INTERNE", + entropy__title_confirm: "CONFIRMER L'ENTROPIE", + eos__about_to_sign_template: "Vous êtes sur le point de signer {}.", + eos__account: "Compte:", + eos__action_name: "Nom d'action:", + eos__amount: "Montant:", + eos__arbitrary_data: "Données arbitraires", + eos__buy_ram: "Acheter Ram", + eos__bytes: "Octets:", + eos__cancel_vote: "Annuler le vote", + eos__checksum: "Vérification:", + eos__code: "Code:", + eos__contract: "Contracter:", + eos__cpu: "CPU:", + eos__creator: "Créateur", + eos__delegate: "Déléguer", + eos__delete_auth: "Supprimer", + eos__from: "Depuis:", + eos__link_auth: "Auth lien", + eos__memo: "Note", + eos__name: "Nom:", + eos__net: "FILET:", + eos__new_account: "Nouveau compte", + eos__no: "Non", + eos__owner: "Propriétaire:", + eos__parent: "Parent:", + eos__payer: "Payeur:", + eos__permission: "Autorisation:", + eos__proxy: "Procuration:", + eos__receiver: "Destinataire:", + eos__refund: "Remboursement", + eos__requirement: "Exigence:", + eos__sell_ram: "Vendre Ram", + eos__sender: "Expéditeur:", + eos__sign_transaction: "Signer une transaction", + eos__threshold: "Seuil:", + eos__to: "À:", + eos__transfer: "Transfert:", + eos__type: "Taper:", + eos__undelegate: "Indépendant", + eos__unlink_auth: "AUTH AUCHANT", + eos__update_auth: "Mettre à jour l'authe", + eos__vote_for_producers: "Voter pour les producteurs", + eos__vote_for_proxy: "Votez pour procuration", + eos__voter: "Électeur:", + eos__yes: "Oui", + ethereum__amount_sent: "Montant envoyé:", + ethereum__confirm_fee: "Confirmer les frais", + ethereum__contract: "Contracter:", + ethereum__data_size_template: "Taille: {} octets", + ethereum__gas_limit: "Limite de gaz:", + ethereum__gas_price: "Prix du gaz:", + ethereum__max_gas_price: "Prix de gaz maximum:", + ethereum__name_and_version: "Nom et version", + ethereum__new_contract: "nouveau contrat?", + ethereum__no_message_field: "Aucun champ de message", + ethereum__priority_fee: "Frais de priorité:", + ethereum__show_full_array: "Afficher le tableau complet", + ethereum__show_full_domain: "Afficher le domaine complet", + ethereum__show_full_message: "Afficher le message complet", + ethereum__show_full_struct: "Montrer une structure complète", + ethereum__sign_eip712: "Signer vraiment les données typées EIP-712?", + ethereum__title_confirm_data: "CONFIRMER LES DONNÉES", + ethereum__title_confirm_domain: "CONFIRMER LE DOMAINE", + ethereum__title_confirm_message: "CONFIRMER LE MESSAGE", + ethereum__title_confirm_struct: "CONFIRMER LA STRUCTURE", + ethereum__title_confirm_typed_data: "CONFIRMER LES DONNÉES TYPÉES", + ethereum__title_signing_address: "ADRESSE DE SIGNATURE", + ethereum__units_template: "{} unités", + ethereum__unknown_token: "Token inconnu", + ethereum__valid_signature: "La signature est valide.", + experimental_mode__enable: "Activer les fonctionnalités expérimentales?", + experimental_mode__only_for_dev: "Seulement pour le développement et les tests bêta!", + experimental_mode__title: "MODE EXPÉRIMENTAL", + fido__already_registered: "Déjà enregistré", + fido__device_already_registered: "Cet appareil est déjà enregistré avec cette application.", + fido__device_already_registered_with_template: "Cet appareil est déjà enregistré avec {}.", + fido__device_not_registered: "Cet appareil n'est pas enregistré avec cette application.", + fido__does_not_belong: "Les informations d'identification que vous essayez d'importer\npas appartenir à cet authentificateur.", + fido__erase_credentials: "Effacer toutes les informations d'identification?", + fido__export_credentials: "Exporter des informations sur les informations d'identification stockées sur cet appareil?", + fido__not_registered: "Non enregistré", + fido__not_registered_with_template: "Cet appareil n'est pas enregistré avec\n{}.", + fido__please_enable_pin_protection: "Veuillez activer la protection des broches.", + fido__title_authenticate: "FIDO2 AUTHENTIFIE", + fido__title_import_credential: "IMPORTER DES DIPLÔMES", + fido__title_list_credentials: "RÉPERTORIER LES RÉFÉRENCES", + fido__title_register: "REGISTRE FIDO2", + fido__title_remove_credential: "SUPPRIMER LES INFORMATIONS D'IDENTIFICATION", + fido__title_reset: "RÉINITIALISATION DE FIDO2", + fido__title_u2f_auth: "U2F AUTHENTIFIE", + fido__title_u2f_register: "REGISTRE U2F", + fido__title_verify_user: "FIDO2 VÉRIFIEZ L'UTILISATEUR", + fido__unable_to_verify_user: "Impossible de vérifier l'utilisateur.", + fido__wanna_erase_credentials: "Voulez-vous vraiment effacer toutes les informations d'identification?", + firmware_update__title: "MISE À JOUR DU FIRMWARE", + firmware_update__title_fingerprint: "EMPREINTE DIGITALE FW", + homescreen__click_to_connect: "Cliquez pour se connecter", + homescreen__click_to_unlock: "Cliquez pour déverrouiller", + homescreen__title_backup_failed: "LA SAUVEGARDE A ÉCHOUÉ", + homescreen__title_backup_needed: "SAUVEGARDE NÉCESSAIRE", + homescreen__title_coinjoin_authorized: "COINJOIN AUTORISÉ", + homescreen__title_experimental_mode: "MODE EXPÉRIMENTAL", + homescreen__title_hold_to_lock: "TENIR À VERROUILLER", + homescreen__title_no_usb_connection: "AUCUNE CONNEXION USB", + homescreen__title_pin_not_set: "ÉPINGLE NON RÉGLÉE", + homescreen__title_seedless: "SANS PÉPINS", + homescreen__title_set: "FAIRE UN ÉCRAN D'ACCUEIL", + inputs__back: "DOS", + inputs__cancel: "ANNULER", + inputs__delete: "SUPPRIMER", + inputs__enter: "ENTRER", + inputs__return: "RETOUR", + inputs__show: "MONTRER", + inputs__space: "ESPACE", + joint__title: "TRANSACTION CONJOINTE", + joint__to_the_total_amount: "Au montant total:", + joint__you_are_contributing: "Vous contribuez:", + lockscreen__tap_to_connect: "Appuyez pour se connecter", + lockscreen__tap_to_unlock: "Appuyez pour déverrouiller", + lockscreen__title_locked: "FERMÉ À CLÉ", + lockscreen__title_not_connected: "PAS CONNECTÉ", + misc__decrypt_value: "Decrypt la valeur", + misc__encrypt_value: "Valeur de crypte", + misc__title_suite_labeling: "ÉTIQUETAGE DE LA SUITE", + modify_amount__address: "Adresse:", + modify_amount__decrease_amount: "Diminue le montant de:", + modify_amount__increase_amount: "Augmenter le montant de:", + modify_amount__new_amount: "Nouveau montant:", + modify_amount__title: "MODIFIER LE MONTANT", + modify_fee__decrease_fee: "Diminuer les frais de:", + modify_fee__fee_rate: "Taux de frais:", + modify_fee__increase_fee: "Augmenter les frais de:", + modify_fee__new_transaction_fee: "Nouveaux frais de transaction:", + modify_fee__no_change: "Vos frais n'ont pas changé.", + modify_fee__title: "MODIFIER LES FRAIS", + modify_fee__transaction_fee: "Frais de transaction:", + monero__confirm_export: "Confirmer l'exportation", + monero__confirm_fee: "Confirmer les frais", + monero__confirm_ki_sync: "Confirmer Ki Sync", + monero__confirm_refresh: "Confirmer Rafraîchissement", + monero__confirm_unlock_time: "Confirmer le temps de déverrouillage", + monero__hashing_inputs: "Entrées de hachage", + monero__payment_id: "ID de paiement", + monero__postprocessing: "Post-traitement ...", + monero__processing: "Traitement...", + monero__processing_inputs: "Traitement des entrées", + monero__processing_outputs: "Traitement des sorties", + monero__signing: "Signer ...", + monero__signing_inputs: "Signature des entrées", + monero__unlock_time_set_template: "Déverrouiller le temps pour cette transaction est défini sur {}", + monero__wanna_export_tx_der: "Voulez-vous vraiment exporter tx_der\npour TX_proof?", + monero__wanna_export_tx_key: "Voulez-vous vraiment exporter TX_KEY?", + monero__wanna_export_watchkey: "Voulez-vous vraiment exporter des informations d'identification de montre uniquement?", + monero__wanna_start_refresh: "Voulez-vous vraiment\ncommencer à actualiser?", + monero__wanna_sync_key_images: "Voulez-vous vraiment\nsynchroniser les images clés?", + nem__absolute: "Absolue", + nem__activate: "Activer", + nem__add: "Ajouter", + nem__confirm_action: "Confirmer l'action", + nem__confirm_address: "confirmer l'adresse", + nem__confirm_creation_fee: "Confirmer les frais de création", + nem__confirm_fee: "Confirmer les frais", + nem__confirm_mosaic: "Confirmer la mosaïque", + nem__confirm_multisig_fee: "Confirmer les frais multisig", + nem__confirm_namespace: "Confirmer l'espace de noms", + nem__confirm_payload: "Confirmer la charge utile", + nem__confirm_properties: "Confirmer les propriétés", + nem__confirm_rental_fee: "Confirmer les frais de location", + nem__confirm_transfer_of: "Confirmer le transfert de", + nem__convert_account_to_multisig: "Convertir le compte en compte multisig?", + nem__cosign_transaction_for: "Transaction cosignale pour", + nem__cosignatory: "cosignataire", + nem__create_mosaic: "Créer de la mosaïque", + nem__create_namespace: "Créer un espace de noms", + nem__deactivate: "Désactiver", + nem__decrease: "Diminuer", + nem__description: "Description:", + nem__divisibility_and_levy_cannot_be_shown: "La divisibilité et le prélèvement ne peuvent pas être montrés pour des mosaïques inconnues", + nem__encrypted: "Crypté:", + nem__final_confirm: "Confirmation finale", + nem__immutable: "immuable", + nem__increase: "Augmenter", + nem__initial_supply: "Alimentation initiale:", + nem__initiate_transaction_for: "Initier la transaction pour", + nem__levy_divisibility: "Divisibilité de prélèvement:", + nem__levy_fee: "Frais de prélèvement:", + nem__levy_fee_of: "Confirmer les frais de prélèvement en mosaïque de", + nem__levy_mosaic: "Mosaïque de prélèvement:", + nem__levy_namespace: "Espace de noms de prélèvement:", + nem__levy_recipient: "Récipiendaire de prélèvement:", + nem__levy_type: "Type de prélèvement:", + nem__modify_supply_for: "Modifier l'offre pour", + nem__modify_the_number_of_cosignatories_by: "Modifier le nombre de cosignatoires par", + nem__mutable: "mutable", + nem__no: "Non", + nem__of: "de", + nem__percentile: "centile", + nem__raw_units_template: "{} unités brutes", + nem__remote_harvesting: "Récolte à distance?", + nem__remove: "Retirer", + nem__set_minimum_cosignatories_to: "Définir des cosignataires minimums sur", + nem__sign_tx_fee_template: "Signer cette transaction\net payer {}\nPour les frais de réseau?", + nem__supply_change: "Changement de matériel", + nem__supply_units_template: "{} fourniture par {} unités entières?", + nem__transferable: "Transférable?", + nem__under_namespace: "sous l'espace de noms", + nem__unencrypted: "Non crypté:", + nem__unknown_mosaic: "Mosaïque inconnue!", + nem__yes: "Oui", + passphrase__access_hidden_wallet: "Accès à un portefeuille caché?", + passphrase__always_on_device: "Voulez-vous vraiment entrer en phrase secrète toujours sur l'appareil?", + passphrase__from_host_not_shown: "La phrase de passe fournie par l'hôte sera utilisée mais ne sera pas affichée en raison des paramètres de l'appareil.", + passphrase__hidden_wallet: "Portefeuille caché", + passphrase__hide: "Masquer la phrase de passe provenant de l'hôte?", + passphrase__next_screen_will_show_passphrase: "Le prochain écran affichera la phrase secrète.", + passphrase__please_enter: "Veuillez saisir votre phrase secrète.", + passphrase__revoke_on_device: "Voulez-vous révoquer la phrase secrète sur le réglage de l'appareil?", + passphrase__title_confirm: "CONFIRMER LA PHRASE PASSANTE", + passphrase__title_enter: "ENTREZ EN PHRASE DE PASSE", + passphrase__title_hide: "MASQUER LA PHRASE SECRÈTE", + passphrase__title_settings: "PARAMÈTRES DE PHRASE DE PASSE", + passphrase__title_source: "SOURCE EN PHRASE DE PASSE", + passphrase__turn_off: "Désactiver la protection de la phrase secrète?", + passphrase__turn_on: "Activer la protection des phrases de passe?", + pin__change: "Changer la broche?", + pin__changed: "Pin a changé.", + pin__cursor_will_change: "La position du curseur changera entre les entrées pour une sécurité améliorée.", + pin__diff_from_wipe_code: "PIN doit être différente wipe.", + pin__disabled: "Protection contre les broches\néteindre.", + pin__enabled: "Protection contre les broches\nallumé.", + pin__enter: "Entrer la broche", + pin__enter_new: "Entrez la nouvelle épingle", + pin__entered_not_valid: "La broche n'est pas valide.", + pin__info: "La broche sera nécessaire pour accéder à cet appareil.", + pin__invalid_pin: "La broche n'est pas valide.", + pin__last_attempt: "Dernière tentative", + pin__mismatch: "Les épingles entrées ne pas!", + pin__pin_mismatch: "Non-concordance", + pin__please_check_again: "Veuillez vérifier à nouveau.", + pin__reenter_new: "Rentrez à la nouvelle épingle", + pin__reenter_to_confirm: "Veuillez réintégrer la broche pour confirmer.", + pin__should_be_long: "La broche doit avoir 4 à 50 chiffres de long.", + pin__title_check_pin: "ÉPINGLE", + pin__title_settings: "PARAMÈTRES DE BROCHE", + pin__title_wrong_pin: "MAUVAISE ÉPINGLE", + pin__tries_left: "essaie à gauche", + pin__turn_off: "Êtes-vous sûr de vouloir désactiver la protection des broches?", + pin__turn_on: "Activer la protection des broches?", + pin__wrong_pin: "Mauvaise épingle", + plurals__contains_x_keys: "clé|clés", + plurals__lock_after_x_hours: "heure|heures", + plurals__lock_after_x_milliseconds: "milliseconde|millisecondes", + plurals__lock_after_x_minutes: "minute|minutes", + plurals__lock_after_x_seconds: "seconde|secondes", + plurals__sign_x_actions: "action|actiones", + plurals__transaction_of_x_operations: "opération|opérations", + plurals__x_groups_needed: "groupe|groupes", + plurals__x_shares_needed: "partage|partages", + progress__authenticity_check: "Vérification de l'authenticité ...", + progress__done: "Fait", + progress__loading_transaction: "Transaction de chargement ...", + progress__one_second_left: "1 seconde à gauche", + progress__please_wait: "S'IL VOUS PLAÎT, ATTENDEZ", + progress__processing: "TRAITEMENT", + progress__refreshing: "RAFRAÎCHISSANTE", + progress__signing_transaction: "Transaction de signature ...", + progress__syncing: "Synchronisation ...", + progress__x_seconds_left_template: "{} secondes à gauche", + reboot_to_bootloader__restart: "Voulez-vous redémarrer Trezor en mode chargeur de démarrage?", + reboot_to_bootloader__title: "ALLER AU CHARGEUR DE DÉMARRAGE", + reboot_to_bootloader__version_by_template: "Version du firmware {}\npar {}", + recovery__cancel_dry_run: "Annuler le chèque de sauvegarde", + recovery__check_dry_run: "Vérifiez votre sauvegarde?", + recovery__cursor_will_change: "La position du curseur changera entre les entrées pour une sécurité améliorée.", + recovery__dry_run_bip39_valid_match: "Récupération entrée est valide et correspond à celle de l'appareil.", + recovery__dry_run_bip39_valid_mismatch: "Récupération entrée est valide mais ne correspond pas à celle de l'appareil.", + recovery__dry_run_slip39_valid_match: "Récupération entrées sont valides et correspondent qui est actuellement.", + recovery__dry_run_slip39_valid_mismatch: "Récupération entrées sont valides mais ne correspondent qui est actuellement.", + recovery__enter_any_share: "Entrez n'importe quelle part", + recovery__enter_backup: "Entrez votre sauvegarde.", + recovery__enter_different_share: "Veuillez saisir une part différente.", + recovery__enter_share_from_diff_group: "Entrez Share à partir d'un groupe différent.", + recovery__group_num_template: "Groupe {}", + recovery__group_threshold_reached: "Le seuil de groupe atteint.", + recovery__invalid_seed_entered: "Les graines de récupération non valides sont entrées.", + recovery__invalid_share_entered: "Part de récupération non valide entré.", + recovery__more_shares_needed: "Plus d'actions nécessaires", + recovery__num_of_words: "Sélectionnez le nombre de mots dans votre sauvegarde.", + recovery__only_first_n_letters: "Vous n'aurez qu'à sélectionner les 2-4 premières lettres de chaque mot.", + recovery__progress_will_be_lost: "Tous les progrès seront perdus.", + recovery__select_num_of_words: "Sélectionnez le nombre de mots dans votre sauvegarde.", + recovery__share_already_entered: "Partager déjà entré.", + recovery__share_from_another_shamir: "Vous êtes entré dans une part d'une autre sauvegarde Shamir.", + recovery__share_num_template: "Partager {}", + recovery__title: "RÉCUPÉRER LE PORTEFEUILLE", + recovery__title_cancel_dry_run: "ANNULER LE CHÈQUE DE SAUVEGARDE", + recovery__title_cancel_recovery: "ANNULER LA RÉCUPÉRATION", + recovery__title_dry_run: "CHÈQUE DE SAUVEGARDE", + recovery__title_recover: "RÉCUPÉRER LE PORTEFEUILLE", + recovery__title_remaining_shares: "ACTIONS RESTANTES", + recovery__type_word_x_of_y_template: "Type Word {} de {}", + recovery__wallet_recovered: "Le portefeuille a récupéré avec succès", + recovery__wanna_cancel_dry_run: "Êtes-vous sûr que vous souhaitez annuler le chèque de sauvegarde?", + recovery__wanna_cancel_recovery: "Êtes-vous sûr de souhaiter annuler le processus de récupération?", + recovery__word_count_template: "({} mots)", + recovery__word_x_of_y_template: "MOT DE {}", + recovery__x_more_items_starting_template_plural: "{count} plus {plural} startes", + recovery__x_more_shares_needed_template_plural: "{count} plus {plural} nécessaires.", + recovery__x_of_y_entered_template: "{} des actions {} entrées avec succès.", + recovery__you_have_entered: "Vous êtes entré", + reset__advanced_group_threshold_info: "Le seuil de groupe spécifie le nombre de groupes nécessaires pour récupérer votre portefeuille.", + reset__all_x_of_y_template: "Tous {} de {} partages", + reset__any_x_of_y_template: "Toute {} de {} partage", + reset__button_create: "CRÉER UN PORTEFEUILLE", + reset__button_recover: "RÉCUPÉRER LE PORTEFEUILLE", + reset__by_continuing: "En vous poursuivant, vous acceptez les termes et conditions de Trezor Company.", + reset__check_backup_title: "VÉRIFIEZ LA SAUVEGARDE", + reset__check_group_share_title_template: "VÉRIFIER G {} - PART {}", + reset__check_seed_title: "VÉRIFIER LES GRAINES", + reset__check_share_title_template: "VÉRIFIER LE PARTAGE # {}", + reset__continue_with_next_share: "Continuez avec la prochaine part.", + reset__continue_with_share_template: "Continuez avec Share # {}.", + reset__finished_verifying_group_template: "Vous avez terminé la vérification de vos parts de récupération pour le groupe {}.", + reset__finished_verifying_seed: "Vous avez fini de vérifier vos graines de récupération.", + reset__finished_verifying_shares: "Vous avez fini de vérifier vos parts de récupération.", + reset__group_description: "Un groupe est composé de partages de recouvrement.", + reset__group_info: "Chaque groupe a un nombre défini d'actions et son propre seuil.Dans les prochaines étapes, vous définissez le nombre d'actions et les seuils.", + reset__group_share_checked_successfully_template: "Groupe {} - partager {} vérifié avec succès.", + reset__group_share_title_template: "GROUPE {} - PARTAGER {}", + reset__more_info_at: "Plus d'informations à", + reset__need_all_share_template: "Pour la récupération, vous avez besoin de tout {} des actions.", + reset__need_any_share_template: "Pour la récupération, vous avez besoin de {} des actions.", + reset__needed_to_form_a_group: "nécessaire pour former un groupe.", + reset__needed_to_recover_your_wallet: "nécessaire pour récupérer votre portefeuille.", + reset__never_make_digital_copy: "Ne faites jamais une copie numérique de votre sauvegarde ou téléchargez-la en ligne!", + reset__num_of_share_holders_template: "{} Les personnes ou les emplacements tiendront chacun une part.", + reset__num_of_shares_advanced_info_template: "Chaque part de récupération est une séquence de 20 mots.Ensuite, vous choisirez le nombre de seuils d'actions nécessaires pour former le groupe {}.", + reset__num_of_shares_basic_info: "Chaque part de récupération est une séquence de 20 mots.Ensuite, vous choisissez le nombre d'actions dont vous avez besoin pour récupérer votre portefeuille.", + reset__num_shares_for_group_template: "Le nombre requis d'actions pour former le groupe {}.", + reset__number_of_shares_info: "= Nombre total de listes de mots uniques utilisées pour le sauvegarde.", + reset__one_share: "1 partage", + reset__only_one_share_will_be_created: "Une seule part sera créée.", + reset__recovery_seed_title: "GRAINES DE RÉCUPÉRATION", + reset__recovery_share_title_template: "PARTAGE DE RÉCUPÉRATION # {}", + reset__required_number_of_groups: "Le nombre requis de groupes pour la récupération.", + reset__select_correct_word: "Sélectionnez le mot correct pour chaque position.", + reset__select_word_template: "SÉLECTIONNER {} MOT", + reset__select_word_x_of_y_template: "Word {} de {}:", + reset__set_it_to_count_template: "Définissez-le sur {} et vous aurez besoin", + reset__share_checked_successfully_template: "Partage de récupération # {} vérifié avec succès.", + reset__share_words_title: "SAUVEGARDE STANDARD", + reset__slip39_checklist_num_groups: "Nombre de groupes", + reset__slip39_checklist_num_shares: "Nombre de partages", + reset__slip39_checklist_set_num_groups: "Définir le nombre de groupes", + reset__slip39_checklist_set_num_shares: "Définir le nombre d'actions", + reset__slip39_checklist_set_sizes: "Régler les tailles et les seuils", + reset__slip39_checklist_set_sizes_longer: "Définir les tailles et les seuils pour chaque groupe", + reset__slip39_checklist_set_threshold: "Définir le seuil", + reset__slip39_checklist_title: "LISTE DE CONTRÔLE DE SAUVEGARDE", + reset__slip39_checklist_write_down: "Notez et vérifiez toutes les partages", + reset__slip39_checklist_write_down_recovery: "Notez et vérifiez toutes les parts de récupération", + reset__the_threshold_sets_the_number_of_shares: "Le seuil définit le nombre d'actions", + reset__threshold_info: "= Nombre minimum de listes de mots uniques utilisées pour la récupération.", + reset__title_backup_is_done: "LA SAUVEGARDE EST TERMINÉE", + reset__title_create_wallet: "CRÉER UN PORTEFEUILLE", + reset__title_create_wallet_shamir: "CRÉER UN PORTEFEUILLE (SHAMIR)", + reset__title_group_threshold: "SEUIL DE GROUPE", + reset__title_number_of_groups: "NOMBRE DE GROUPES", + reset__title_number_of_shares: "NOMBRE DE PARTAGES", + reset__title_set_group_threshold: "DÉFINIR LE SEUIL DE GROUPE", + reset__title_set_number_of_groups: "DÉFINIR LE NOMBRE DE GROUPES", + reset__title_set_number_of_shares: "DÉFINIR LE NOMBRE D'ACTIONS", + reset__title_set_threshold: "DÉFINIR LE SEUIL", + reset__to_form_group_template: "pour former le groupe {}.", + reset__tos_link: "trezor.io/tos", + reset__total_number_of_shares_in_group_template: "Définissez le nombre total d'actions dans le groupe {}.", + reset__use_your_backup: "Utilisez votre sauvegarde lorsque vous devez récupérer votre portefeuille.", + reset__write_down_words_template: "Notez tous les mots {} dans l'ordre.", + reset__wrong_word_selected: "Mauvais mot sélectionné!", + reset__you_need_one_share: "Pour la récupération, vous avez besoin de 1 part.", + reset__your_backup_is_done: "Votre sauvegarde est terminée.", + ripple__confirm_tag: "Confirmer la balise", + ripple__destination_tag_template: "Tag de destination:\n{}", + rotation__change_template: "Voulez-vous modifier la rotation des périphériques en {}?", + rotation__east: "est", + rotation__north: "nord", + rotation__south: "sud", + rotation__title_change: "CHANGER LA ROTATION", + rotation__west: "Ouest", + safety_checks__approve_unsafe_always: "Trezor vous permettra d'approuver certaines actions qui pourraient être dangereuses.", + safety_checks__approve_unsafe_temporary: "Trezor vous permettra temporairement d'approuver certaines actions qui pourraient être dangereuses.", + safety_checks__enforce_strict: "Voulez-vous vraiment appliquer des contrôles de sécurité stricts (recommandés)?", + safety_checks__title: "CONTRÔLES DE SÉCURITÉ", + safety_checks__title_safety_override: "REMPLIR DE LA SÉCURITÉ", + sd_card__all_data_will_be_lost: "Toutes les données sur la carte SD seront perdues.", + sd_card__card_required: "Carte SD requise.", + sd_card__disable: "Voulez-vous vraiment supprimer la protection des cartes SD de votre appareil?", + sd_card__disabled: "Vous avez une protection SD handicapée avec succès.", + sd_card__enable: "Voulez-vous vraiment sécuriser votre appareil avec une protection de carte SD?", + sd_card__enabled: "Vous avez réussi à activer la protection SD.", + sd_card__error: "Erreur de carte SD", + sd_card__format_card: "Carte SD de format", + sd_card__insert_correct_card: "Veuillez insérer la carte SD correcte pour cet appareil.", + sd_card__please_insert: "Veuillez insérer votre carte SD.", + sd_card__please_unplug_and_insert: "Veuillez débrancher l'appareil et insérer votre carte SD.", + sd_card__problem_accessing: "Il y avait un problème à accéder à la carte SD.", + sd_card__refresh: "Voulez-vous vraiment remplacer le secret de la carte SD actuel par un secret nouvellement généré?", + sd_card__refreshed: "Vous avez réussi à rafraîchir la protection SD.", + sd_card__restart: "Voulez-vous redémarrer Trezor en mode chargeur de démarrage?", + sd_card__title: "PROTECTION DES CARTES SD", + sd_card__title_problem: "PROBLÈME DE CARTE SD", + sd_card__unknown_filesystem: "Système de fichiers inconnu", + sd_card__unplug_and_insert_correct: "Veuillez débrancher l'appareil et insérer la carte SD correcte.", + sd_card__use_different_card: "Utilisez une carte différente ou formate la carte SD au système de fichiers FAT32", + sd_card__wanna_format: "Voulez-vous vraiment formater la carte SD?", + sd_card__wrong_sd_card: "Mauvaise carte SD", + send__address_path: "chemin d'adresse", + send__amount: "Montant:", + send__confirm_sending: "Confirm_sending", + send__from_multiple_accounts: "Envoi à partir de plusieurs comptes.", + send__including_fee: "Y compris les frais:", + send__maximum_fee: "Frais maximaux:", + send__receiving_to_multisig: "Recevoir une adresse multisig.", + send__title_amount: "MONTANT", + send__title_confirm_sending: "CONFIRMER L'ENVOI", + send__title_joint_transaction: "TRANSACTION CONJOINTE", + send__title_receiving_to: "RECEVOIR", + send__title_recipient: "DESTINATAIRE", + send__title_sending: "ENVOI EN COURS", + send__title_sending_amount: "MONTANT", + send__title_sending_to: "ENVOI À", + send__to_the_total_amount: "Au montant total:", + send__total_amount: "Montant total:", + send__transaction_id: "Identifiant de transaction:", + send__you_are_contributing: "Vous contribuez:", + share_words__words_in_order: "mots dans l'ordre.", + share_words__wrote_down_all: "J'ai écrit tout", + sign_message__bytes_template: "{} Octets", + sign_message__confirm_address: "Adresse de signature", + sign_message__confirm_message: "Confirmer le message", + sign_message__message_size: "Taille du message:", + sign_message__verify_address: "Vérifier l'adresse", + stellar__account: "Compte", + stellar__account_merge: "Fusion du compte", + stellar__account_thresholds: "Seuils de compte", + stellar__add_signer: "Ajouter le signataire", + stellar__add_trust: "Ajouter la confiance", + stellar__all_will_be_sent_to: "Tous les xlm seront envoyés à:", + stellar__allow_trust: "Autoriser la confiance", + stellar__asset: "Actif", + stellar__bump_sequence: "Séquence de bosse", + stellar__buying: "Achat:", + stellar__clear_data: "Effacer les données", + stellar__clear_flags: "Drapeaux clairs", + stellar__confirm_issuer: "Confirmer l'émetteur", + stellar__confirm_memo: "Confirmer la note", + stellar__confirm_network: "Confirmer le réseau", + stellar__confirm_operation: "Confirmation de l'opération", + stellar__confirm_stellar: "Confirmer stellaire", + stellar__confirm_timebounds: "Confirmer les temps", + stellar__create_account: "Créer un compte", + stellar__debited_amount: "Montant débité", + stellar__delete: "Supprimer", + stellar__delete_passive_offer: "Supprimer l'offre passive", + stellar__delete_trust: "Supprimer la confiance", + stellar__destination: "Destination:", + stellar__exchanges_require_memo: "IMPORTANT: De nombreux échanges nécessitent un mémo lors du dépôt", + stellar__final_confirm: "Confirmation finale", + stellar__hash: "Hacher:", + stellar__high: "Haut:", + stellar__home_domain: "Domaine de la maison", + stellar__inflation: "Inflation", + stellar__initial_balance: "Balance initiale", + stellar__initialize_signing_with: "Initialiser la signature avec", + stellar__issuer_template: "{} émetteur:", + stellar__key: "Clé:", + stellar__limit: "Limite:", + stellar__low: "Faible:", + stellar__master_weight: "Poids maître:", + stellar__medium: "Moyen:", + stellar__new_offer: "Nouvelle offre", + stellar__new_passive_offer: "Nouvelle offre passive", + stellar__no_memo_set: "Pas de mémo!", + stellar__no_restriction: "[pas de réstriction]", + stellar__on_network_template: "La transaction est sur {}", + stellar__path_pay: "PATH PAY", + stellar__path_pay_at_least: "Le chemin payant le moins", + stellar__pay: "Payer:", + stellar__pay_at_most: "Payer tout au plus:", + stellar__preauth_transaction: "Transaction pré-UTH:", + stellar__price_per_template: "Prix par {}:", + stellar__private_network: "Réseau privé", + stellar__remove_signer: "Supprimer le signataire", + stellar__revoke_trust: "Révoquer la confiance", + stellar__selling: "Vente:", + stellar__set_data: "Définir les données", + stellar__set_flags: "Définir les drapeaux", + stellar__set_sequence_to_template: "Définir la séquence sur {}?", + stellar__sign_tx_count_template: "Signer cette transaction composée de {}", + stellar__sign_tx_fee_template: "et payer {}\npour les frais?", + stellar__source_account: "Compte source:", + stellar__testnet_network: "réseau Testnet", + stellar__trusted_account: "Compte de confiance", + stellar__update: "Mise à jour", + stellar__valid_from: "Valide de (UTC)", + stellar__valid_to: "Valable à (UTC)", + stellar__value_sha256: "Valeur (SHA-256):", + stellar__wanna_clean_value_key_template: "Voulez-vous effacer la clé de valeur {}?", + stellar__your_account: "Votre compte", + tezos__address: "Adresse:", + tezos__amount: "Montant:", + tezos__baker_address: "Adresse boulanger:", + tezos__balance: "Équilibre:", + tezos__ballot: "Bulletin de vote:", + tezos__confirm_delegation: "Confirmer la délégation", + tezos__confirm_origination: "Confirmer l'origine", + tezos__delegator: "Délégant:", + tezos__fee: "Frais:", + tezos__proposal: "Proposition", + tezos__register_delegate: "Enregistrer le délégué", + tezos__remove_delegation: "Supprimer la délégation", + tezos__submit_ballot: "Soumettre un bulletin de vote", + tezos__submit_proposal: "Soumettre la proposition", + tezos__submit_proposals: "Soumettre des propositions", + tutorial__middle_click: "Appuyez à gauche et à droite à la même chose. Il est temps de confirmer.", + tutorial__press_and_hold: "Appuyez et maintenez le bouton droit pour approuver des opérations.", + tutorial__ready_to_use: "Tu es prêt à. Utilisez Trezor.", + tutorial__scroll_down: "Appuyez à droite pour faire défiler vers le bas pour lire tout le contenu lorsque le texte ne tient pas. Appuyez à gauche pour faire défiler vers le haut.", + tutorial__sure_you_want_skip: "Est-tu sûr que tu. Vous voulez sauter le tutoriel?", + tutorial__title_hello: "BONJOUR", + tutorial__title_screen_scroll: "PARCHEMIN D'ÉCRAN", + tutorial__title_skip: "PASSER LE TUTORIEL", + tutorial__title_tutorial_complete: "TUTORIEL COMPLET", + tutorial__use_trezor: "Utiliser Trezor par. Cliquez sur les boutons gauche et droit. Continuer tout droit.", + tutorial__welcome_press_right: "Bienvenue à Trezor. Appuyez sur le droit de continuer.", + u2f__get: "Augmenter et récupérer le compteur U2F?", + u2f__set_template: "Définissez le compteur U2F sur {}?", + u2f__title_get: "OBTENEZ UN COMPTEUR U2F", + u2f__title_set: "DÉFINIR LE COMPTEUR U2F", + wipe__info: "Toutes les données seront effacées.", + wipe__title: "EFFACER L'APPAREIL", + wipe__want_to_wipe: "Voulez-vous vraiment essuyer l'appareil?", + wipe_code__change: "Changer de code d'essuyage?", + wipe_code__changed: "Effacer le code modifié.", + wipe_code__diff_from_pin: "Le code d'essuyage doit être différent de votre broche.\nVeuillez réessayer.", + wipe_code__disabled: "Essuyez le code désactivé.", + wipe_code__enabled: "Effacer le code activé.", + wipe_code__enter_new: "Entrez le nouveau code d'essuyage", + wipe_code__info: "Le code peut pour effacer toutes les données.", + wipe_code__invalid: "Code d'essuyage non valide", + wipe_code__mismatch: "Les codes d'essuyage entrés ne correspondent pas!", + wipe_code__reenter: "Rentrer le code d'essuyage", + wipe_code__reenter_to_confirm: "Veuillez réintégrer le code d'essuyage pour confirmer.", + wipe_code__title_check: "VÉRIFIER LE CODE D'ESSUYAGE", + wipe_code__title_invalid: "CODE D'ESSUYAGE NON VALIDE", + wipe_code__title_settings: "EFFACER DU CODE", + wipe_code__turn_off: "Désactiver la protection du code d'essuyage?", + wipe_code__turn_on: "Activer la protection du code d'essuyage?", + wipe_code__wipe_code_mismatch: "Effacer le décalage du code", + word_count__title: "NOMBRE DE MOTS", + words__are_you_sure: "Es-tu sûr?", + words__array_of: "Tableau de", + words__buying: "Achat", + words__confirm: "Confirmer", + words__contains: "contient", + words__continue_anyway: "Continuer de toute façon?", + words__continue_with: "Continue avec", + words__error: "Erreur", + words__from: "depuis", + words__keep_it_safe: "Garde-le en sécurité!", + words__know_what_your_doing: "Continuez uniquement si vous savez ce que vous faites!", + words__my_trezor: "Mon Trezor", + words__outputs: "les sorties", + words__please_check_again: "Veuillez vérifier à nouveau", + words__please_try_again: "Veuillez réessayer", + words__really_wanna: "Voulez-vous vraiment", + words__sign: "Signe", + words__title_check: "VÉRIFIER", + words__title_group: "GROUPE", + words__title_information: "INFORMATION", + words__title_remember: "SOUVIENS-TOI", + words__title_share: "PARTAGER", + words__title_shares: "ACTIONS", + words__title_success: "SUCCÈS", + words__title_summary: "RÉSUMÉ", + words__title_threshold: "SEUIL", + words__unknown: "Inconnu", + words__warning: "Avertissement", +}; diff --git a/core/embed/rust/src/ui/translations/fr.rs.mako b/core/embed/rust/src/ui/translations/fr.rs.mako new file mode 100644 index 000000000..bbc3556f7 --- /dev/null +++ b/core/embed/rust/src/ui/translations/fr.rs.mako @@ -0,0 +1,33 @@ +//! generated from fr.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +// NOTE: not used as a code, only for +// documentation purposes + +<% +import json + +from pathlib import Path + +THIS = Path(local.filename).resolve() +SRCDIR = THIS.parent + +file = SRCDIR / "fr.json" + +data = json.loads(file.read_text())["translations"] +items_to_write: list[tuple[str, str]] = [] +for section_name, section in data.items(): + for k, v in section.items(): + name = f"{section_name}__{k}" + items_to_write.append((name, v)) +items_to_write.sort(key=lambda x: x[0]) +%>\ +use super::general::TranslationsGeneral; + +#[rustfmt::skip] +pub const TRANSLATIONS: TranslationsGeneral = TranslationsGeneral { +% for k, v in items_to_write: + ${k}: ${utf8_str(v)}, +% endfor +}; diff --git a/core/embed/rust/src/ui/translations/general.rs b/core/embed/rust/src/ui/translations/general.rs new file mode 100644 index 000000000..f5ff3caff --- /dev/null +++ b/core/embed/rust/src/ui/translations/general.rs @@ -0,0 +1,1683 @@ +//! generated from cs.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +#[rustfmt::skip] +#[allow(non_snake_case)] +pub struct TranslationsGeneral { + pub addr_mismatch__contact_support: &'static str, + pub addr_mismatch__key_mismatch: &'static str, + pub addr_mismatch__mismatch: &'static str, + pub addr_mismatch__support_url: &'static str, + pub addr_mismatch__title: &'static str, + pub addr_mismatch__title_key_mismatch: &'static str, + pub addr_mismatch__wrong_derication_path: &'static str, + pub addr_mismatch__xpub_mismatch: &'static str, + pub address__address: &'static str, + pub address__public_key: &'static str, + pub address__title_cosigner: &'static str, + pub address__title_receive_address: &'static str, + pub address__title_yours: &'static str, + pub address_details__account: &'static str, + pub address_details__derivation_path: &'static str, + pub address_details__title_receive_address: &'static str, + pub address_details__title_receiving_to: &'static str, + pub authenticate__confirm_template: &'static str, + pub authenticate__header: &'static str, + pub auto_lock__change_template: &'static str, + pub auto_lock__title: &'static str, + pub backup__can_back_up_anytime: &'static str, + pub backup__it_should_be_backed_up: &'static str, + pub backup__it_should_be_backed_up_now: &'static str, + pub backup__new_wallet_created: &'static str, + pub backup__new_wallet_successfully_created: &'static str, + pub backup__recover_anytime: &'static str, + pub backup__title_backup_wallet: &'static str, + pub backup__title_skip: &'static str, + pub backup__want_to_skip: &'static str, + pub binance__buy: &'static str, + pub binance__confirm_cancel: &'static str, + pub binance__confirm_input: &'static str, + pub binance__confirm_order: &'static str, + pub binance__confirm_output: &'static str, + pub binance__order_id: &'static str, + pub binance__pair: &'static str, + pub binance__price: &'static str, + pub binance__quantity: &'static str, + pub binance__sell: &'static str, + pub binance__sender_address: &'static str, + pub binance__side: &'static str, + pub binance__unknown: &'static str, + pub bitcoin__commitment_data: &'static str, + pub bitcoin__confirm_locktime: &'static str, + pub bitcoin__create_proof_of_ownership: &'static str, + pub bitcoin__high_mining_fee_template: &'static str, + pub bitcoin__locktime_no_effect: &'static str, + pub bitcoin__locktime_set_to: &'static str, + pub bitcoin__locktime_set_to_blockheight: &'static str, + pub bitcoin__lot_of_change_outputs: &'static str, + pub bitcoin__multiple_accounts: &'static str, + pub bitcoin__new_fee_rate: &'static str, + pub bitcoin__simple_send_of: &'static str, + pub bitcoin__ticket_amount: &'static str, + pub bitcoin__title_confirm_details: &'static str, + pub bitcoin__title_finalize_transaction: &'static str, + pub bitcoin__title_high_mining_fee: &'static str, + pub bitcoin__title_meld_transaction: &'static str, + pub bitcoin__title_modify_amount: &'static str, + pub bitcoin__title_payjoin: &'static str, + pub bitcoin__title_proof_of_ownership: &'static str, + pub bitcoin__title_purchase_ticket: &'static str, + pub bitcoin__title_update_transaction: &'static str, + pub bitcoin__unknown_path: &'static str, + pub bitcoin__unknown_transaction: &'static str, + pub bitcoin__unusually_high_fee: &'static str, + pub bitcoin__unverified_external_inputs: &'static str, + pub bitcoin__valid_signature: &'static str, + pub bitcoin__voting_rights: &'static str, + pub buttons__abort: &'static str, + pub buttons__access: &'static str, + pub buttons__again: &'static str, + pub buttons__allow: &'static str, + pub buttons__back_up: &'static str, + pub buttons__cancel: &'static str, + pub buttons__change: &'static str, + pub buttons__check: &'static str, + pub buttons__check_again: &'static str, + pub buttons__close: &'static str, + pub buttons__confirm: &'static str, + pub buttons__continue: &'static str, + pub buttons__details: &'static str, + pub buttons__enable: &'static str, + pub buttons__enter: &'static str, + pub buttons__enter_share: &'static str, + pub buttons__export: &'static str, + pub buttons__format: &'static str, + pub buttons__go_back: &'static str, + pub buttons__hold_to_confirm: &'static str, + pub buttons__info: &'static str, + pub buttons__install: &'static str, + pub buttons__more_info: &'static str, + pub buttons__ok_i_understand: &'static str, + pub buttons__purchase: &'static str, + pub buttons__quit: &'static str, + pub buttons__restart: &'static str, + pub buttons__retry: &'static str, + pub buttons__select: &'static str, + pub buttons__set: &'static str, + pub buttons__show_all: &'static str, + pub buttons__show_words: &'static str, + pub buttons__skip: &'static str, + pub buttons__try_again: &'static str, + pub buttons__turn_off: &'static str, + pub buttons__turn_on: &'static str, + pub cardano__addr_base: &'static str, + pub cardano__addr_enterprise: &'static str, + pub cardano__addr_legacy: &'static str, + pub cardano__addr_pointer: &'static str, + pub cardano__addr_reward: &'static str, + pub cardano__address_no_staking: &'static str, + pub cardano__amount: &'static str, + pub cardano__amount_burned_decimals_unknown: &'static str, + pub cardano__amount_minted_decimals_unknown: &'static str, + pub cardano__amount_sent_decimals_unknown: &'static str, + pub cardano__anonymous_pool: &'static str, + pub cardano__asset_fingerprint: &'static str, + pub cardano__auxiliary_data_hash: &'static str, + pub cardano__block: &'static str, + pub cardano__catalyst: &'static str, + pub cardano__certificate: &'static str, + pub cardano__certificate_path: &'static str, + pub cardano__change_output: &'static str, + pub cardano__change_output_path: &'static str, + pub cardano__change_output_staking_path: &'static str, + pub cardano__check_all_items: &'static str, + pub cardano__choose_level_of_details: &'static str, + pub cardano__collateral_input_id: &'static str, + pub cardano__collateral_input_index: &'static str, + pub cardano__collateral_output_contains_tokens: &'static str, + pub cardano__collateral_return: &'static str, + pub cardano__confirm: &'static str, + pub cardano__confirm_signing_stake_pool: &'static str, + pub cardano__confirm_transaction: &'static str, + pub cardano__confirming_a_multisig_transaction: &'static str, + pub cardano__confirming_pool_registration: &'static str, + pub cardano__confirming_transction: &'static str, + pub cardano__cost: &'static str, + pub cardano__credential_mismatch: &'static str, + pub cardano__datum_hash: &'static str, + pub cardano__delegating_to: &'static str, + pub cardano__for_account_and_index_template: &'static str, + pub cardano__for_account_template: &'static str, + pub cardano__for_key_hash: &'static str, + pub cardano__for_script: &'static str, + pub cardano__inline_datum: &'static str, + pub cardano__input_id: &'static str, + pub cardano__input_index: &'static str, + pub cardano__intro_text_address: &'static str, + pub cardano__intro_text_change: &'static str, + pub cardano__intro_text_owned_by_device: &'static str, + pub cardano__intro_text_registration_payment: &'static str, + pub cardano__key_hash: &'static str, + pub cardano__margin: &'static str, + pub cardano__multisig_path: &'static str, + pub cardano__nested_scripts_template: &'static str, + pub cardano__network: &'static str, + pub cardano__no_output_tx: &'static str, + pub cardano__nonce: &'static str, + pub cardano__other: &'static str, + pub cardano__path: &'static str, + pub cardano__pledge: &'static str, + pub cardano__pointer: &'static str, + pub cardano__policy_id: &'static str, + pub cardano__pool_metadata_hash: &'static str, + pub cardano__pool_metadata_url: &'static str, + pub cardano__pool_owner: &'static str, + pub cardano__pool_owner_path: &'static str, + pub cardano__pool_reward_account: &'static str, + pub cardano__reference_input_id: &'static str, + pub cardano__reference_input_index: &'static str, + pub cardano__reference_script: &'static str, + pub cardano__required_signer: &'static str, + pub cardano__reward: &'static str, + pub cardano__reward_address: &'static str, + pub cardano__reward_eligibility_warning: &'static str, + pub cardano__rewards_go_to: &'static str, + pub cardano__script: &'static str, + pub cardano__script_all: &'static str, + pub cardano__script_any: &'static str, + pub cardano__script_data_hash: &'static str, + pub cardano__script_hash: &'static str, + pub cardano__script_invalid_before: &'static str, + pub cardano__script_invalid_hereafter: &'static str, + pub cardano__script_key: &'static str, + pub cardano__script_n_of_k: &'static str, + pub cardano__script_reward: &'static str, + pub cardano__sending: &'static str, + pub cardano__show_simple: &'static str, + pub cardano__sign_tx_path_template: &'static str, + pub cardano__stake_delegation: &'static str, + pub cardano__stake_deregistration: &'static str, + pub cardano__stake_pool_registration: &'static str, + pub cardano__stake_pool_registration_pool_id: &'static str, + pub cardano__stake_registration: &'static str, + pub cardano__staking_key_for_account: &'static str, + pub cardano__to_pool: &'static str, + pub cardano__token_minting_path: &'static str, + pub cardano__total_collateral: &'static str, + pub cardano__transaction: &'static str, + pub cardano__transaction_contains_minting_or_burning: &'static str, + pub cardano__transaction_contains_script_address_no_datum: &'static str, + pub cardano__transaction_fee: &'static str, + pub cardano__transaction_id: &'static str, + pub cardano__transaction_no_collateral_input: &'static str, + pub cardano__transaction_no_script_data_hash: &'static str, + pub cardano__transaction_output_contains_tokens: &'static str, + pub cardano__ttl: &'static str, + pub cardano__unknown: &'static str, + pub cardano__unknown_collateral_amount: &'static str, + pub cardano__unusual_path: &'static str, + pub cardano__valid_since: &'static str, + pub cardano__verify_script: &'static str, + pub cardano__vote_key_registration: &'static str, + pub cardano__vote_public_key: &'static str, + pub cardano__voting_purpose: &'static str, + pub cardano__warning: &'static str, + pub cardano__weight: &'static str, + pub cardano__withdrawal_for_address_template: &'static str, + pub cardano__witness_path: &'static str, + pub cardano__x_of_y_signatures_template: &'static str, + pub coinjoin__access_account: &'static str, + pub coinjoin__do_not_disconnect: &'static str, + pub coinjoin__max_mining_fee: &'static str, + pub coinjoin__max_rounds: &'static str, + pub coinjoin__title: &'static str, + pub coinjoin__title_do_not_disconnect: &'static str, + pub coinjoin__title_progress: &'static str, + pub coinjoin__waiting_for_others: &'static str, + pub confirm_total__account: &'static str, + pub confirm_total__fee_rate: &'static str, + pub confirm_total__sending_from_account: &'static str, + pub confirm_total__title_fee: &'static str, + pub confirm_total__title_sending_from: &'static str, + pub debug__loading_seed: &'static str, + pub debug__loading_seed_not_recommended: &'static str, + pub device_name__change_template: &'static str, + pub device_name__title: &'static str, + pub entropy__send: &'static str, + pub entropy__title: &'static str, + pub entropy__title_confirm: &'static str, + pub eos__about_to_sign_template: &'static str, + pub eos__account: &'static str, + pub eos__action_name: &'static str, + pub eos__amount: &'static str, + pub eos__arbitrary_data: &'static str, + pub eos__buy_ram: &'static str, + pub eos__bytes: &'static str, + pub eos__cancel_vote: &'static str, + pub eos__checksum: &'static str, + pub eos__code: &'static str, + pub eos__contract: &'static str, + pub eos__cpu: &'static str, + pub eos__creator: &'static str, + pub eos__delegate: &'static str, + pub eos__delete_auth: &'static str, + pub eos__from: &'static str, + pub eos__link_auth: &'static str, + pub eos__memo: &'static str, + pub eos__name: &'static str, + pub eos__net: &'static str, + pub eos__new_account: &'static str, + pub eos__no: &'static str, + pub eos__owner: &'static str, + pub eos__parent: &'static str, + pub eos__payer: &'static str, + pub eos__permission: &'static str, + pub eos__proxy: &'static str, + pub eos__receiver: &'static str, + pub eos__refund: &'static str, + pub eos__requirement: &'static str, + pub eos__sell_ram: &'static str, + pub eos__sender: &'static str, + pub eos__sign_transaction: &'static str, + pub eos__threshold: &'static str, + pub eos__to: &'static str, + pub eos__transfer: &'static str, + pub eos__type: &'static str, + pub eos__undelegate: &'static str, + pub eos__unlink_auth: &'static str, + pub eos__update_auth: &'static str, + pub eos__vote_for_producers: &'static str, + pub eos__vote_for_proxy: &'static str, + pub eos__voter: &'static str, + pub eos__yes: &'static str, + pub ethereum__amount_sent: &'static str, + pub ethereum__confirm_fee: &'static str, + pub ethereum__contract: &'static str, + pub ethereum__data_size_template: &'static str, + pub ethereum__gas_limit: &'static str, + pub ethereum__gas_price: &'static str, + pub ethereum__max_gas_price: &'static str, + pub ethereum__name_and_version: &'static str, + pub ethereum__new_contract: &'static str, + pub ethereum__no_message_field: &'static str, + pub ethereum__priority_fee: &'static str, + pub ethereum__show_full_array: &'static str, + pub ethereum__show_full_domain: &'static str, + pub ethereum__show_full_message: &'static str, + pub ethereum__show_full_struct: &'static str, + pub ethereum__sign_eip712: &'static str, + pub ethereum__title_confirm_data: &'static str, + pub ethereum__title_confirm_domain: &'static str, + pub ethereum__title_confirm_message: &'static str, + pub ethereum__title_confirm_struct: &'static str, + pub ethereum__title_confirm_typed_data: &'static str, + pub ethereum__title_signing_address: &'static str, + pub ethereum__units_template: &'static str, + pub ethereum__unknown_token: &'static str, + pub ethereum__valid_signature: &'static str, + pub experimental_mode__enable: &'static str, + pub experimental_mode__only_for_dev: &'static str, + pub experimental_mode__title: &'static str, + pub fido__already_registered: &'static str, + pub fido__device_already_registered: &'static str, + pub fido__device_already_registered_with_template: &'static str, + pub fido__device_not_registered: &'static str, + pub fido__does_not_belong: &'static str, + pub fido__erase_credentials: &'static str, + pub fido__export_credentials: &'static str, + pub fido__not_registered: &'static str, + pub fido__not_registered_with_template: &'static str, + pub fido__please_enable_pin_protection: &'static str, + pub fido__title_authenticate: &'static str, + pub fido__title_import_credential: &'static str, + pub fido__title_list_credentials: &'static str, + pub fido__title_register: &'static str, + pub fido__title_remove_credential: &'static str, + pub fido__title_reset: &'static str, + pub fido__title_u2f_auth: &'static str, + pub fido__title_u2f_register: &'static str, + pub fido__title_verify_user: &'static str, + pub fido__unable_to_verify_user: &'static str, + pub fido__wanna_erase_credentials: &'static str, + pub firmware_update__title: &'static str, + pub firmware_update__title_fingerprint: &'static str, + pub homescreen__click_to_connect: &'static str, + pub homescreen__click_to_unlock: &'static str, + pub homescreen__title_backup_failed: &'static str, + pub homescreen__title_backup_needed: &'static str, + pub homescreen__title_coinjoin_authorized: &'static str, + pub homescreen__title_experimental_mode: &'static str, + pub homescreen__title_hold_to_lock: &'static str, + pub homescreen__title_no_usb_connection: &'static str, + pub homescreen__title_pin_not_set: &'static str, + pub homescreen__title_seedless: &'static str, + pub homescreen__title_set: &'static str, + pub inputs__back: &'static str, + pub inputs__cancel: &'static str, + pub inputs__delete: &'static str, + pub inputs__enter: &'static str, + pub inputs__return: &'static str, + pub inputs__show: &'static str, + pub inputs__space: &'static str, + pub joint__title: &'static str, + pub joint__to_the_total_amount: &'static str, + pub joint__you_are_contributing: &'static str, + pub lockscreen__tap_to_connect: &'static str, + pub lockscreen__tap_to_unlock: &'static str, + pub lockscreen__title_locked: &'static str, + pub lockscreen__title_not_connected: &'static str, + pub misc__decrypt_value: &'static str, + pub misc__encrypt_value: &'static str, + pub misc__title_suite_labeling: &'static str, + pub modify_amount__address: &'static str, + pub modify_amount__decrease_amount: &'static str, + pub modify_amount__increase_amount: &'static str, + pub modify_amount__new_amount: &'static str, + pub modify_amount__title: &'static str, + pub modify_fee__decrease_fee: &'static str, + pub modify_fee__fee_rate: &'static str, + pub modify_fee__increase_fee: &'static str, + pub modify_fee__new_transaction_fee: &'static str, + pub modify_fee__no_change: &'static str, + pub modify_fee__title: &'static str, + pub modify_fee__transaction_fee: &'static str, + pub monero__confirm_export: &'static str, + pub monero__confirm_fee: &'static str, + pub monero__confirm_ki_sync: &'static str, + pub monero__confirm_refresh: &'static str, + pub monero__confirm_unlock_time: &'static str, + pub monero__hashing_inputs: &'static str, + pub monero__payment_id: &'static str, + pub monero__postprocessing: &'static str, + pub monero__processing: &'static str, + pub monero__processing_inputs: &'static str, + pub monero__processing_outputs: &'static str, + pub monero__signing: &'static str, + pub monero__signing_inputs: &'static str, + pub monero__unlock_time_set_template: &'static str, + pub monero__wanna_export_tx_der: &'static str, + pub monero__wanna_export_tx_key: &'static str, + pub monero__wanna_export_watchkey: &'static str, + pub monero__wanna_start_refresh: &'static str, + pub monero__wanna_sync_key_images: &'static str, + pub nem__absolute: &'static str, + pub nem__activate: &'static str, + pub nem__add: &'static str, + pub nem__confirm_action: &'static str, + pub nem__confirm_address: &'static str, + pub nem__confirm_creation_fee: &'static str, + pub nem__confirm_fee: &'static str, + pub nem__confirm_mosaic: &'static str, + pub nem__confirm_multisig_fee: &'static str, + pub nem__confirm_namespace: &'static str, + pub nem__confirm_payload: &'static str, + pub nem__confirm_properties: &'static str, + pub nem__confirm_rental_fee: &'static str, + pub nem__confirm_transfer_of: &'static str, + pub nem__convert_account_to_multisig: &'static str, + pub nem__cosign_transaction_for: &'static str, + pub nem__cosignatory: &'static str, + pub nem__create_mosaic: &'static str, + pub nem__create_namespace: &'static str, + pub nem__deactivate: &'static str, + pub nem__decrease: &'static str, + pub nem__description: &'static str, + pub nem__divisibility_and_levy_cannot_be_shown: &'static str, + pub nem__encrypted: &'static str, + pub nem__final_confirm: &'static str, + pub nem__immutable: &'static str, + pub nem__increase: &'static str, + pub nem__initial_supply: &'static str, + pub nem__initiate_transaction_for: &'static str, + pub nem__levy_divisibility: &'static str, + pub nem__levy_fee: &'static str, + pub nem__levy_fee_of: &'static str, + pub nem__levy_mosaic: &'static str, + pub nem__levy_namespace: &'static str, + pub nem__levy_recipient: &'static str, + pub nem__levy_type: &'static str, + pub nem__modify_supply_for: &'static str, + pub nem__modify_the_number_of_cosignatories_by: &'static str, + pub nem__mutable: &'static str, + pub nem__no: &'static str, + pub nem__of: &'static str, + pub nem__percentile: &'static str, + pub nem__raw_units_template: &'static str, + pub nem__remote_harvesting: &'static str, + pub nem__remove: &'static str, + pub nem__set_minimum_cosignatories_to: &'static str, + pub nem__sign_tx_fee_template: &'static str, + pub nem__supply_change: &'static str, + pub nem__supply_units_template: &'static str, + pub nem__transferable: &'static str, + pub nem__under_namespace: &'static str, + pub nem__unencrypted: &'static str, + pub nem__unknown_mosaic: &'static str, + pub nem__yes: &'static str, + pub passphrase__access_hidden_wallet: &'static str, + pub passphrase__always_on_device: &'static str, + pub passphrase__from_host_not_shown: &'static str, + pub passphrase__hidden_wallet: &'static str, + pub passphrase__hide: &'static str, + pub passphrase__next_screen_will_show_passphrase: &'static str, + pub passphrase__please_enter: &'static str, + pub passphrase__revoke_on_device: &'static str, + pub passphrase__title_confirm: &'static str, + pub passphrase__title_enter: &'static str, + pub passphrase__title_hide: &'static str, + pub passphrase__title_settings: &'static str, + pub passphrase__title_source: &'static str, + pub passphrase__turn_off: &'static str, + pub passphrase__turn_on: &'static str, + pub pin__change: &'static str, + pub pin__changed: &'static str, + pub pin__cursor_will_change: &'static str, + pub pin__diff_from_wipe_code: &'static str, + pub pin__disabled: &'static str, + pub pin__enabled: &'static str, + pub pin__enter: &'static str, + pub pin__enter_new: &'static str, + pub pin__entered_not_valid: &'static str, + pub pin__info: &'static str, + pub pin__invalid_pin: &'static str, + pub pin__last_attempt: &'static str, + pub pin__mismatch: &'static str, + pub pin__pin_mismatch: &'static str, + pub pin__please_check_again: &'static str, + pub pin__reenter_new: &'static str, + pub pin__reenter_to_confirm: &'static str, + pub pin__should_be_long: &'static str, + pub pin__title_check_pin: &'static str, + pub pin__title_settings: &'static str, + pub pin__title_wrong_pin: &'static str, + pub pin__tries_left: &'static str, + pub pin__turn_off: &'static str, + pub pin__turn_on: &'static str, + pub pin__wrong_pin: &'static str, + pub progress__authenticity_check: &'static str, + pub progress__done: &'static str, + pub progress__loading_transaction: &'static str, + pub progress__one_second_left: &'static str, + pub progress__please_wait: &'static str, + pub progress__processing: &'static str, + pub progress__refreshing: &'static str, + pub progress__signing_transaction: &'static str, + pub progress__syncing: &'static str, + pub progress__x_seconds_left_template: &'static str, + pub reboot_to_bootloader__restart: &'static str, + pub reboot_to_bootloader__title: &'static str, + pub reboot_to_bootloader__version_by_template: &'static str, + pub recovery__cancel_dry_run: &'static str, + pub recovery__check_dry_run: &'static str, + pub recovery__cursor_will_change: &'static str, + pub recovery__dry_run_bip39_valid_match: &'static str, + pub recovery__dry_run_bip39_valid_mismatch: &'static str, + pub recovery__dry_run_slip39_valid_match: &'static str, + pub recovery__dry_run_slip39_valid_mismatch: &'static str, + pub recovery__enter_any_share: &'static str, + pub recovery__enter_backup: &'static str, + pub recovery__enter_different_share: &'static str, + pub recovery__enter_share_from_diff_group: &'static str, + pub recovery__group_num_template: &'static str, + pub recovery__group_threshold_reached: &'static str, + pub recovery__invalid_seed_entered: &'static str, + pub recovery__invalid_share_entered: &'static str, + pub recovery__more_shares_needed: &'static str, + pub recovery__num_of_words: &'static str, + pub recovery__only_first_n_letters: &'static str, + pub recovery__progress_will_be_lost: &'static str, + pub recovery__select_num_of_words: &'static str, + pub recovery__share_already_entered: &'static str, + pub recovery__share_from_another_shamir: &'static str, + pub recovery__share_num_template: &'static str, + pub recovery__title: &'static str, + pub recovery__title_cancel_dry_run: &'static str, + pub recovery__title_cancel_recovery: &'static str, + pub recovery__title_dry_run: &'static str, + pub recovery__title_recover: &'static str, + pub recovery__title_remaining_shares: &'static str, + pub recovery__type_word_x_of_y_template: &'static str, + pub recovery__wallet_recovered: &'static str, + pub recovery__wanna_cancel_dry_run: &'static str, + pub recovery__wanna_cancel_recovery: &'static str, + pub recovery__word_count_template: &'static str, + pub recovery__word_x_of_y_template: &'static str, + pub recovery__x_of_y_entered_template: &'static str, + pub recovery__you_have_entered: &'static str, + pub reset__advanced_group_threshold_info: &'static str, + pub reset__all_x_of_y_template: &'static str, + pub reset__any_x_of_y_template: &'static str, + pub reset__button_create: &'static str, + pub reset__button_recover: &'static str, + pub reset__by_continuing: &'static str, + pub reset__check_backup_title: &'static str, + pub reset__check_group_share_title_template: &'static str, + pub reset__check_seed_title: &'static str, + pub reset__check_share_title_template: &'static str, + pub reset__continue_with_next_share: &'static str, + pub reset__continue_with_share_template: &'static str, + pub reset__finished_verifying_group_template: &'static str, + pub reset__finished_verifying_seed: &'static str, + pub reset__finished_verifying_shares: &'static str, + pub reset__group_description: &'static str, + pub reset__group_info: &'static str, + pub reset__group_share_checked_successfully_template: &'static str, + pub reset__group_share_title_template: &'static str, + pub reset__more_info_at: &'static str, + pub reset__need_all_share_template: &'static str, + pub reset__need_any_share_template: &'static str, + pub reset__needed_to_form_a_group: &'static str, + pub reset__needed_to_recover_your_wallet: &'static str, + pub reset__never_make_digital_copy: &'static str, + pub reset__num_of_share_holders_template: &'static str, + pub reset__num_of_shares_advanced_info_template: &'static str, + pub reset__num_of_shares_basic_info: &'static str, + pub reset__num_shares_for_group_template: &'static str, + pub reset__number_of_shares_info: &'static str, + pub reset__one_share: &'static str, + pub reset__only_one_share_will_be_created: &'static str, + pub reset__recovery_seed_title: &'static str, + pub reset__recovery_share_title_template: &'static str, + pub reset__required_number_of_groups: &'static str, + pub reset__select_correct_word: &'static str, + pub reset__select_word_template: &'static str, + pub reset__select_word_x_of_y_template: &'static str, + pub reset__set_it_to_count_template: &'static str, + pub reset__share_checked_successfully_template: &'static str, + pub reset__share_words_title: &'static str, + pub reset__slip39_checklist_num_groups: &'static str, + pub reset__slip39_checklist_num_shares: &'static str, + pub reset__slip39_checklist_set_num_groups: &'static str, + pub reset__slip39_checklist_set_num_shares: &'static str, + pub reset__slip39_checklist_set_sizes: &'static str, + pub reset__slip39_checklist_set_sizes_longer: &'static str, + pub reset__slip39_checklist_set_threshold: &'static str, + pub reset__slip39_checklist_title: &'static str, + pub reset__slip39_checklist_write_down: &'static str, + pub reset__slip39_checklist_write_down_recovery: &'static str, + pub reset__the_threshold_sets_the_number_of_shares: &'static str, + pub reset__threshold_info: &'static str, + pub reset__title_backup_is_done: &'static str, + pub reset__title_create_wallet: &'static str, + pub reset__title_create_wallet_shamir: &'static str, + pub reset__title_group_threshold: &'static str, + pub reset__title_number_of_groups: &'static str, + pub reset__title_number_of_shares: &'static str, + pub reset__title_set_group_threshold: &'static str, + pub reset__title_set_number_of_groups: &'static str, + pub reset__title_set_number_of_shares: &'static str, + pub reset__title_set_threshold: &'static str, + pub reset__to_form_group_template: &'static str, + pub reset__tos_link: &'static str, + pub reset__total_number_of_shares_in_group_template: &'static str, + pub reset__use_your_backup: &'static str, + pub reset__write_down_words_template: &'static str, + pub reset__wrong_word_selected: &'static str, + pub reset__you_need_one_share: &'static str, + pub reset__your_backup_is_done: &'static str, + pub ripple__confirm_tag: &'static str, + pub ripple__destination_tag_template: &'static str, + pub rotation__change_template: &'static str, + pub rotation__east: &'static str, + pub rotation__north: &'static str, + pub rotation__south: &'static str, + pub rotation__title_change: &'static str, + pub rotation__west: &'static str, + pub safety_checks__approve_unsafe_always: &'static str, + pub safety_checks__approve_unsafe_temporary: &'static str, + pub safety_checks__enforce_strict: &'static str, + pub safety_checks__title: &'static str, + pub safety_checks__title_safety_override: &'static str, + pub sd_card__all_data_will_be_lost: &'static str, + pub sd_card__card_required: &'static str, + pub sd_card__disable: &'static str, + pub sd_card__disabled: &'static str, + pub sd_card__enable: &'static str, + pub sd_card__enabled: &'static str, + pub sd_card__error: &'static str, + pub sd_card__format_card: &'static str, + pub sd_card__insert_correct_card: &'static str, + pub sd_card__please_insert: &'static str, + pub sd_card__please_unplug_and_insert: &'static str, + pub sd_card__problem_accessing: &'static str, + pub sd_card__refresh: &'static str, + pub sd_card__refreshed: &'static str, + pub sd_card__restart: &'static str, + pub sd_card__title: &'static str, + pub sd_card__title_problem: &'static str, + pub sd_card__unknown_filesystem: &'static str, + pub sd_card__unplug_and_insert_correct: &'static str, + pub sd_card__use_different_card: &'static str, + pub sd_card__wanna_format: &'static str, + pub sd_card__wrong_sd_card: &'static str, + pub send__address_path: &'static str, + pub send__amount: &'static str, + pub send__confirm_sending: &'static str, + pub send__from_multiple_accounts: &'static str, + pub send__including_fee: &'static str, + pub send__maximum_fee: &'static str, + pub send__receiving_to_multisig: &'static str, + pub send__title_amount: &'static str, + pub send__title_confirm_sending: &'static str, + pub send__title_joint_transaction: &'static str, + pub send__title_receiving_to: &'static str, + pub send__title_recipient: &'static str, + pub send__title_sending: &'static str, + pub send__title_sending_amount: &'static str, + pub send__title_sending_to: &'static str, + pub send__to_the_total_amount: &'static str, + pub send__total_amount: &'static str, + pub send__transaction_id: &'static str, + pub send__you_are_contributing: &'static str, + pub share_words__words_in_order: &'static str, + pub share_words__wrote_down_all: &'static str, + pub sign_message__bytes_template: &'static str, + pub sign_message__confirm_address: &'static str, + pub sign_message__confirm_message: &'static str, + pub sign_message__message_size: &'static str, + pub sign_message__verify_address: &'static str, + pub stellar__account: &'static str, + pub stellar__account_merge: &'static str, + pub stellar__account_thresholds: &'static str, + pub stellar__add_signer: &'static str, + pub stellar__add_trust: &'static str, + pub stellar__all_will_be_sent_to: &'static str, + pub stellar__allow_trust: &'static str, + pub stellar__asset: &'static str, + pub stellar__bump_sequence: &'static str, + pub stellar__buying: &'static str, + pub stellar__clear_data: &'static str, + pub stellar__clear_flags: &'static str, + pub stellar__confirm_issuer: &'static str, + pub stellar__confirm_memo: &'static str, + pub stellar__confirm_network: &'static str, + pub stellar__confirm_operation: &'static str, + pub stellar__confirm_stellar: &'static str, + pub stellar__confirm_timebounds: &'static str, + pub stellar__create_account: &'static str, + pub stellar__debited_amount: &'static str, + pub stellar__delete: &'static str, + pub stellar__delete_passive_offer: &'static str, + pub stellar__delete_trust: &'static str, + pub stellar__destination: &'static str, + pub stellar__exchanges_require_memo: &'static str, + pub stellar__final_confirm: &'static str, + pub stellar__hash: &'static str, + pub stellar__high: &'static str, + pub stellar__home_domain: &'static str, + pub stellar__inflation: &'static str, + pub stellar__initial_balance: &'static str, + pub stellar__initialize_signing_with: &'static str, + pub stellar__issuer_template: &'static str, + pub stellar__key: &'static str, + pub stellar__limit: &'static str, + pub stellar__low: &'static str, + pub stellar__master_weight: &'static str, + pub stellar__medium: &'static str, + pub stellar__new_offer: &'static str, + pub stellar__new_passive_offer: &'static str, + pub stellar__no_memo_set: &'static str, + pub stellar__no_restriction: &'static str, + pub stellar__on_network_template: &'static str, + pub stellar__path_pay: &'static str, + pub stellar__path_pay_at_least: &'static str, + pub stellar__pay: &'static str, + pub stellar__pay_at_most: &'static str, + pub stellar__preauth_transaction: &'static str, + pub stellar__price_per_template: &'static str, + pub stellar__private_network: &'static str, + pub stellar__remove_signer: &'static str, + pub stellar__revoke_trust: &'static str, + pub stellar__selling: &'static str, + pub stellar__set_data: &'static str, + pub stellar__set_flags: &'static str, + pub stellar__set_sequence_to_template: &'static str, + pub stellar__sign_tx_count_template: &'static str, + pub stellar__sign_tx_fee_template: &'static str, + pub stellar__source_account: &'static str, + pub stellar__testnet_network: &'static str, + pub stellar__trusted_account: &'static str, + pub stellar__update: &'static str, + pub stellar__valid_from: &'static str, + pub stellar__valid_to: &'static str, + pub stellar__value_sha256: &'static str, + pub stellar__wanna_clean_value_key_template: &'static str, + pub stellar__your_account: &'static str, + pub tezos__address: &'static str, + pub tezos__amount: &'static str, + pub tezos__baker_address: &'static str, + pub tezos__balance: &'static str, + pub tezos__ballot: &'static str, + pub tezos__confirm_delegation: &'static str, + pub tezos__confirm_origination: &'static str, + pub tezos__delegator: &'static str, + pub tezos__fee: &'static str, + pub tezos__proposal: &'static str, + pub tezos__register_delegate: &'static str, + pub tezos__remove_delegation: &'static str, + pub tezos__submit_ballot: &'static str, + pub tezos__submit_proposal: &'static str, + pub tezos__submit_proposals: &'static str, + pub tutorial__middle_click: &'static str, + pub tutorial__press_and_hold: &'static str, + pub tutorial__ready_to_use: &'static str, + pub tutorial__scroll_down: &'static str, + pub tutorial__sure_you_want_skip: &'static str, + pub tutorial__title_hello: &'static str, + pub tutorial__title_screen_scroll: &'static str, + pub tutorial__title_skip: &'static str, + pub tutorial__title_tutorial_complete: &'static str, + pub tutorial__use_trezor: &'static str, + pub tutorial__welcome_press_right: &'static str, + pub u2f__get: &'static str, + pub u2f__set_template: &'static str, + pub u2f__title_get: &'static str, + pub u2f__title_set: &'static str, + pub wipe__info: &'static str, + pub wipe__title: &'static str, + pub wipe__want_to_wipe: &'static str, + pub wipe_code__change: &'static str, + pub wipe_code__changed: &'static str, + pub wipe_code__diff_from_pin: &'static str, + pub wipe_code__disabled: &'static str, + pub wipe_code__enabled: &'static str, + pub wipe_code__enter_new: &'static str, + pub wipe_code__info: &'static str, + pub wipe_code__invalid: &'static str, + pub wipe_code__mismatch: &'static str, + pub wipe_code__reenter: &'static str, + pub wipe_code__reenter_to_confirm: &'static str, + pub wipe_code__title_check: &'static str, + pub wipe_code__title_invalid: &'static str, + pub wipe_code__title_settings: &'static str, + pub wipe_code__turn_off: &'static str, + pub wipe_code__turn_on: &'static str, + pub wipe_code__wipe_code_mismatch: &'static str, + pub word_count__title: &'static str, + pub words__are_you_sure: &'static str, + pub words__buying: &'static str, + pub words__continue_anyway: &'static str, + pub words__continue_with: &'static str, + pub words__error: &'static str, + pub words__from: &'static str, + pub words__keep_it_safe: &'static str, + pub words__know_what_your_doing: &'static str, + pub words__my_trezor: &'static str, + pub words__outputs: &'static str, + pub words__please_check_again: &'static str, + pub words__please_try_again: &'static str, + pub words__really_wanna: &'static str, + pub words__sign: &'static str, + pub words__title_check: &'static str, + pub words__title_group: &'static str, + pub words__title_information: &'static str, + pub words__title_remember: &'static str, + pub words__title_share: &'static str, + pub words__title_shares: &'static str, + pub words__title_success: &'static str, + pub words__title_summary: &'static str, + pub words__title_threshold: &'static str, + pub words__unknown: &'static str, + pub words__warning: &'static str, + pub plurals__sign_x_actions: &'static str, + pub plurals__contains_x_keys: &'static str, + pub words__contains: &'static str, + pub plurals__x_shares_needed: &'static str, + pub words__array_of: &'static str, + pub recovery__x_more_shares_needed_template_plural: &'static str, + pub plurals__transaction_of_x_operations: &'static str, + pub plurals__x_groups_needed: &'static str, + pub recovery__x_more_items_starting_template_plural: &'static str, + pub plurals__lock_after_x_hours: &'static str, + pub plurals__lock_after_x_milliseconds: &'static str, + pub plurals__lock_after_x_minutes: &'static str, + pub plurals__lock_after_x_seconds: &'static str, + pub words__confirm: &'static str, +} + +#[rustfmt::skip] +impl TranslationsGeneral { + pub fn get_text(&self, key: &str) -> Option<&'static str> { + self.get_info(key).map(|(text, _)| text) + } + + pub fn get_position(&self, key: &str) -> Option { + self.get_info(key).map(|(_, pos)| pos) + } + + fn get_info(&self, key: &str) -> Option<(&'static str, usize)> { + match key { + "addr_mismatch__contact_support" => Some((self.addr_mismatch__contact_support, 0)), + "addr_mismatch__key_mismatch" => Some((self.addr_mismatch__key_mismatch, 1)), + "addr_mismatch__mismatch" => Some((self.addr_mismatch__mismatch, 2)), + "addr_mismatch__support_url" => Some((self.addr_mismatch__support_url, 3)), + "addr_mismatch__title" => Some((self.addr_mismatch__title, 4)), + "addr_mismatch__title_key_mismatch" => Some((self.addr_mismatch__title_key_mismatch, 5)), + "addr_mismatch__wrong_derication_path" => Some((self.addr_mismatch__wrong_derication_path, 6)), + "addr_mismatch__xpub_mismatch" => Some((self.addr_mismatch__xpub_mismatch, 7)), + "address__address" => Some((self.address__address, 8)), + "address__public_key" => Some((self.address__public_key, 9)), + "address__title_cosigner" => Some((self.address__title_cosigner, 10)), + "address__title_receive_address" => Some((self.address__title_receive_address, 11)), + "address__title_yours" => Some((self.address__title_yours, 12)), + "address_details__account" => Some((self.address_details__account, 13)), + "address_details__derivation_path" => Some((self.address_details__derivation_path, 14)), + "address_details__title_receive_address" => Some((self.address_details__title_receive_address, 15)), + "address_details__title_receiving_to" => Some((self.address_details__title_receiving_to, 16)), + "authenticate__confirm_template" => Some((self.authenticate__confirm_template, 17)), + "authenticate__header" => Some((self.authenticate__header, 18)), + "auto_lock__change_template" => Some((self.auto_lock__change_template, 19)), + "auto_lock__title" => Some((self.auto_lock__title, 20)), + "backup__can_back_up_anytime" => Some((self.backup__can_back_up_anytime, 21)), + "backup__it_should_be_backed_up" => Some((self.backup__it_should_be_backed_up, 22)), + "backup__it_should_be_backed_up_now" => Some((self.backup__it_should_be_backed_up_now, 23)), + "backup__new_wallet_created" => Some((self.backup__new_wallet_created, 24)), + "backup__new_wallet_successfully_created" => Some((self.backup__new_wallet_successfully_created, 25)), + "backup__recover_anytime" => Some((self.backup__recover_anytime, 26)), + "backup__title_backup_wallet" => Some((self.backup__title_backup_wallet, 27)), + "backup__title_skip" => Some((self.backup__title_skip, 28)), + "backup__want_to_skip" => Some((self.backup__want_to_skip, 29)), + "binance__buy" => Some((self.binance__buy, 30)), + "binance__confirm_cancel" => Some((self.binance__confirm_cancel, 31)), + "binance__confirm_input" => Some((self.binance__confirm_input, 32)), + "binance__confirm_order" => Some((self.binance__confirm_order, 33)), + "binance__confirm_output" => Some((self.binance__confirm_output, 34)), + "binance__order_id" => Some((self.binance__order_id, 35)), + "binance__pair" => Some((self.binance__pair, 36)), + "binance__price" => Some((self.binance__price, 37)), + "binance__quantity" => Some((self.binance__quantity, 38)), + "binance__sell" => Some((self.binance__sell, 39)), + "binance__sender_address" => Some((self.binance__sender_address, 40)), + "binance__side" => Some((self.binance__side, 41)), + "binance__unknown" => Some((self.binance__unknown, 42)), + "bitcoin__commitment_data" => Some((self.bitcoin__commitment_data, 43)), + "bitcoin__confirm_locktime" => Some((self.bitcoin__confirm_locktime, 44)), + "bitcoin__create_proof_of_ownership" => Some((self.bitcoin__create_proof_of_ownership, 45)), + "bitcoin__high_mining_fee_template" => Some((self.bitcoin__high_mining_fee_template, 46)), + "bitcoin__locktime_no_effect" => Some((self.bitcoin__locktime_no_effect, 47)), + "bitcoin__locktime_set_to" => Some((self.bitcoin__locktime_set_to, 48)), + "bitcoin__locktime_set_to_blockheight" => Some((self.bitcoin__locktime_set_to_blockheight, 49)), + "bitcoin__lot_of_change_outputs" => Some((self.bitcoin__lot_of_change_outputs, 50)), + "bitcoin__multiple_accounts" => Some((self.bitcoin__multiple_accounts, 51)), + "bitcoin__new_fee_rate" => Some((self.bitcoin__new_fee_rate, 52)), + "bitcoin__simple_send_of" => Some((self.bitcoin__simple_send_of, 53)), + "bitcoin__ticket_amount" => Some((self.bitcoin__ticket_amount, 54)), + "bitcoin__title_confirm_details" => Some((self.bitcoin__title_confirm_details, 55)), + "bitcoin__title_finalize_transaction" => Some((self.bitcoin__title_finalize_transaction, 56)), + "bitcoin__title_high_mining_fee" => Some((self.bitcoin__title_high_mining_fee, 57)), + "bitcoin__title_meld_transaction" => Some((self.bitcoin__title_meld_transaction, 58)), + "bitcoin__title_modify_amount" => Some((self.bitcoin__title_modify_amount, 59)), + "bitcoin__title_payjoin" => Some((self.bitcoin__title_payjoin, 60)), + "bitcoin__title_proof_of_ownership" => Some((self.bitcoin__title_proof_of_ownership, 61)), + "bitcoin__title_purchase_ticket" => Some((self.bitcoin__title_purchase_ticket, 62)), + "bitcoin__title_update_transaction" => Some((self.bitcoin__title_update_transaction, 63)), + "bitcoin__unknown_path" => Some((self.bitcoin__unknown_path, 64)), + "bitcoin__unknown_transaction" => Some((self.bitcoin__unknown_transaction, 65)), + "bitcoin__unusually_high_fee" => Some((self.bitcoin__unusually_high_fee, 66)), + "bitcoin__unverified_external_inputs" => Some((self.bitcoin__unverified_external_inputs, 67)), + "bitcoin__valid_signature" => Some((self.bitcoin__valid_signature, 68)), + "bitcoin__voting_rights" => Some((self.bitcoin__voting_rights, 69)), + "buttons__abort" => Some((self.buttons__abort, 70)), + "buttons__access" => Some((self.buttons__access, 71)), + "buttons__again" => Some((self.buttons__again, 72)), + "buttons__allow" => Some((self.buttons__allow, 73)), + "buttons__back_up" => Some((self.buttons__back_up, 74)), + "buttons__cancel" => Some((self.buttons__cancel, 75)), + "buttons__change" => Some((self.buttons__change, 76)), + "buttons__check" => Some((self.buttons__check, 77)), + "buttons__check_again" => Some((self.buttons__check_again, 78)), + "buttons__close" => Some((self.buttons__close, 79)), + "buttons__confirm" => Some((self.buttons__confirm, 80)), + "buttons__continue" => Some((self.buttons__continue, 81)), + "buttons__details" => Some((self.buttons__details, 82)), + "buttons__enable" => Some((self.buttons__enable, 83)), + "buttons__enter" => Some((self.buttons__enter, 84)), + "buttons__enter_share" => Some((self.buttons__enter_share, 85)), + "buttons__export" => Some((self.buttons__export, 86)), + "buttons__format" => Some((self.buttons__format, 87)), + "buttons__go_back" => Some((self.buttons__go_back, 88)), + "buttons__hold_to_confirm" => Some((self.buttons__hold_to_confirm, 89)), + "buttons__info" => Some((self.buttons__info, 90)), + "buttons__install" => Some((self.buttons__install, 91)), + "buttons__more_info" => Some((self.buttons__more_info, 92)), + "buttons__ok_i_understand" => Some((self.buttons__ok_i_understand, 93)), + "buttons__purchase" => Some((self.buttons__purchase, 94)), + "buttons__quit" => Some((self.buttons__quit, 95)), + "buttons__restart" => Some((self.buttons__restart, 96)), + "buttons__retry" => Some((self.buttons__retry, 97)), + "buttons__select" => Some((self.buttons__select, 98)), + "buttons__set" => Some((self.buttons__set, 99)), + "buttons__show_all" => Some((self.buttons__show_all, 100)), + "buttons__show_words" => Some((self.buttons__show_words, 101)), + "buttons__skip" => Some((self.buttons__skip, 102)), + "buttons__try_again" => Some((self.buttons__try_again, 103)), + "buttons__turn_off" => Some((self.buttons__turn_off, 104)), + "buttons__turn_on" => Some((self.buttons__turn_on, 105)), + "cardano__addr_base" => Some((self.cardano__addr_base, 106)), + "cardano__addr_enterprise" => Some((self.cardano__addr_enterprise, 107)), + "cardano__addr_legacy" => Some((self.cardano__addr_legacy, 108)), + "cardano__addr_pointer" => Some((self.cardano__addr_pointer, 109)), + "cardano__addr_reward" => Some((self.cardano__addr_reward, 110)), + "cardano__address_no_staking" => Some((self.cardano__address_no_staking, 111)), + "cardano__amount" => Some((self.cardano__amount, 112)), + "cardano__amount_burned_decimals_unknown" => Some((self.cardano__amount_burned_decimals_unknown, 113)), + "cardano__amount_minted_decimals_unknown" => Some((self.cardano__amount_minted_decimals_unknown, 114)), + "cardano__amount_sent_decimals_unknown" => Some((self.cardano__amount_sent_decimals_unknown, 115)), + "cardano__anonymous_pool" => Some((self.cardano__anonymous_pool, 116)), + "cardano__asset_fingerprint" => Some((self.cardano__asset_fingerprint, 117)), + "cardano__auxiliary_data_hash" => Some((self.cardano__auxiliary_data_hash, 118)), + "cardano__block" => Some((self.cardano__block, 119)), + "cardano__catalyst" => Some((self.cardano__catalyst, 120)), + "cardano__certificate" => Some((self.cardano__certificate, 121)), + "cardano__certificate_path" => Some((self.cardano__certificate_path, 122)), + "cardano__change_output" => Some((self.cardano__change_output, 123)), + "cardano__change_output_path" => Some((self.cardano__change_output_path, 124)), + "cardano__change_output_staking_path" => Some((self.cardano__change_output_staking_path, 125)), + "cardano__check_all_items" => Some((self.cardano__check_all_items, 126)), + "cardano__choose_level_of_details" => Some((self.cardano__choose_level_of_details, 127)), + "cardano__collateral_input_id" => Some((self.cardano__collateral_input_id, 128)), + "cardano__collateral_input_index" => Some((self.cardano__collateral_input_index, 129)), + "cardano__collateral_output_contains_tokens" => Some((self.cardano__collateral_output_contains_tokens, 130)), + "cardano__collateral_return" => Some((self.cardano__collateral_return, 131)), + "cardano__confirm" => Some((self.cardano__confirm, 132)), + "cardano__confirm_signing_stake_pool" => Some((self.cardano__confirm_signing_stake_pool, 133)), + "cardano__confirm_transaction" => Some((self.cardano__confirm_transaction, 134)), + "cardano__confirming_a_multisig_transaction" => Some((self.cardano__confirming_a_multisig_transaction, 135)), + "cardano__confirming_pool_registration" => Some((self.cardano__confirming_pool_registration, 136)), + "cardano__confirming_transction" => Some((self.cardano__confirming_transction, 137)), + "cardano__cost" => Some((self.cardano__cost, 138)), + "cardano__credential_mismatch" => Some((self.cardano__credential_mismatch, 139)), + "cardano__datum_hash" => Some((self.cardano__datum_hash, 140)), + "cardano__delegating_to" => Some((self.cardano__delegating_to, 141)), + "cardano__for_account_and_index_template" => Some((self.cardano__for_account_and_index_template, 142)), + "cardano__for_account_template" => Some((self.cardano__for_account_template, 143)), + "cardano__for_key_hash" => Some((self.cardano__for_key_hash, 144)), + "cardano__for_script" => Some((self.cardano__for_script, 145)), + "cardano__inline_datum" => Some((self.cardano__inline_datum, 146)), + "cardano__input_id" => Some((self.cardano__input_id, 147)), + "cardano__input_index" => Some((self.cardano__input_index, 148)), + "cardano__intro_text_address" => Some((self.cardano__intro_text_address, 149)), + "cardano__intro_text_change" => Some((self.cardano__intro_text_change, 150)), + "cardano__intro_text_owned_by_device" => Some((self.cardano__intro_text_owned_by_device, 151)), + "cardano__intro_text_registration_payment" => Some((self.cardano__intro_text_registration_payment, 152)), + "cardano__key_hash" => Some((self.cardano__key_hash, 153)), + "cardano__margin" => Some((self.cardano__margin, 154)), + "cardano__multisig_path" => Some((self.cardano__multisig_path, 155)), + "cardano__nested_scripts_template" => Some((self.cardano__nested_scripts_template, 156)), + "cardano__network" => Some((self.cardano__network, 157)), + "cardano__no_output_tx" => Some((self.cardano__no_output_tx, 158)), + "cardano__nonce" => Some((self.cardano__nonce, 159)), + "cardano__other" => Some((self.cardano__other, 160)), + "cardano__path" => Some((self.cardano__path, 161)), + "cardano__pledge" => Some((self.cardano__pledge, 162)), + "cardano__pointer" => Some((self.cardano__pointer, 163)), + "cardano__policy_id" => Some((self.cardano__policy_id, 164)), + "cardano__pool_metadata_hash" => Some((self.cardano__pool_metadata_hash, 165)), + "cardano__pool_metadata_url" => Some((self.cardano__pool_metadata_url, 166)), + "cardano__pool_owner" => Some((self.cardano__pool_owner, 167)), + "cardano__pool_owner_path" => Some((self.cardano__pool_owner_path, 168)), + "cardano__pool_reward_account" => Some((self.cardano__pool_reward_account, 169)), + "cardano__reference_input_id" => Some((self.cardano__reference_input_id, 170)), + "cardano__reference_input_index" => Some((self.cardano__reference_input_index, 171)), + "cardano__reference_script" => Some((self.cardano__reference_script, 172)), + "cardano__required_signer" => Some((self.cardano__required_signer, 173)), + "cardano__reward" => Some((self.cardano__reward, 174)), + "cardano__reward_address" => Some((self.cardano__reward_address, 175)), + "cardano__reward_eligibility_warning" => Some((self.cardano__reward_eligibility_warning, 176)), + "cardano__rewards_go_to" => Some((self.cardano__rewards_go_to, 177)), + "cardano__script" => Some((self.cardano__script, 178)), + "cardano__script_all" => Some((self.cardano__script_all, 179)), + "cardano__script_any" => Some((self.cardano__script_any, 180)), + "cardano__script_data_hash" => Some((self.cardano__script_data_hash, 181)), + "cardano__script_hash" => Some((self.cardano__script_hash, 182)), + "cardano__script_invalid_before" => Some((self.cardano__script_invalid_before, 183)), + "cardano__script_invalid_hereafter" => Some((self.cardano__script_invalid_hereafter, 184)), + "cardano__script_key" => Some((self.cardano__script_key, 185)), + "cardano__script_n_of_k" => Some((self.cardano__script_n_of_k, 186)), + "cardano__script_reward" => Some((self.cardano__script_reward, 187)), + "cardano__sending" => Some((self.cardano__sending, 188)), + "cardano__show_simple" => Some((self.cardano__show_simple, 189)), + "cardano__sign_tx_path_template" => Some((self.cardano__sign_tx_path_template, 190)), + "cardano__stake_delegation" => Some((self.cardano__stake_delegation, 191)), + "cardano__stake_deregistration" => Some((self.cardano__stake_deregistration, 192)), + "cardano__stake_pool_registration" => Some((self.cardano__stake_pool_registration, 193)), + "cardano__stake_pool_registration_pool_id" => Some((self.cardano__stake_pool_registration_pool_id, 194)), + "cardano__stake_registration" => Some((self.cardano__stake_registration, 195)), + "cardano__staking_key_for_account" => Some((self.cardano__staking_key_for_account, 196)), + "cardano__to_pool" => Some((self.cardano__to_pool, 197)), + "cardano__token_minting_path" => Some((self.cardano__token_minting_path, 198)), + "cardano__total_collateral" => Some((self.cardano__total_collateral, 199)), + "cardano__transaction" => Some((self.cardano__transaction, 200)), + "cardano__transaction_contains_minting_or_burning" => Some((self.cardano__transaction_contains_minting_or_burning, 201)), + "cardano__transaction_contains_script_address_no_datum" => Some((self.cardano__transaction_contains_script_address_no_datum, 202)), + "cardano__transaction_fee" => Some((self.cardano__transaction_fee, 203)), + "cardano__transaction_id" => Some((self.cardano__transaction_id, 204)), + "cardano__transaction_no_collateral_input" => Some((self.cardano__transaction_no_collateral_input, 205)), + "cardano__transaction_no_script_data_hash" => Some((self.cardano__transaction_no_script_data_hash, 206)), + "cardano__transaction_output_contains_tokens" => Some((self.cardano__transaction_output_contains_tokens, 207)), + "cardano__ttl" => Some((self.cardano__ttl, 208)), + "cardano__unknown" => Some((self.cardano__unknown, 209)), + "cardano__unknown_collateral_amount" => Some((self.cardano__unknown_collateral_amount, 210)), + "cardano__unusual_path" => Some((self.cardano__unusual_path, 211)), + "cardano__valid_since" => Some((self.cardano__valid_since, 212)), + "cardano__verify_script" => Some((self.cardano__verify_script, 213)), + "cardano__vote_key_registration" => Some((self.cardano__vote_key_registration, 214)), + "cardano__vote_public_key" => Some((self.cardano__vote_public_key, 215)), + "cardano__voting_purpose" => Some((self.cardano__voting_purpose, 216)), + "cardano__warning" => Some((self.cardano__warning, 217)), + "cardano__weight" => Some((self.cardano__weight, 218)), + "cardano__withdrawal_for_address_template" => Some((self.cardano__withdrawal_for_address_template, 219)), + "cardano__witness_path" => Some((self.cardano__witness_path, 220)), + "cardano__x_of_y_signatures_template" => Some((self.cardano__x_of_y_signatures_template, 221)), + "coinjoin__access_account" => Some((self.coinjoin__access_account, 222)), + "coinjoin__do_not_disconnect" => Some((self.coinjoin__do_not_disconnect, 223)), + "coinjoin__max_mining_fee" => Some((self.coinjoin__max_mining_fee, 224)), + "coinjoin__max_rounds" => Some((self.coinjoin__max_rounds, 225)), + "coinjoin__title" => Some((self.coinjoin__title, 226)), + "coinjoin__title_do_not_disconnect" => Some((self.coinjoin__title_do_not_disconnect, 227)), + "coinjoin__title_progress" => Some((self.coinjoin__title_progress, 228)), + "coinjoin__waiting_for_others" => Some((self.coinjoin__waiting_for_others, 229)), + "confirm_total__account" => Some((self.confirm_total__account, 230)), + "confirm_total__fee_rate" => Some((self.confirm_total__fee_rate, 231)), + "confirm_total__sending_from_account" => Some((self.confirm_total__sending_from_account, 232)), + "confirm_total__title_fee" => Some((self.confirm_total__title_fee, 233)), + "confirm_total__title_sending_from" => Some((self.confirm_total__title_sending_from, 234)), + "debug__loading_seed" => Some((self.debug__loading_seed, 235)), + "debug__loading_seed_not_recommended" => Some((self.debug__loading_seed_not_recommended, 236)), + "device_name__change_template" => Some((self.device_name__change_template, 237)), + "device_name__title" => Some((self.device_name__title, 238)), + "entropy__send" => Some((self.entropy__send, 239)), + "entropy__title" => Some((self.entropy__title, 240)), + "entropy__title_confirm" => Some((self.entropy__title_confirm, 241)), + "eos__about_to_sign_template" => Some((self.eos__about_to_sign_template, 242)), + "eos__account" => Some((self.eos__account, 243)), + "eos__action_name" => Some((self.eos__action_name, 244)), + "eos__amount" => Some((self.eos__amount, 245)), + "eos__arbitrary_data" => Some((self.eos__arbitrary_data, 246)), + "eos__buy_ram" => Some((self.eos__buy_ram, 247)), + "eos__bytes" => Some((self.eos__bytes, 248)), + "eos__cancel_vote" => Some((self.eos__cancel_vote, 249)), + "eos__checksum" => Some((self.eos__checksum, 250)), + "eos__code" => Some((self.eos__code, 251)), + "eos__contract" => Some((self.eos__contract, 252)), + "eos__cpu" => Some((self.eos__cpu, 253)), + "eos__creator" => Some((self.eos__creator, 254)), + "eos__delegate" => Some((self.eos__delegate, 255)), + "eos__delete_auth" => Some((self.eos__delete_auth, 256)), + "eos__from" => Some((self.eos__from, 257)), + "eos__link_auth" => Some((self.eos__link_auth, 258)), + "eos__memo" => Some((self.eos__memo, 259)), + "eos__name" => Some((self.eos__name, 260)), + "eos__net" => Some((self.eos__net, 261)), + "eos__new_account" => Some((self.eos__new_account, 262)), + "eos__no" => Some((self.eos__no, 263)), + "eos__owner" => Some((self.eos__owner, 264)), + "eos__parent" => Some((self.eos__parent, 265)), + "eos__payer" => Some((self.eos__payer, 266)), + "eos__permission" => Some((self.eos__permission, 267)), + "eos__proxy" => Some((self.eos__proxy, 268)), + "eos__receiver" => Some((self.eos__receiver, 269)), + "eos__refund" => Some((self.eos__refund, 270)), + "eos__requirement" => Some((self.eos__requirement, 271)), + "eos__sell_ram" => Some((self.eos__sell_ram, 272)), + "eos__sender" => Some((self.eos__sender, 273)), + "eos__sign_transaction" => Some((self.eos__sign_transaction, 274)), + "eos__threshold" => Some((self.eos__threshold, 275)), + "eos__to" => Some((self.eos__to, 276)), + "eos__transfer" => Some((self.eos__transfer, 277)), + "eos__type" => Some((self.eos__type, 278)), + "eos__undelegate" => Some((self.eos__undelegate, 279)), + "eos__unlink_auth" => Some((self.eos__unlink_auth, 280)), + "eos__update_auth" => Some((self.eos__update_auth, 281)), + "eos__vote_for_producers" => Some((self.eos__vote_for_producers, 282)), + "eos__vote_for_proxy" => Some((self.eos__vote_for_proxy, 283)), + "eos__voter" => Some((self.eos__voter, 284)), + "eos__yes" => Some((self.eos__yes, 285)), + "ethereum__amount_sent" => Some((self.ethereum__amount_sent, 286)), + "ethereum__confirm_fee" => Some((self.ethereum__confirm_fee, 287)), + "ethereum__contract" => Some((self.ethereum__contract, 288)), + "ethereum__data_size_template" => Some((self.ethereum__data_size_template, 289)), + "ethereum__gas_limit" => Some((self.ethereum__gas_limit, 290)), + "ethereum__gas_price" => Some((self.ethereum__gas_price, 291)), + "ethereum__max_gas_price" => Some((self.ethereum__max_gas_price, 292)), + "ethereum__name_and_version" => Some((self.ethereum__name_and_version, 293)), + "ethereum__new_contract" => Some((self.ethereum__new_contract, 294)), + "ethereum__no_message_field" => Some((self.ethereum__no_message_field, 295)), + "ethereum__priority_fee" => Some((self.ethereum__priority_fee, 296)), + "ethereum__show_full_array" => Some((self.ethereum__show_full_array, 297)), + "ethereum__show_full_domain" => Some((self.ethereum__show_full_domain, 298)), + "ethereum__show_full_message" => Some((self.ethereum__show_full_message, 299)), + "ethereum__show_full_struct" => Some((self.ethereum__show_full_struct, 300)), + "ethereum__sign_eip712" => Some((self.ethereum__sign_eip712, 301)), + "ethereum__title_confirm_data" => Some((self.ethereum__title_confirm_data, 302)), + "ethereum__title_confirm_domain" => Some((self.ethereum__title_confirm_domain, 303)), + "ethereum__title_confirm_message" => Some((self.ethereum__title_confirm_message, 304)), + "ethereum__title_confirm_struct" => Some((self.ethereum__title_confirm_struct, 305)), + "ethereum__title_confirm_typed_data" => Some((self.ethereum__title_confirm_typed_data, 306)), + "ethereum__title_signing_address" => Some((self.ethereum__title_signing_address, 307)), + "ethereum__units_template" => Some((self.ethereum__units_template, 308)), + "ethereum__unknown_token" => Some((self.ethereum__unknown_token, 309)), + "ethereum__valid_signature" => Some((self.ethereum__valid_signature, 310)), + "experimental_mode__enable" => Some((self.experimental_mode__enable, 311)), + "experimental_mode__only_for_dev" => Some((self.experimental_mode__only_for_dev, 312)), + "experimental_mode__title" => Some((self.experimental_mode__title, 313)), + "fido__already_registered" => Some((self.fido__already_registered, 314)), + "fido__device_already_registered" => Some((self.fido__device_already_registered, 315)), + "fido__device_already_registered_with_template" => Some((self.fido__device_already_registered_with_template, 316)), + "fido__device_not_registered" => Some((self.fido__device_not_registered, 317)), + "fido__does_not_belong" => Some((self.fido__does_not_belong, 318)), + "fido__erase_credentials" => Some((self.fido__erase_credentials, 319)), + "fido__export_credentials" => Some((self.fido__export_credentials, 320)), + "fido__not_registered" => Some((self.fido__not_registered, 321)), + "fido__not_registered_with_template" => Some((self.fido__not_registered_with_template, 322)), + "fido__please_enable_pin_protection" => Some((self.fido__please_enable_pin_protection, 323)), + "fido__title_authenticate" => Some((self.fido__title_authenticate, 324)), + "fido__title_import_credential" => Some((self.fido__title_import_credential, 325)), + "fido__title_list_credentials" => Some((self.fido__title_list_credentials, 326)), + "fido__title_register" => Some((self.fido__title_register, 327)), + "fido__title_remove_credential" => Some((self.fido__title_remove_credential, 328)), + "fido__title_reset" => Some((self.fido__title_reset, 329)), + "fido__title_u2f_auth" => Some((self.fido__title_u2f_auth, 330)), + "fido__title_u2f_register" => Some((self.fido__title_u2f_register, 331)), + "fido__title_verify_user" => Some((self.fido__title_verify_user, 332)), + "fido__unable_to_verify_user" => Some((self.fido__unable_to_verify_user, 333)), + "fido__wanna_erase_credentials" => Some((self.fido__wanna_erase_credentials, 334)), + "firmware_update__title" => Some((self.firmware_update__title, 335)), + "firmware_update__title_fingerprint" => Some((self.firmware_update__title_fingerprint, 336)), + "homescreen__click_to_connect" => Some((self.homescreen__click_to_connect, 337)), + "homescreen__click_to_unlock" => Some((self.homescreen__click_to_unlock, 338)), + "homescreen__title_backup_failed" => Some((self.homescreen__title_backup_failed, 339)), + "homescreen__title_backup_needed" => Some((self.homescreen__title_backup_needed, 340)), + "homescreen__title_coinjoin_authorized" => Some((self.homescreen__title_coinjoin_authorized, 341)), + "homescreen__title_experimental_mode" => Some((self.homescreen__title_experimental_mode, 342)), + "homescreen__title_hold_to_lock" => Some((self.homescreen__title_hold_to_lock, 343)), + "homescreen__title_no_usb_connection" => Some((self.homescreen__title_no_usb_connection, 344)), + "homescreen__title_pin_not_set" => Some((self.homescreen__title_pin_not_set, 345)), + "homescreen__title_seedless" => Some((self.homescreen__title_seedless, 346)), + "homescreen__title_set" => Some((self.homescreen__title_set, 347)), + "inputs__back" => Some((self.inputs__back, 348)), + "inputs__cancel" => Some((self.inputs__cancel, 349)), + "inputs__delete" => Some((self.inputs__delete, 350)), + "inputs__enter" => Some((self.inputs__enter, 351)), + "inputs__return" => Some((self.inputs__return, 352)), + "inputs__show" => Some((self.inputs__show, 353)), + "inputs__space" => Some((self.inputs__space, 354)), + "joint__title" => Some((self.joint__title, 355)), + "joint__to_the_total_amount" => Some((self.joint__to_the_total_amount, 356)), + "joint__you_are_contributing" => Some((self.joint__you_are_contributing, 357)), + "lockscreen__tap_to_connect" => Some((self.lockscreen__tap_to_connect, 361)), + "lockscreen__tap_to_unlock" => Some((self.lockscreen__tap_to_unlock, 362)), + "lockscreen__title_locked" => Some((self.lockscreen__title_locked, 363)), + "lockscreen__title_not_connected" => Some((self.lockscreen__title_not_connected, 364)), + "misc__decrypt_value" => Some((self.misc__decrypt_value, 365)), + "misc__encrypt_value" => Some((self.misc__encrypt_value, 366)), + "misc__title_suite_labeling" => Some((self.misc__title_suite_labeling, 367)), + "modify_amount__address" => Some((self.modify_amount__address, 368)), + "modify_amount__decrease_amount" => Some((self.modify_amount__decrease_amount, 369)), + "modify_amount__increase_amount" => Some((self.modify_amount__increase_amount, 370)), + "modify_amount__new_amount" => Some((self.modify_amount__new_amount, 371)), + "modify_amount__title" => Some((self.modify_amount__title, 372)), + "modify_fee__decrease_fee" => Some((self.modify_fee__decrease_fee, 373)), + "modify_fee__fee_rate" => Some((self.modify_fee__fee_rate, 374)), + "modify_fee__increase_fee" => Some((self.modify_fee__increase_fee, 375)), + "modify_fee__new_transaction_fee" => Some((self.modify_fee__new_transaction_fee, 376)), + "modify_fee__no_change" => Some((self.modify_fee__no_change, 377)), + "modify_fee__title" => Some((self.modify_fee__title, 378)), + "modify_fee__transaction_fee" => Some((self.modify_fee__transaction_fee, 379)), + "monero__confirm_export" => Some((self.monero__confirm_export, 380)), + "monero__confirm_fee" => Some((self.monero__confirm_fee, 381)), + "monero__confirm_ki_sync" => Some((self.monero__confirm_ki_sync, 382)), + "monero__confirm_refresh" => Some((self.monero__confirm_refresh, 383)), + "monero__confirm_unlock_time" => Some((self.monero__confirm_unlock_time, 384)), + "monero__hashing_inputs" => Some((self.monero__hashing_inputs, 385)), + "monero__payment_id" => Some((self.monero__payment_id, 386)), + "monero__postprocessing" => Some((self.monero__postprocessing, 387)), + "monero__processing" => Some((self.monero__processing, 388)), + "monero__processing_inputs" => Some((self.monero__processing_inputs, 389)), + "monero__processing_outputs" => Some((self.monero__processing_outputs, 390)), + "monero__signing" => Some((self.monero__signing, 391)), + "monero__signing_inputs" => Some((self.monero__signing_inputs, 392)), + "monero__unlock_time_set_template" => Some((self.monero__unlock_time_set_template, 393)), + "monero__wanna_export_tx_der" => Some((self.monero__wanna_export_tx_der, 394)), + "monero__wanna_export_tx_key" => Some((self.monero__wanna_export_tx_key, 395)), + "monero__wanna_export_watchkey" => Some((self.monero__wanna_export_watchkey, 396)), + "monero__wanna_start_refresh" => Some((self.monero__wanna_start_refresh, 397)), + "monero__wanna_sync_key_images" => Some((self.monero__wanna_sync_key_images, 398)), + "nem__absolute" => Some((self.nem__absolute, 399)), + "nem__activate" => Some((self.nem__activate, 400)), + "nem__add" => Some((self.nem__add, 401)), + "nem__confirm_action" => Some((self.nem__confirm_action, 402)), + "nem__confirm_address" => Some((self.nem__confirm_address, 403)), + "nem__confirm_creation_fee" => Some((self.nem__confirm_creation_fee, 404)), + "nem__confirm_fee" => Some((self.nem__confirm_fee, 405)), + "nem__confirm_mosaic" => Some((self.nem__confirm_mosaic, 406)), + "nem__confirm_multisig_fee" => Some((self.nem__confirm_multisig_fee, 407)), + "nem__confirm_namespace" => Some((self.nem__confirm_namespace, 408)), + "nem__confirm_payload" => Some((self.nem__confirm_payload, 409)), + "nem__confirm_properties" => Some((self.nem__confirm_properties, 410)), + "nem__confirm_rental_fee" => Some((self.nem__confirm_rental_fee, 411)), + "nem__confirm_transfer_of" => Some((self.nem__confirm_transfer_of, 412)), + "nem__convert_account_to_multisig" => Some((self.nem__convert_account_to_multisig, 413)), + "nem__cosign_transaction_for" => Some((self.nem__cosign_transaction_for, 414)), + "nem__cosignatory" => Some((self.nem__cosignatory, 415)), + "nem__create_mosaic" => Some((self.nem__create_mosaic, 416)), + "nem__create_namespace" => Some((self.nem__create_namespace, 417)), + "nem__deactivate" => Some((self.nem__deactivate, 418)), + "nem__decrease" => Some((self.nem__decrease, 419)), + "nem__description" => Some((self.nem__description, 420)), + "nem__divisibility_and_levy_cannot_be_shown" => Some((self.nem__divisibility_and_levy_cannot_be_shown, 421)), + "nem__encrypted" => Some((self.nem__encrypted, 422)), + "nem__final_confirm" => Some((self.nem__final_confirm, 423)), + "nem__immutable" => Some((self.nem__immutable, 424)), + "nem__increase" => Some((self.nem__increase, 425)), + "nem__initial_supply" => Some((self.nem__initial_supply, 426)), + "nem__initiate_transaction_for" => Some((self.nem__initiate_transaction_for, 427)), + "nem__levy_divisibility" => Some((self.nem__levy_divisibility, 428)), + "nem__levy_fee" => Some((self.nem__levy_fee, 429)), + "nem__levy_fee_of" => Some((self.nem__levy_fee_of, 430)), + "nem__levy_mosaic" => Some((self.nem__levy_mosaic, 431)), + "nem__levy_namespace" => Some((self.nem__levy_namespace, 432)), + "nem__levy_recipient" => Some((self.nem__levy_recipient, 433)), + "nem__levy_type" => Some((self.nem__levy_type, 434)), + "nem__modify_supply_for" => Some((self.nem__modify_supply_for, 435)), + "nem__modify_the_number_of_cosignatories_by" => Some((self.nem__modify_the_number_of_cosignatories_by, 436)), + "nem__mutable" => Some((self.nem__mutable, 437)), + "nem__no" => Some((self.nem__no, 438)), + "nem__of" => Some((self.nem__of, 439)), + "nem__percentile" => Some((self.nem__percentile, 440)), + "nem__raw_units_template" => Some((self.nem__raw_units_template, 441)), + "nem__remote_harvesting" => Some((self.nem__remote_harvesting, 442)), + "nem__remove" => Some((self.nem__remove, 443)), + "nem__set_minimum_cosignatories_to" => Some((self.nem__set_minimum_cosignatories_to, 444)), + "nem__sign_tx_fee_template" => Some((self.nem__sign_tx_fee_template, 445)), + "nem__supply_change" => Some((self.nem__supply_change, 446)), + "nem__supply_units_template" => Some((self.nem__supply_units_template, 447)), + "nem__transferable" => Some((self.nem__transferable, 448)), + "nem__under_namespace" => Some((self.nem__under_namespace, 449)), + "nem__unencrypted" => Some((self.nem__unencrypted, 450)), + "nem__unknown_mosaic" => Some((self.nem__unknown_mosaic, 451)), + "nem__yes" => Some((self.nem__yes, 452)), + "passphrase__access_hidden_wallet" => Some((self.passphrase__access_hidden_wallet, 453)), + "passphrase__always_on_device" => Some((self.passphrase__always_on_device, 454)), + "passphrase__from_host_not_shown" => Some((self.passphrase__from_host_not_shown, 455)), + "passphrase__hidden_wallet" => Some((self.passphrase__hidden_wallet, 456)), + "passphrase__hide" => Some((self.passphrase__hide, 457)), + "passphrase__next_screen_will_show_passphrase" => Some((self.passphrase__next_screen_will_show_passphrase, 458)), + "passphrase__please_enter" => Some((self.passphrase__please_enter, 459)), + "passphrase__revoke_on_device" => Some((self.passphrase__revoke_on_device, 460)), + "passphrase__title_confirm" => Some((self.passphrase__title_confirm, 461)), + "passphrase__title_enter" => Some((self.passphrase__title_enter, 462)), + "passphrase__title_hide" => Some((self.passphrase__title_hide, 463)), + "passphrase__title_settings" => Some((self.passphrase__title_settings, 464)), + "passphrase__title_source" => Some((self.passphrase__title_source, 465)), + "passphrase__turn_off" => Some((self.passphrase__turn_off, 466)), + "passphrase__turn_on" => Some((self.passphrase__turn_on, 467)), + "pin__change" => Some((self.pin__change, 468)), + "pin__changed" => Some((self.pin__changed, 469)), + "pin__cursor_will_change" => Some((self.pin__cursor_will_change, 470)), + "pin__diff_from_wipe_code" => Some((self.pin__diff_from_wipe_code, 471)), + "pin__disabled" => Some((self.pin__disabled, 472)), + "pin__enabled" => Some((self.pin__enabled, 473)), + "pin__enter" => Some((self.pin__enter, 474)), + "pin__enter_new" => Some((self.pin__enter_new, 475)), + "pin__entered_not_valid" => Some((self.pin__entered_not_valid, 476)), + "pin__info" => Some((self.pin__info, 477)), + "pin__invalid_pin" => Some((self.pin__invalid_pin, 478)), + "pin__last_attempt" => Some((self.pin__last_attempt, 479)), + "pin__mismatch" => Some((self.pin__mismatch, 480)), + "pin__pin_mismatch" => Some((self.pin__pin_mismatch, 481)), + "pin__please_check_again" => Some((self.pin__please_check_again, 482)), + "pin__reenter_new" => Some((self.pin__reenter_new, 483)), + "pin__reenter_to_confirm" => Some((self.pin__reenter_to_confirm, 484)), + "pin__should_be_long" => Some((self.pin__should_be_long, 485)), + "pin__title_check_pin" => Some((self.pin__title_check_pin, 486)), + "pin__title_settings" => Some((self.pin__title_settings, 487)), + "pin__title_wrong_pin" => Some((self.pin__title_wrong_pin, 488)), + "pin__tries_left" => Some((self.pin__tries_left, 489)), + "pin__turn_off" => Some((self.pin__turn_off, 490)), + "pin__turn_on" => Some((self.pin__turn_on, 491)), + "pin__wrong_pin" => Some((self.pin__wrong_pin, 492)), + "progress__authenticity_check" => Some((self.progress__authenticity_check, 493)), + "progress__done" => Some((self.progress__done, 494)), + "progress__loading_transaction" => Some((self.progress__loading_transaction, 495)), + "progress__one_second_left" => Some((self.progress__one_second_left, 496)), + "progress__please_wait" => Some((self.progress__please_wait, 497)), + "progress__processing" => Some((self.progress__processing, 498)), + "progress__refreshing" => Some((self.progress__refreshing, 499)), + "progress__signing_transaction" => Some((self.progress__signing_transaction, 500)), + "progress__syncing" => Some((self.progress__syncing, 501)), + "progress__x_seconds_left_template" => Some((self.progress__x_seconds_left_template, 502)), + "reboot_to_bootloader__restart" => Some((self.reboot_to_bootloader__restart, 503)), + "reboot_to_bootloader__title" => Some((self.reboot_to_bootloader__title, 504)), + "reboot_to_bootloader__version_by_template" => Some((self.reboot_to_bootloader__version_by_template, 505)), + "recovery__cancel_dry_run" => Some((self.recovery__cancel_dry_run, 506)), + "recovery__check_dry_run" => Some((self.recovery__check_dry_run, 507)), + "recovery__cursor_will_change" => Some((self.recovery__cursor_will_change, 508)), + "recovery__dry_run_bip39_valid_match" => Some((self.recovery__dry_run_bip39_valid_match, 509)), + "recovery__dry_run_bip39_valid_mismatch" => Some((self.recovery__dry_run_bip39_valid_mismatch, 510)), + "recovery__dry_run_slip39_valid_match" => Some((self.recovery__dry_run_slip39_valid_match, 511)), + "recovery__dry_run_slip39_valid_mismatch" => Some((self.recovery__dry_run_slip39_valid_mismatch, 512)), + "recovery__enter_any_share" => Some((self.recovery__enter_any_share, 513)), + "recovery__enter_backup" => Some((self.recovery__enter_backup, 514)), + "recovery__enter_different_share" => Some((self.recovery__enter_different_share, 515)), + "recovery__enter_share_from_diff_group" => Some((self.recovery__enter_share_from_diff_group, 516)), + "recovery__group_num_template" => Some((self.recovery__group_num_template, 517)), + "recovery__group_threshold_reached" => Some((self.recovery__group_threshold_reached, 518)), + "recovery__invalid_seed_entered" => Some((self.recovery__invalid_seed_entered, 519)), + "recovery__invalid_share_entered" => Some((self.recovery__invalid_share_entered, 520)), + "recovery__more_shares_needed" => Some((self.recovery__more_shares_needed, 521)), + "recovery__num_of_words" => Some((self.recovery__num_of_words, 522)), + "recovery__only_first_n_letters" => Some((self.recovery__only_first_n_letters, 523)), + "recovery__progress_will_be_lost" => Some((self.recovery__progress_will_be_lost, 524)), + "recovery__select_num_of_words" => Some((self.recovery__select_num_of_words, 525)), + "recovery__share_already_entered" => Some((self.recovery__share_already_entered, 526)), + "recovery__share_from_another_shamir" => Some((self.recovery__share_from_another_shamir, 527)), + "recovery__share_num_template" => Some((self.recovery__share_num_template, 528)), + "recovery__title" => Some((self.recovery__title, 529)), + "recovery__title_cancel_dry_run" => Some((self.recovery__title_cancel_dry_run, 530)), + "recovery__title_cancel_recovery" => Some((self.recovery__title_cancel_recovery, 531)), + "recovery__title_dry_run" => Some((self.recovery__title_dry_run, 532)), + "recovery__title_recover" => Some((self.recovery__title_recover, 533)), + "recovery__title_remaining_shares" => Some((self.recovery__title_remaining_shares, 534)), + "recovery__type_word_x_of_y_template" => Some((self.recovery__type_word_x_of_y_template, 535)), + "recovery__wallet_recovered" => Some((self.recovery__wallet_recovered, 536)), + "recovery__wanna_cancel_dry_run" => Some((self.recovery__wanna_cancel_dry_run, 537)), + "recovery__wanna_cancel_recovery" => Some((self.recovery__wanna_cancel_recovery, 538)), + "recovery__word_count_template" => Some((self.recovery__word_count_template, 539)), + "recovery__word_x_of_y_template" => Some((self.recovery__word_x_of_y_template, 540)), + "recovery__x_of_y_entered_template" => Some((self.recovery__x_of_y_entered_template, 541)), + "recovery__you_have_entered" => Some((self.recovery__you_have_entered, 542)), + "reset__advanced_group_threshold_info" => Some((self.reset__advanced_group_threshold_info, 543)), + "reset__all_x_of_y_template" => Some((self.reset__all_x_of_y_template, 544)), + "reset__any_x_of_y_template" => Some((self.reset__any_x_of_y_template, 545)), + "reset__button_create" => Some((self.reset__button_create, 546)), + "reset__button_recover" => Some((self.reset__button_recover, 547)), + "reset__by_continuing" => Some((self.reset__by_continuing, 548)), + "reset__check_backup_title" => Some((self.reset__check_backup_title, 549)), + "reset__check_group_share_title_template" => Some((self.reset__check_group_share_title_template, 550)), + "reset__check_seed_title" => Some((self.reset__check_seed_title, 551)), + "reset__check_share_title_template" => Some((self.reset__check_share_title_template, 552)), + "reset__continue_with_next_share" => Some((self.reset__continue_with_next_share, 553)), + "reset__continue_with_share_template" => Some((self.reset__continue_with_share_template, 554)), + "reset__finished_verifying_group_template" => Some((self.reset__finished_verifying_group_template, 555)), + "reset__finished_verifying_seed" => Some((self.reset__finished_verifying_seed, 556)), + "reset__finished_verifying_shares" => Some((self.reset__finished_verifying_shares, 557)), + "reset__group_description" => Some((self.reset__group_description, 558)), + "reset__group_info" => Some((self.reset__group_info, 559)), + "reset__group_share_checked_successfully_template" => Some((self.reset__group_share_checked_successfully_template, 560)), + "reset__group_share_title_template" => Some((self.reset__group_share_title_template, 561)), + "reset__more_info_at" => Some((self.reset__more_info_at, 562)), + "reset__need_all_share_template" => Some((self.reset__need_all_share_template, 563)), + "reset__need_any_share_template" => Some((self.reset__need_any_share_template, 564)), + "reset__needed_to_form_a_group" => Some((self.reset__needed_to_form_a_group, 565)), + "reset__needed_to_recover_your_wallet" => Some((self.reset__needed_to_recover_your_wallet, 566)), + "reset__never_make_digital_copy" => Some((self.reset__never_make_digital_copy, 567)), + "reset__num_of_share_holders_template" => Some((self.reset__num_of_share_holders_template, 568)), + "reset__num_of_shares_advanced_info_template" => Some((self.reset__num_of_shares_advanced_info_template, 569)), + "reset__num_of_shares_basic_info" => Some((self.reset__num_of_shares_basic_info, 570)), + "reset__num_shares_for_group_template" => Some((self.reset__num_shares_for_group_template, 571)), + "reset__number_of_shares_info" => Some((self.reset__number_of_shares_info, 572)), + "reset__one_share" => Some((self.reset__one_share, 573)), + "reset__only_one_share_will_be_created" => Some((self.reset__only_one_share_will_be_created, 574)), + "reset__recovery_seed_title" => Some((self.reset__recovery_seed_title, 575)), + "reset__recovery_share_title_template" => Some((self.reset__recovery_share_title_template, 576)), + "reset__required_number_of_groups" => Some((self.reset__required_number_of_groups, 577)), + "reset__select_correct_word" => Some((self.reset__select_correct_word, 578)), + "reset__select_word_template" => Some((self.reset__select_word_template, 579)), + "reset__select_word_x_of_y_template" => Some((self.reset__select_word_x_of_y_template, 580)), + "reset__set_it_to_count_template" => Some((self.reset__set_it_to_count_template, 581)), + "reset__share_checked_successfully_template" => Some((self.reset__share_checked_successfully_template, 582)), + "reset__share_words_title" => Some((self.reset__share_words_title, 583)), + "reset__slip39_checklist_num_groups" => Some((self.reset__slip39_checklist_num_groups, 584)), + "reset__slip39_checklist_num_shares" => Some((self.reset__slip39_checklist_num_shares, 585)), + "reset__slip39_checklist_set_num_groups" => Some((self.reset__slip39_checklist_set_num_groups, 586)), + "reset__slip39_checklist_set_num_shares" => Some((self.reset__slip39_checklist_set_num_shares, 587)), + "reset__slip39_checklist_set_sizes" => Some((self.reset__slip39_checklist_set_sizes, 588)), + "reset__slip39_checklist_set_sizes_longer" => Some((self.reset__slip39_checklist_set_sizes_longer, 589)), + "reset__slip39_checklist_set_threshold" => Some((self.reset__slip39_checklist_set_threshold, 590)), + "reset__slip39_checklist_title" => Some((self.reset__slip39_checklist_title, 591)), + "reset__slip39_checklist_write_down" => Some((self.reset__slip39_checklist_write_down, 592)), + "reset__slip39_checklist_write_down_recovery" => Some((self.reset__slip39_checklist_write_down_recovery, 593)), + "reset__the_threshold_sets_the_number_of_shares" => Some((self.reset__the_threshold_sets_the_number_of_shares, 594)), + "reset__threshold_info" => Some((self.reset__threshold_info, 595)), + "reset__title_backup_is_done" => Some((self.reset__title_backup_is_done, 596)), + "reset__title_create_wallet" => Some((self.reset__title_create_wallet, 597)), + "reset__title_create_wallet_shamir" => Some((self.reset__title_create_wallet_shamir, 598)), + "reset__title_group_threshold" => Some((self.reset__title_group_threshold, 599)), + "reset__title_number_of_groups" => Some((self.reset__title_number_of_groups, 600)), + "reset__title_number_of_shares" => Some((self.reset__title_number_of_shares, 601)), + "reset__title_set_group_threshold" => Some((self.reset__title_set_group_threshold, 602)), + "reset__title_set_number_of_groups" => Some((self.reset__title_set_number_of_groups, 603)), + "reset__title_set_number_of_shares" => Some((self.reset__title_set_number_of_shares, 604)), + "reset__title_set_threshold" => Some((self.reset__title_set_threshold, 605)), + "reset__to_form_group_template" => Some((self.reset__to_form_group_template, 606)), + "reset__tos_link" => Some((self.reset__tos_link, 607)), + "reset__total_number_of_shares_in_group_template" => Some((self.reset__total_number_of_shares_in_group_template, 608)), + "reset__use_your_backup" => Some((self.reset__use_your_backup, 609)), + "reset__write_down_words_template" => Some((self.reset__write_down_words_template, 610)), + "reset__wrong_word_selected" => Some((self.reset__wrong_word_selected, 611)), + "reset__you_need_one_share" => Some((self.reset__you_need_one_share, 612)), + "reset__your_backup_is_done" => Some((self.reset__your_backup_is_done, 613)), + "ripple__confirm_tag" => Some((self.ripple__confirm_tag, 614)), + "ripple__destination_tag_template" => Some((self.ripple__destination_tag_template, 615)), + "rotation__change_template" => Some((self.rotation__change_template, 616)), + "rotation__east" => Some((self.rotation__east, 617)), + "rotation__north" => Some((self.rotation__north, 618)), + "rotation__south" => Some((self.rotation__south, 619)), + "rotation__title_change" => Some((self.rotation__title_change, 620)), + "rotation__west" => Some((self.rotation__west, 621)), + "safety_checks__approve_unsafe_always" => Some((self.safety_checks__approve_unsafe_always, 622)), + "safety_checks__approve_unsafe_temporary" => Some((self.safety_checks__approve_unsafe_temporary, 623)), + "safety_checks__enforce_strict" => Some((self.safety_checks__enforce_strict, 624)), + "safety_checks__title" => Some((self.safety_checks__title, 625)), + "safety_checks__title_safety_override" => Some((self.safety_checks__title_safety_override, 626)), + "sd_card__all_data_will_be_lost" => Some((self.sd_card__all_data_will_be_lost, 627)), + "sd_card__card_required" => Some((self.sd_card__card_required, 628)), + "sd_card__disable" => Some((self.sd_card__disable, 629)), + "sd_card__disabled" => Some((self.sd_card__disabled, 630)), + "sd_card__enable" => Some((self.sd_card__enable, 631)), + "sd_card__enabled" => Some((self.sd_card__enabled, 632)), + "sd_card__error" => Some((self.sd_card__error, 633)), + "sd_card__format_card" => Some((self.sd_card__format_card, 634)), + "sd_card__insert_correct_card" => Some((self.sd_card__insert_correct_card, 635)), + "sd_card__please_insert" => Some((self.sd_card__please_insert, 636)), + "sd_card__please_unplug_and_insert" => Some((self.sd_card__please_unplug_and_insert, 637)), + "sd_card__problem_accessing" => Some((self.sd_card__problem_accessing, 638)), + "sd_card__refresh" => Some((self.sd_card__refresh, 639)), + "sd_card__refreshed" => Some((self.sd_card__refreshed, 640)), + "sd_card__restart" => Some((self.sd_card__restart, 641)), + "sd_card__title" => Some((self.sd_card__title, 642)), + "sd_card__title_problem" => Some((self.sd_card__title_problem, 643)), + "sd_card__unknown_filesystem" => Some((self.sd_card__unknown_filesystem, 644)), + "sd_card__unplug_and_insert_correct" => Some((self.sd_card__unplug_and_insert_correct, 645)), + "sd_card__use_different_card" => Some((self.sd_card__use_different_card, 646)), + "sd_card__wanna_format" => Some((self.sd_card__wanna_format, 647)), + "sd_card__wrong_sd_card" => Some((self.sd_card__wrong_sd_card, 648)), + "send__address_path" => Some((self.send__address_path, 649)), + "send__amount" => Some((self.send__amount, 650)), + "send__confirm_sending" => Some((self.send__confirm_sending, 651)), + "send__from_multiple_accounts" => Some((self.send__from_multiple_accounts, 652)), + "send__including_fee" => Some((self.send__including_fee, 653)), + "send__maximum_fee" => Some((self.send__maximum_fee, 654)), + "send__receiving_to_multisig" => Some((self.send__receiving_to_multisig, 655)), + "send__title_amount" => Some((self.send__title_amount, 656)), + "send__title_confirm_sending" => Some((self.send__title_confirm_sending, 657)), + "send__title_joint_transaction" => Some((self.send__title_joint_transaction, 658)), + "send__title_receiving_to" => Some((self.send__title_receiving_to, 659)), + "send__title_recipient" => Some((self.send__title_recipient, 660)), + "send__title_sending" => Some((self.send__title_sending, 661)), + "send__title_sending_amount" => Some((self.send__title_sending_amount, 662)), + "send__title_sending_to" => Some((self.send__title_sending_to, 663)), + "send__to_the_total_amount" => Some((self.send__to_the_total_amount, 664)), + "send__total_amount" => Some((self.send__total_amount, 665)), + "send__transaction_id" => Some((self.send__transaction_id, 666)), + "send__you_are_contributing" => Some((self.send__you_are_contributing, 667)), + "share_words__words_in_order" => Some((self.share_words__words_in_order, 668)), + "share_words__wrote_down_all" => Some((self.share_words__wrote_down_all, 669)), + "sign_message__bytes_template" => Some((self.sign_message__bytes_template, 670)), + "sign_message__confirm_address" => Some((self.sign_message__confirm_address, 671)), + "sign_message__confirm_message" => Some((self.sign_message__confirm_message, 672)), + "sign_message__message_size" => Some((self.sign_message__message_size, 673)), + "sign_message__verify_address" => Some((self.sign_message__verify_address, 674)), + "stellar__account" => Some((self.stellar__account, 675)), + "stellar__account_merge" => Some((self.stellar__account_merge, 676)), + "stellar__account_thresholds" => Some((self.stellar__account_thresholds, 677)), + "stellar__add_signer" => Some((self.stellar__add_signer, 678)), + "stellar__add_trust" => Some((self.stellar__add_trust, 679)), + "stellar__all_will_be_sent_to" => Some((self.stellar__all_will_be_sent_to, 680)), + "stellar__allow_trust" => Some((self.stellar__allow_trust, 681)), + "stellar__asset" => Some((self.stellar__asset, 682)), + "stellar__bump_sequence" => Some((self.stellar__bump_sequence, 683)), + "stellar__buying" => Some((self.stellar__buying, 684)), + "stellar__clear_data" => Some((self.stellar__clear_data, 685)), + "stellar__clear_flags" => Some((self.stellar__clear_flags, 686)), + "stellar__confirm_issuer" => Some((self.stellar__confirm_issuer, 687)), + "stellar__confirm_memo" => Some((self.stellar__confirm_memo, 688)), + "stellar__confirm_network" => Some((self.stellar__confirm_network, 689)), + "stellar__confirm_operation" => Some((self.stellar__confirm_operation, 690)), + "stellar__confirm_stellar" => Some((self.stellar__confirm_stellar, 691)), + "stellar__confirm_timebounds" => Some((self.stellar__confirm_timebounds, 692)), + "stellar__create_account" => Some((self.stellar__create_account, 693)), + "stellar__debited_amount" => Some((self.stellar__debited_amount, 694)), + "stellar__delete" => Some((self.stellar__delete, 695)), + "stellar__delete_passive_offer" => Some((self.stellar__delete_passive_offer, 696)), + "stellar__delete_trust" => Some((self.stellar__delete_trust, 697)), + "stellar__destination" => Some((self.stellar__destination, 698)), + "stellar__exchanges_require_memo" => Some((self.stellar__exchanges_require_memo, 699)), + "stellar__final_confirm" => Some((self.stellar__final_confirm, 700)), + "stellar__hash" => Some((self.stellar__hash, 701)), + "stellar__high" => Some((self.stellar__high, 702)), + "stellar__home_domain" => Some((self.stellar__home_domain, 703)), + "stellar__inflation" => Some((self.stellar__inflation, 704)), + "stellar__initial_balance" => Some((self.stellar__initial_balance, 705)), + "stellar__initialize_signing_with" => Some((self.stellar__initialize_signing_with, 706)), + "stellar__issuer_template" => Some((self.stellar__issuer_template, 707)), + "stellar__key" => Some((self.stellar__key, 708)), + "stellar__limit" => Some((self.stellar__limit, 709)), + "stellar__low" => Some((self.stellar__low, 710)), + "stellar__master_weight" => Some((self.stellar__master_weight, 711)), + "stellar__medium" => Some((self.stellar__medium, 712)), + "stellar__new_offer" => Some((self.stellar__new_offer, 713)), + "stellar__new_passive_offer" => Some((self.stellar__new_passive_offer, 714)), + "stellar__no_memo_set" => Some((self.stellar__no_memo_set, 715)), + "stellar__no_restriction" => Some((self.stellar__no_restriction, 716)), + "stellar__on_network_template" => Some((self.stellar__on_network_template, 717)), + "stellar__path_pay" => Some((self.stellar__path_pay, 718)), + "stellar__path_pay_at_least" => Some((self.stellar__path_pay_at_least, 719)), + "stellar__pay" => Some((self.stellar__pay, 720)), + "stellar__pay_at_most" => Some((self.stellar__pay_at_most, 721)), + "stellar__preauth_transaction" => Some((self.stellar__preauth_transaction, 722)), + "stellar__price_per_template" => Some((self.stellar__price_per_template, 723)), + "stellar__private_network" => Some((self.stellar__private_network, 724)), + "stellar__remove_signer" => Some((self.stellar__remove_signer, 725)), + "stellar__revoke_trust" => Some((self.stellar__revoke_trust, 726)), + "stellar__selling" => Some((self.stellar__selling, 727)), + "stellar__set_data" => Some((self.stellar__set_data, 728)), + "stellar__set_flags" => Some((self.stellar__set_flags, 729)), + "stellar__set_sequence_to_template" => Some((self.stellar__set_sequence_to_template, 730)), + "stellar__sign_tx_count_template" => Some((self.stellar__sign_tx_count_template, 731)), + "stellar__sign_tx_fee_template" => Some((self.stellar__sign_tx_fee_template, 732)), + "stellar__source_account" => Some((self.stellar__source_account, 733)), + "stellar__testnet_network" => Some((self.stellar__testnet_network, 734)), + "stellar__trusted_account" => Some((self.stellar__trusted_account, 735)), + "stellar__update" => Some((self.stellar__update, 736)), + "stellar__valid_from" => Some((self.stellar__valid_from, 737)), + "stellar__valid_to" => Some((self.stellar__valid_to, 738)), + "stellar__value_sha256" => Some((self.stellar__value_sha256, 739)), + "stellar__wanna_clean_value_key_template" => Some((self.stellar__wanna_clean_value_key_template, 740)), + "stellar__your_account" => Some((self.stellar__your_account, 741)), + "tezos__address" => Some((self.tezos__address, 742)), + "tezos__amount" => Some((self.tezos__amount, 743)), + "tezos__baker_address" => Some((self.tezos__baker_address, 744)), + "tezos__balance" => Some((self.tezos__balance, 745)), + "tezos__ballot" => Some((self.tezos__ballot, 746)), + "tezos__confirm_delegation" => Some((self.tezos__confirm_delegation, 747)), + "tezos__confirm_origination" => Some((self.tezos__confirm_origination, 748)), + "tezos__delegator" => Some((self.tezos__delegator, 749)), + "tezos__fee" => Some((self.tezos__fee, 750)), + "tezos__proposal" => Some((self.tezos__proposal, 751)), + "tezos__register_delegate" => Some((self.tezos__register_delegate, 752)), + "tezos__remove_delegation" => Some((self.tezos__remove_delegation, 753)), + "tezos__submit_ballot" => Some((self.tezos__submit_ballot, 754)), + "tezos__submit_proposal" => Some((self.tezos__submit_proposal, 755)), + "tezos__submit_proposals" => Some((self.tezos__submit_proposals, 756)), + "tutorial__middle_click" => Some((self.tutorial__middle_click, 757)), + "tutorial__press_and_hold" => Some((self.tutorial__press_and_hold, 758)), + "tutorial__ready_to_use" => Some((self.tutorial__ready_to_use, 759)), + "tutorial__scroll_down" => Some((self.tutorial__scroll_down, 760)), + "tutorial__sure_you_want_skip" => Some((self.tutorial__sure_you_want_skip, 761)), + "tutorial__title_hello" => Some((self.tutorial__title_hello, 762)), + "tutorial__title_screen_scroll" => Some((self.tutorial__title_screen_scroll, 763)), + "tutorial__title_skip" => Some((self.tutorial__title_skip, 764)), + "tutorial__title_tutorial_complete" => Some((self.tutorial__title_tutorial_complete, 765)), + "tutorial__use_trezor" => Some((self.tutorial__use_trezor, 766)), + "tutorial__welcome_press_right" => Some((self.tutorial__welcome_press_right, 767)), + "u2f__get" => Some((self.u2f__get, 768)), + "u2f__set_template" => Some((self.u2f__set_template, 769)), + "u2f__title_get" => Some((self.u2f__title_get, 770)), + "u2f__title_set" => Some((self.u2f__title_set, 771)), + "wipe__info" => Some((self.wipe__info, 772)), + "wipe__title" => Some((self.wipe__title, 773)), + "wipe__want_to_wipe" => Some((self.wipe__want_to_wipe, 774)), + "wipe_code__change" => Some((self.wipe_code__change, 775)), + "wipe_code__changed" => Some((self.wipe_code__changed, 776)), + "wipe_code__diff_from_pin" => Some((self.wipe_code__diff_from_pin, 777)), + "wipe_code__disabled" => Some((self.wipe_code__disabled, 778)), + "wipe_code__enabled" => Some((self.wipe_code__enabled, 779)), + "wipe_code__enter_new" => Some((self.wipe_code__enter_new, 780)), + "wipe_code__info" => Some((self.wipe_code__info, 781)), + "wipe_code__invalid" => Some((self.wipe_code__invalid, 782)), + "wipe_code__mismatch" => Some((self.wipe_code__mismatch, 783)), + "wipe_code__reenter" => Some((self.wipe_code__reenter, 784)), + "wipe_code__reenter_to_confirm" => Some((self.wipe_code__reenter_to_confirm, 785)), + "wipe_code__title_check" => Some((self.wipe_code__title_check, 786)), + "wipe_code__title_invalid" => Some((self.wipe_code__title_invalid, 787)), + "wipe_code__title_settings" => Some((self.wipe_code__title_settings, 788)), + "wipe_code__turn_off" => Some((self.wipe_code__turn_off, 789)), + "wipe_code__turn_on" => Some((self.wipe_code__turn_on, 790)), + "wipe_code__wipe_code_mismatch" => Some((self.wipe_code__wipe_code_mismatch, 791)), + "word_count__title" => Some((self.word_count__title, 792)), + "words__are_you_sure" => Some((self.words__are_you_sure, 793)), + "words__buying" => Some((self.words__buying, 794)), + "words__continue_anyway" => Some((self.words__continue_anyway, 795)), + "words__continue_with" => Some((self.words__continue_with, 796)), + "words__error" => Some((self.words__error, 797)), + "words__from" => Some((self.words__from, 798)), + "words__keep_it_safe" => Some((self.words__keep_it_safe, 799)), + "words__know_what_your_doing" => Some((self.words__know_what_your_doing, 800)), + "words__my_trezor" => Some((self.words__my_trezor, 801)), + "words__outputs" => Some((self.words__outputs, 802)), + "words__please_check_again" => Some((self.words__please_check_again, 803)), + "words__please_try_again" => Some((self.words__please_try_again, 804)), + "words__really_wanna" => Some((self.words__really_wanna, 805)), + "words__sign" => Some((self.words__sign, 806)), + "words__title_check" => Some((self.words__title_check, 807)), + "words__title_group" => Some((self.words__title_group, 808)), + "words__title_information" => Some((self.words__title_information, 809)), + "words__title_remember" => Some((self.words__title_remember, 810)), + "words__title_share" => Some((self.words__title_share, 811)), + "words__title_shares" => Some((self.words__title_shares, 812)), + "words__title_success" => Some((self.words__title_success, 813)), + "words__title_summary" => Some((self.words__title_summary, 814)), + "words__title_threshold" => Some((self.words__title_threshold, 815)), + "words__unknown" => Some((self.words__unknown, 816)), + "words__warning" => Some((self.words__warning, 817)), + "plurals__sign_x_actions" => Some((self.plurals__sign_x_actions, 818)), + "plurals__contains_x_keys" => Some((self.plurals__contains_x_keys, 819)), + "words__contains" => Some((self.words__contains, 820)), + "plurals__x_shares_needed" => Some((self.plurals__x_shares_needed, 821)), + "words__array_of" => Some((self.words__array_of, 822)), + "recovery__x_more_shares_needed_template_plural" => Some((self.recovery__x_more_shares_needed_template_plural, 823)), + "plurals__transaction_of_x_operations" => Some((self.plurals__transaction_of_x_operations, 824)), + "plurals__x_groups_needed" => Some((self.plurals__x_groups_needed, 825)), + "recovery__x_more_items_starting_template_plural" => Some((self.recovery__x_more_items_starting_template_plural, 826)), + "plurals__lock_after_x_hours" => Some((self.plurals__lock_after_x_hours, 827)), + "plurals__lock_after_x_milliseconds" => Some((self.plurals__lock_after_x_milliseconds, 828)), + "plurals__lock_after_x_minutes" => Some((self.plurals__lock_after_x_minutes, 829)), + "plurals__lock_after_x_seconds" => Some((self.plurals__lock_after_x_seconds, 830)), + "words__confirm" => Some((self.words__confirm, 831)), + _ => None, + } + } +} diff --git a/core/embed/rust/src/ui/translations/general.rs.mako b/core/embed/rust/src/ui/translations/general.rs.mako new file mode 100644 index 000000000..ed4bec259 --- /dev/null +++ b/core/embed/rust/src/ui/translations/general.rs.mako @@ -0,0 +1,62 @@ +//! generated from cs.rs.mako +//! (by running `make templates` in `core`) +//! do not edit manually! + +<% +import json +from pathlib import Path + +THIS = Path(local.filename).resolve() +SRCDIR = THIS.parent + +order_file = SRCDIR / "order.json" +order_index_name = json.loads(order_file.read_text()) +order_name_index = {v: int(k) for k, v in order_index_name.items()} + +en_file = SRCDIR / "en.json" +en_data = json.loads(en_file.read_text())["translations"] + +def get_all_json_keys(data: dict) -> set[str]: + keys: set[str] = set() + for section_name, section in data.items(): + for k, v in section.items(): + keys.add(f"{section_name}__{k}") + return keys + +en_keys = get_all_json_keys(en_data) + +keys_index_mapping: dict[str, int] = {} +for key in en_keys: + if key not in order_name_index: + raise ValueError(f"key {key} not found in order.json") + keys_index_mapping[key] = order_name_index[key] + +index_sorted_keys = sorted(keys_index_mapping.items(), key=lambda x: x[1]) +%>\ +#[rustfmt::skip] +#[allow(non_snake_case)] +pub struct TranslationsGeneral { +% for name, _ in index_sorted_keys: + pub ${name}: &'static str, +% endfor +} + +#[rustfmt::skip] +impl TranslationsGeneral { + pub fn get_text(&self, key: &str) -> Option<<&'static str> { + self.get_info(key).map(|(text, _)| text) + } + + pub fn get_position(&self, key: &str) -> Option { + self.get_info(key).map(|(_, pos)| pos) + } + + fn get_info(&self, key: &str) -> Option<(&'static str, usize)> { + match key { +% for name, index in index_sorted_keys: + ${utf8_str(name)} => Some((self.${name}, ${index})), +% endfor + _ => None, + } + } +} diff --git a/core/embed/rust/src/ui/translations/micropython.rs b/core/embed/rust/src/ui/translations/micropython.rs new file mode 100644 index 000000000..c74ea803e --- /dev/null +++ b/core/embed/rust/src/ui/translations/micropython.rs @@ -0,0 +1,75 @@ +use crate::{ + error::Error, + micropython::{ + ffi, + obj::{Obj, ObjBase}, + qstr::Qstr, + typ::Type, + util, + }, +}; + +use super::{get_language_name, tr}; + +extern "C" fn translate_attr_fn(_self_in: Obj, attr: ffi::qstr, dest: *mut Obj) { + let block = || { + let arg = unsafe { dest.read() }; + if !arg.is_null() { + // Null destination would mean a `setattr`. + return Err(Error::TypeError); + } + let attr = Qstr::from_u16(attr as u16); + unsafe { dest.write(TR_OBJ.getattr(attr)?) }; + Ok(()) + }; + unsafe { util::try_or_raise(block) } +} + +#[repr(C)] +pub struct TrObj { + base: ObjBase, +} + +static TR_TYPE: Type = obj_type! { + name: Qstr::MP_QSTR_TR, + attr_fn: translate_attr_fn, +}; + +// SAFETY: We are in a single-threaded environment. +unsafe impl Sync for TrObj {} + +impl TrObj { + fn obj_type() -> &'static Type { + &TR_TYPE + } + + fn getattr(&self, attr: Qstr) -> Result { + tr(attr.as_str()).try_into() + } + + /// Convert TrObj to a MicroPython object + pub const fn as_obj(&'static self) -> Obj { + // SAFETY: + // - We are an object struct with a base and a type. + // - 'static lifetime holds us in place. + // - There's nothing to mutate. + unsafe { Obj::from_ptr(self as *const _ as *mut _) } + } +} + +/// Translations object callable from micropython. +pub static TR_OBJ: TrObj = TrObj { + base: TR_TYPE.as_base(), +}; + +/// Language name getter callable from micropython. +pub extern "C" fn language_name_obj() -> Obj { + let block = || { + if let Some(lang) = get_language_name() { + lang.try_into() + } else { + Ok(Obj::const_none()) + } + }; + unsafe { util::try_or_raise(block) } +} diff --git a/core/embed/rust/src/ui/translations/mod.rs b/core/embed/rust/src/ui/translations/mod.rs new file mode 100644 index 000000000..0ac357433 --- /dev/null +++ b/core/embed/rust/src/ui/translations/mod.rs @@ -0,0 +1,413 @@ +mod en; +#[cfg(feature = "micropython")] +mod export; +mod general; +#[cfg(feature = "micropython")] +mod micropython; + +use en::EN_TRANSLATIONS; + +use crate::trezorhal::translations::{get_pointer_with_offset, get_translations_blob, PointerData}; +use core::str; + +const TERMINATE_BYTE: u8 = 0xFF; +const ALIGNMENT_BYTE: u8 = 0x00; +const HEADER_LEN: usize = 256; + +/// Translation function for Rust. +pub fn tr(key: &str) -> &'static str { + translate(key).unwrap_or_default() +} + +// TODO: somehow make it a singleton object, so it is loaded just once +// TODO: split it into header.rs, translations.rs and font.rs? +// TODO: --- put the {en,cs,fr}.* files to `languages` dir? +// TODO: not generate fr/cs.rs files not to confuse, rather do .txt? +// TODO: add some tests? + +struct TranslationsHeader { + /// Human readable language identifier (e.g. "cs" of "fr") + pub language: &'static str, + /// Version in format {major}.{minor}.{patch} + pub version: &'static str, + /// Overall length of the data blob (excluding the header) + pub data_length: u16, + /// Length of the translation data + pub translations_length: u16, + /// Number of translation items + pub translations_num: u16, + /// Hash of the data blob (excluding the header) + pub data_hash: [u8; 32], + /// Title to show user before changing the language + pub change_language_title: &'static str, + /// Text to show user before changing the language + pub change_language_prompt: &'static str, +} + +impl TranslationsHeader { + const MAGIC: [u8; 4] = [84, 82, 84, 82]; // b"TRTR" + const VERSION_LEN: usize = 16; + const LANG_LEN: usize = 32; + const DATA_HASH_LEN: usize = 32; + const CHANGE_LANG_TITLE_LEN: usize = 20; + const CHANGE_LANG_PROMPT_LEN: usize = 40; + const SIGNATURE_LEN: usize = 65; + + pub fn from_flash() -> Result { + let header_data = &get_translations_blob()[..HEADER_LEN]; + Self::from_bytes(header_data) + } + + pub fn from_bytes(data: &'static [u8]) -> Result { + if data.len() != HEADER_LEN { + return Err("Invalid header length"); + } + + let (magic, rest) = data.split_at(4); + if magic != Self::MAGIC { + return Err("Invalid header magic"); + } + + let (version, rest) = rest.split_at(Self::VERSION_LEN); + let version = core::str::from_utf8(version) + .or(Err("Invalid version string"))? + .trim_end_matches(0 as char); + + let (language, rest) = rest.split_at(Self::LANG_LEN); + let language = core::str::from_utf8(language) + .or(Err("Invalid language string"))? + .trim_end_matches(0 as char); + + let (data_length_bytes, rest) = rest.split_at(2); + let data_length = u16::from_le_bytes( + data_length_bytes + .try_into() + .or(Err("Invalid data length bytes"))?, + ); + + let (translations_length_bytes, rest) = rest.split_at(2); + let translations_length = u16::from_le_bytes( + translations_length_bytes + .try_into() + .or(Err("Invalid translations length bytes"))?, + ); + + let (translations_num_bytes, rest) = rest.split_at(2); + let translations_num = u16::from_le_bytes( + translations_num_bytes + .try_into() + .or(Err("Invalid translations num bytes"))?, + ); + + let (data_hash, rest) = rest.split_at(Self::DATA_HASH_LEN); + let data_hash: [u8; 32] = data_hash.try_into().or(Err("Invalid data hash length"))?; + + let (change_language_title, rest) = rest.split_at(Self::CHANGE_LANG_TITLE_LEN); + let change_language_title = core::str::from_utf8(change_language_title) + .or(Err("Invalid change_language_title string"))? + .trim_end_matches(0 as char); + + let (change_language_prompt, rest) = rest.split_at(Self::CHANGE_LANG_PROMPT_LEN); + let change_language_prompt = core::str::from_utf8(change_language_prompt) + .or(Err("Invalid change_language_prompt string"))? + .trim_end_matches(0 as char); + + let non_sig_len = rest.len() as i32 - Self::SIGNATURE_LEN as i32; + if non_sig_len < 0 { + return Err("Not enough space for signature"); + } + let (non_sig_rest, _sig) = rest.split_at(non_sig_len as usize); + + // Rest must be empty bytes + for byte in non_sig_rest { + if *byte != 0 { + return Err("Invalid header data"); + } + } + + // Signature was already checked in micropython + + Ok(Self { + language, + version, + data_length, + translations_length, + translations_num, + data_hash, + change_language_title, + change_language_prompt, + }) + } + + /// Starting offset for the font data. + pub fn font_start_offset(&self) -> u16 { + HEADER_LEN as u16 + self.translations_length + } + + /// Offset for the last byte of the blob. + pub fn blob_end_offset(&self) -> u16 { + HEADER_LEN as u16 + self.data_length + } + + /// Get the part of the blob dedicated to the translations. + pub fn get_all_translations_data(&self) -> &'static [u8] { + let start = HEADER_LEN; + let end = start + self.translations_length as usize; + &get_translations_blob()[start..end] + } + + /// Get the offset table for the translations data. + pub fn get_translations_table(&self) -> OffsetTableData { + // Each entry has 2 bytes of offset + // Connection to a specific translation is carried by the index in the table + const ENTRY_SIZE: usize = 2; + let table_len = self.translations_num as usize * ENTRY_SIZE; + let start = HEADER_LEN; + let end = start + table_len; + let table_data = &get_translations_blob()[start..end]; + + OffsetTableData { + offset_table_data: table_data, + overall_len: self.translations_length, + } + } + + /// Get the part of the blob dedicated to the fonts. + pub fn get_all_font_data(&self) -> &'static [u8] { + let start = self.font_start_offset() as usize; + let end = self.blob_end_offset() as usize; + &get_translations_blob()[start..end] + } + + pub fn get_font_table(&self) -> OffsetTableData { + let all_font_data = self.get_all_font_data(); + let data_size = all_font_data.len() as u16; + + let (font_amount_bytes, data) = all_font_data.split_at(2); + let font_amount = u16::from_le_bytes(unwrap!(font_amount_bytes.try_into())); + + // Each entry has 2 bytes of font-ID and 2 bytes of offset + const FONT_ENTRY_SIZE: usize = 4; + let pairs_len = font_amount as usize * FONT_ENTRY_SIZE; + let pairs_data = &data[..pairs_len]; + + OffsetTableData { + offset_table_data: pairs_data, + overall_len: data_size, + } + } + + pub fn get_font_data_offset(&self, font_offset: u16, len: u16) -> &'static [u8] { + let start = font_offset as usize; + let end = start + len as usize; + &self.get_all_font_data()[start..end] + } +} + +pub struct OffsetTableData { + /// Offset table raw data + pub offset_table_data: &'static [u8], + /// Overall length of the data (used for getting the length of the last + /// element in the table) + pub overall_len: u16, +} + +/// Get the language name. +fn get_language_name() -> Option<&'static str> { + TranslationsHeader::from_flash() + .ok() + .map(|header| header.language) +} + +/// Try to find the translation in flash (for a non-english language). +/// If not found, fallback to english. +fn translate(key: &str) -> Option<&'static str> { + let mut translation: Option<&'static str> = None; + + if are_there_translations() { + if let Some(index) = EN_TRANSLATIONS.get_position(key) { + translation = get_translation_by_index(index); + } + } + + if translation.is_none() { + translation = EN_TRANSLATIONS.get_text(key); + } + + translation +} + +/// Quickly checks whether there are some valid translations data +fn are_there_translations() -> bool { + get_translations_blob()[0] != TERMINATE_BYTE +} + +/// Returns the translation at the given index. +fn get_translation_by_index(index: usize) -> Option<&'static str> { + let header = unwrap!(TranslationsHeader::from_flash()); + let translations_table = header.get_translations_table(); + + let offsets = read_u16_list(translations_table.offset_table_data); + + // The index is out of bounds. + // May happen when new firmware is using older translations and the string + // is not defined yet. + // Fallback to english. + if index > offsets.len() { + return None; + } + + // Identifying where the string starts. + let start_offset = offsets[index]; + + // In case the string is last, we may need to strip the alignment bytes + let mut strip_alignment_bytes = false; + + // Getting the data length - difference between the current offset and the next + // one. + let len = if index == offsets.len() - 1 { + // The last element in the table + strip_alignment_bytes = true; + translations_table.overall_len - start_offset + } else { + offsets[index + 1] - start_offset + }; + + // The string is not defined in the blob. + // May happen when old firmware is using newer translations and the string + // was deleted in the newer version. + // Fallback to english. + if len == 0 { + return None; + } + + let start = start_offset as usize; + let mut end = start + len as usize; + let translations_data = header.get_all_translations_data(); + if strip_alignment_bytes { + // Strip possible alignment bytes at the end + while translations_data[end - 1] == ALIGNMENT_BYTE { + end -= 1; + } + } + let data = &translations_data[start..end]; + str::from_utf8(data).ok() +} + +/// Given blob data returns a list of (u16, u16) values. +fn read_u16_pairs_list(bytes: &'static [u8]) -> &'static [(u16, u16)] { + let (prefix, data, suffix) = unsafe { bytes.align_to::<(u16, u16)>() }; + if !prefix.is_empty() || !suffix.is_empty() { + return &[]; + } + data +} + +/// Given blob data returns a list of u16 values. +fn read_u16_list(bytes: &'static [u8]) -> &'static [u16] { + let (prefix, data, suffix) = unsafe { bytes.align_to::() }; + if !prefix.is_empty() || !suffix.is_empty() { + return &[]; + } + data +} + +pub struct OffsetLen { + pub offset: u16, + pub len: u16, +} + +/// Information about specific font - where it starts and how long it is +fn get_font_offset_from_id(font_id: u16) -> Option { + let font_table = unwrap!(TranslationsHeader::from_flash()).get_font_table(); + match_first_u16_pair( + font_id, + font_table.offset_table_data, + font_table.overall_len, + ) +} + +/// Returns the offset and length of the u16 pair whose first element matches +/// `code_id`. +fn match_first_u16_pair( + code_id: u16, + pairs_data: &'static [u8], + overall_len: u16, +) -> Option { + // To get the length of the font, need to read the offset of the next font + let mut font_offset: Option = None; + let mut next_offset: Option = None; + + for &(code, offset) in read_u16_pairs_list(pairs_data) { + if code == code_id { + font_offset = Some(offset); + } else if font_offset.is_some() { + next_offset = Some(offset); + break; + } + } + + if let Some(offset) = font_offset { + // When our font was last, there is no next offset - use the data size + let next = next_offset.unwrap_or(overall_len); + let len = next - offset; + return Some(OffsetLen { offset, len }); + } + + None +} + +// Get information about a given glyph with a given font-offset +fn get_glyph_data(char_code: u16, font_offset: u16, font_len: u16) -> Option { + let font_data = + unwrap!(TranslationsHeader::from_flash()).get_font_data_offset(font_offset, font_len); + let data_size = font_data.len() as u16; + + let (glyph_amount_bytes, data) = font_data.split_at(2); + let glyph_amount = u16::from_le_bytes(unwrap!(glyph_amount_bytes.try_into())); + + // Each entry has 2 bytes of glyph-code and 2 bytes of offset + const GLYPH_ENTRY_SIZE: usize = 4; + let pairs_len = glyph_amount as usize * GLYPH_ENTRY_SIZE; + let pairs_data = &data[..pairs_len]; + + match_first_u16_pair(char_code, pairs_data, data_size) +} + +/// Get information about the glyph in the given font. +/// First finding out the offset of the font within the translations data. +/// Then finding out the offset of the glyph within the font. +/// Returning the absolute offset of the glyph within the translations data - +/// together with its length. +fn get_glyph_font_data(char_code: u16, font_id: u16) -> Option { + if !are_there_translations() { + return None; + } + + if let Some(font_offset) = get_font_offset_from_id(font_id) { + if let Some(glyph_data) = get_glyph_data(char_code, font_offset.offset, font_offset.len) { + let font_start_offset = unwrap!(TranslationsHeader::from_flash()).font_start_offset(); + let final_offset = font_start_offset + font_offset.offset + glyph_data.offset; + return Some(OffsetLen { + offset: final_offset, + len: glyph_data.len, + }); + } + } + None +} + +#[no_mangle] +pub extern "C" fn get_utf8_glyph(char_code: cty::uint16_t, font: cty::c_int) -> PointerData { + // C will send a negative number + let font_abs = font.unsigned_abs() as u16; + + if let Some(glyph_data) = get_glyph_font_data(char_code, font_abs) { + return get_pointer_with_offset(glyph_data.offset, glyph_data.len); + } + + PointerData { + ptr: core::ptr::null(), + len: 0, + } +} diff --git a/core/embed/rust/src/ui/translations/order.json b/core/embed/rust/src/ui/translations/order.json new file mode 100644 index 000000000..7a3a6f415 --- /dev/null +++ b/core/embed/rust/src/ui/translations/order.json @@ -0,0 +1,834 @@ +{ + "0": "addr_mismatch__contact_support", + "1": "addr_mismatch__key_mismatch", + "2": "addr_mismatch__mismatch", + "3": "addr_mismatch__support_url", + "4": "addr_mismatch__title", + "5": "addr_mismatch__title_key_mismatch", + "6": "addr_mismatch__wrong_derication_path", + "7": "addr_mismatch__xpub_mismatch", + "8": "address__address", + "9": "address__public_key", + "10": "address__title_cosigner", + "11": "address__title_receive_address", + "12": "address__title_yours", + "13": "address_details__account", + "14": "address_details__derivation_path", + "15": "address_details__title_receive_address", + "16": "address_details__title_receiving_to", + "17": "authenticate__confirm_template", + "18": "authenticate__header", + "19": "auto_lock__change_template", + "20": "auto_lock__title", + "21": "backup__can_back_up_anytime", + "22": "backup__it_should_be_backed_up", + "23": "backup__it_should_be_backed_up_now", + "24": "backup__new_wallet_created", + "25": "backup__new_wallet_successfully_created", + "26": "backup__recover_anytime", + "27": "backup__title_backup_wallet", + "28": "backup__title_skip", + "29": "backup__want_to_skip", + "30": "binance__buy", + "31": "binance__confirm_cancel", + "32": "binance__confirm_input", + "33": "binance__confirm_order", + "34": "binance__confirm_output", + "35": "binance__order_id", + "36": "binance__pair", + "37": "binance__price", + "38": "binance__quantity", + "39": "binance__sell", + "40": "binance__sender_address", + "41": "binance__side", + "42": "binance__unknown", + "43": "bitcoin__commitment_data", + "44": "bitcoin__confirm_locktime", + "45": "bitcoin__create_proof_of_ownership", + "46": "bitcoin__high_mining_fee_template", + "47": "bitcoin__locktime_no_effect", + "48": "bitcoin__locktime_set_to", + "49": "bitcoin__locktime_set_to_blockheight", + "50": "bitcoin__lot_of_change_outputs", + "51": "bitcoin__multiple_accounts", + "52": "bitcoin__new_fee_rate", + "53": "bitcoin__simple_send_of", + "54": "bitcoin__ticket_amount", + "55": "bitcoin__title_confirm_details", + "56": "bitcoin__title_finalize_transaction", + "57": "bitcoin__title_high_mining_fee", + "58": "bitcoin__title_meld_transaction", + "59": "bitcoin__title_modify_amount", + "60": "bitcoin__title_payjoin", + "61": "bitcoin__title_proof_of_ownership", + "62": "bitcoin__title_purchase_ticket", + "63": "bitcoin__title_update_transaction", + "64": "bitcoin__unknown_path", + "65": "bitcoin__unknown_transaction", + "66": "bitcoin__unusually_high_fee", + "67": "bitcoin__unverified_external_inputs", + "68": "bitcoin__valid_signature", + "69": "bitcoin__voting_rights", + "70": "buttons__abort", + "71": "buttons__access", + "72": "buttons__again", + "73": "buttons__allow", + "74": "buttons__back_up", + "75": "buttons__cancel", + "76": "buttons__change", + "77": "buttons__check", + "78": "buttons__check_again", + "79": "buttons__close", + "80": "buttons__confirm", + "81": "buttons__continue", + "82": "buttons__details", + "83": "buttons__enable", + "84": "buttons__enter", + "85": "buttons__enter_share", + "86": "buttons__export", + "87": "buttons__format", + "88": "buttons__go_back", + "89": "buttons__hold_to_confirm", + "90": "buttons__info", + "91": "buttons__install", + "92": "buttons__more_info", + "93": "buttons__ok_i_understand", + "94": "buttons__purchase", + "95": "buttons__quit", + "96": "buttons__restart", + "97": "buttons__retry", + "98": "buttons__select", + "99": "buttons__set", + "100": "buttons__show_all", + "101": "buttons__show_words", + "102": "buttons__skip", + "103": "buttons__try_again", + "104": "buttons__turn_off", + "105": "buttons__turn_on", + "106": "cardano__addr_base", + "107": "cardano__addr_enterprise", + "108": "cardano__addr_legacy", + "109": "cardano__addr_pointer", + "110": "cardano__addr_reward", + "111": "cardano__address_no_staking", + "112": "cardano__amount", + "113": "cardano__amount_burned_decimals_unknown", + "114": "cardano__amount_minted_decimals_unknown", + "115": "cardano__amount_sent_decimals_unknown", + "116": "cardano__anonymous_pool", + "117": "cardano__asset_fingerprint", + "118": "cardano__auxiliary_data_hash", + "119": "cardano__block", + "120": "cardano__catalyst", + "121": "cardano__certificate", + "122": "cardano__certificate_path", + "123": "cardano__change_output", + "124": "cardano__change_output_path", + "125": "cardano__change_output_staking_path", + "126": "cardano__check_all_items", + "127": "cardano__choose_level_of_details", + "128": "cardano__collateral_input_id", + "129": "cardano__collateral_input_index", + "130": "cardano__collateral_output_contains_tokens", + "131": "cardano__collateral_return", + "132": "cardano__confirm", + "133": "cardano__confirm_signing_stake_pool", + "134": "cardano__confirm_transaction", + "135": "cardano__confirming_a_multisig_transaction", + "136": "cardano__confirming_pool_registration", + "137": "cardano__confirming_transction", + "138": "cardano__cost", + "139": "cardano__credential_mismatch", + "140": "cardano__datum_hash", + "141": "cardano__delegating_to", + "142": "cardano__for_account_and_index_template", + "143": "cardano__for_account_template", + "144": "cardano__for_key_hash", + "145": "cardano__for_script", + "146": "cardano__inline_datum", + "147": "cardano__input_id", + "148": "cardano__input_index", + "149": "cardano__intro_text_address", + "150": "cardano__intro_text_change", + "151": "cardano__intro_text_owned_by_device", + "152": "cardano__intro_text_registration_payment", + "153": "cardano__key_hash", + "154": "cardano__margin", + "155": "cardano__multisig_path", + "156": "cardano__nested_scripts_template", + "157": "cardano__network", + "158": "cardano__no_output_tx", + "159": "cardano__nonce", + "160": "cardano__other", + "161": "cardano__path", + "162": "cardano__pledge", + "163": "cardano__pointer", + "164": "cardano__policy_id", + "165": "cardano__pool_metadata_hash", + "166": "cardano__pool_metadata_url", + "167": "cardano__pool_owner", + "168": "cardano__pool_owner_path", + "169": "cardano__pool_reward_account", + "170": "cardano__reference_input_id", + "171": "cardano__reference_input_index", + "172": "cardano__reference_script", + "173": "cardano__required_signer", + "174": "cardano__reward", + "175": "cardano__reward_address", + "176": "cardano__reward_eligibility_warning", + "177": "cardano__rewards_go_to", + "178": "cardano__script", + "179": "cardano__script_all", + "180": "cardano__script_any", + "181": "cardano__script_data_hash", + "182": "cardano__script_hash", + "183": "cardano__script_invalid_before", + "184": "cardano__script_invalid_hereafter", + "185": "cardano__script_key", + "186": "cardano__script_n_of_k", + "187": "cardano__script_reward", + "188": "cardano__sending", + "189": "cardano__show_simple", + "190": "cardano__sign_tx_path_template", + "191": "cardano__stake_delegation", + "192": "cardano__stake_deregistration", + "193": "cardano__stake_pool_registration", + "194": "cardano__stake_pool_registration_pool_id", + "195": "cardano__stake_registration", + "196": "cardano__staking_key_for_account", + "197": "cardano__to_pool", + "198": "cardano__token_minting_path", + "199": "cardano__total_collateral", + "200": "cardano__transaction", + "201": "cardano__transaction_contains_minting_or_burning", + "202": "cardano__transaction_contains_script_address_no_datum", + "203": "cardano__transaction_fee", + "204": "cardano__transaction_id", + "205": "cardano__transaction_no_collateral_input", + "206": "cardano__transaction_no_script_data_hash", + "207": "cardano__transaction_output_contains_tokens", + "208": "cardano__ttl", + "209": "cardano__unknown", + "210": "cardano__unknown_collateral_amount", + "211": "cardano__unusual_path", + "212": "cardano__valid_since", + "213": "cardano__verify_script", + "214": "cardano__vote_key_registration", + "215": "cardano__vote_public_key", + "216": "cardano__voting_purpose", + "217": "cardano__warning", + "218": "cardano__weight", + "219": "cardano__withdrawal_for_address_template", + "220": "cardano__witness_path", + "221": "cardano__x_of_y_signatures_template", + "222": "coinjoin__access_account", + "223": "coinjoin__do_not_disconnect", + "224": "coinjoin__max_mining_fee", + "225": "coinjoin__max_rounds", + "226": "coinjoin__title", + "227": "coinjoin__title_do_not_disconnect", + "228": "coinjoin__title_progress", + "229": "coinjoin__waiting_for_others", + "230": "confirm_total__account", + "231": "confirm_total__fee_rate", + "232": "confirm_total__sending_from_account", + "233": "confirm_total__title_fee", + "234": "confirm_total__title_sending_from", + "235": "debug__loading_seed", + "236": "debug__loading_seed_not_recommended", + "237": "device_name__change_template", + "238": "device_name__title", + "239": "entropy__send", + "240": "entropy__title", + "241": "entropy__title_confirm", + "242": "eos__about_to_sign_template", + "243": "eos__account", + "244": "eos__action_name", + "245": "eos__amount", + "246": "eos__arbitrary_data", + "247": "eos__buy_ram", + "248": "eos__bytes", + "249": "eos__cancel_vote", + "250": "eos__checksum", + "251": "eos__code", + "252": "eos__contract", + "253": "eos__cpu", + "254": "eos__creator", + "255": "eos__delegate", + "256": "eos__delete_auth", + "257": "eos__from", + "258": "eos__link_auth", + "259": "eos__memo", + "260": "eos__name", + "261": "eos__net", + "262": "eos__new_account", + "263": "eos__no", + "264": "eos__owner", + "265": "eos__parent", + "266": "eos__payer", + "267": "eos__permission", + "268": "eos__proxy", + "269": "eos__receiver", + "270": "eos__refund", + "271": "eos__requirement", + "272": "eos__sell_ram", + "273": "eos__sender", + "274": "eos__sign_transaction", + "275": "eos__threshold", + "276": "eos__to", + "277": "eos__transfer", + "278": "eos__type", + "279": "eos__undelegate", + "280": "eos__unlink_auth", + "281": "eos__update_auth", + "282": "eos__vote_for_producers", + "283": "eos__vote_for_proxy", + "284": "eos__voter", + "285": "eos__yes", + "286": "ethereum__amount_sent", + "287": "ethereum__confirm_fee", + "288": "ethereum__contract", + "289": "ethereum__data_size_template", + "290": "ethereum__gas_limit", + "291": "ethereum__gas_price", + "292": "ethereum__max_gas_price", + "293": "ethereum__name_and_version", + "294": "ethereum__new_contract", + "295": "ethereum__no_message_field", + "296": "ethereum__priority_fee", + "297": "ethereum__show_full_array", + "298": "ethereum__show_full_domain", + "299": "ethereum__show_full_message", + "300": "ethereum__show_full_struct", + "301": "ethereum__sign_eip712", + "302": "ethereum__title_confirm_data", + "303": "ethereum__title_confirm_domain", + "304": "ethereum__title_confirm_message", + "305": "ethereum__title_confirm_struct", + "306": "ethereum__title_confirm_typed_data", + "307": "ethereum__title_signing_address", + "308": "ethereum__units_template", + "309": "ethereum__unknown_token", + "310": "ethereum__valid_signature", + "311": "experimental_mode__enable", + "312": "experimental_mode__only_for_dev", + "313": "experimental_mode__title", + "314": "fido__already_registered", + "315": "fido__device_already_registered", + "316": "fido__device_already_registered_with_template", + "317": "fido__device_not_registered", + "318": "fido__does_not_belong", + "319": "fido__erase_credentials", + "320": "fido__export_credentials", + "321": "fido__not_registered", + "322": "fido__not_registered_with_template", + "323": "fido__please_enable_pin_protection", + "324": "fido__title_authenticate", + "325": "fido__title_import_credential", + "326": "fido__title_list_credentials", + "327": "fido__title_register", + "328": "fido__title_remove_credential", + "329": "fido__title_reset", + "330": "fido__title_u2f_auth", + "331": "fido__title_u2f_register", + "332": "fido__title_verify_user", + "333": "fido__unable_to_verify_user", + "334": "fido__wanna_erase_credentials", + "335": "firmware_update__title", + "336": "firmware_update__title_fingerprint", + "337": "homescreen__click_to_connect", + "338": "homescreen__click_to_unlock", + "339": "homescreen__title_backup_failed", + "340": "homescreen__title_backup_needed", + "341": "homescreen__title_coinjoin_authorized", + "342": "homescreen__title_experimental_mode", + "343": "homescreen__title_hold_to_lock", + "344": "homescreen__title_no_usb_connection", + "345": "homescreen__title_pin_not_set", + "346": "homescreen__title_seedless", + "347": "homescreen__title_set", + "348": "inputs__back", + "349": "inputs__cancel", + "350": "inputs__delete", + "351": "inputs__enter", + "352": "inputs__return", + "353": "inputs__show", + "354": "inputs__space", + "355": "joint__title", + "356": "joint__to_the_total_amount", + "357": "joint__you_are_contributing", + "358": "language__change_template", + "359": "language__set_default", + "360": "language__title_change", + "361": "lockscreen__tap_to_connect", + "362": "lockscreen__tap_to_unlock", + "363": "lockscreen__title_locked", + "364": "lockscreen__title_not_connected", + "365": "misc__decrypt_value", + "366": "misc__encrypt_value", + "367": "misc__title_suite_labeling", + "368": "modify_amount__address", + "369": "modify_amount__decrease_amount", + "370": "modify_amount__increase_amount", + "371": "modify_amount__new_amount", + "372": "modify_amount__title", + "373": "modify_fee__decrease_fee", + "374": "modify_fee__fee_rate", + "375": "modify_fee__increase_fee", + "376": "modify_fee__new_transaction_fee", + "377": "modify_fee__no_change", + "378": "modify_fee__title", + "379": "modify_fee__transaction_fee", + "380": "monero__confirm_export", + "381": "monero__confirm_fee", + "382": "monero__confirm_ki_sync", + "383": "monero__confirm_refresh", + "384": "monero__confirm_unlock_time", + "385": "monero__hashing_inputs", + "386": "monero__payment_id", + "387": "monero__postprocessing", + "388": "monero__processing", + "389": "monero__processing_inputs", + "390": "monero__processing_outputs", + "391": "monero__signing", + "392": "monero__signing_inputs", + "393": "monero__unlock_time_set_template", + "394": "monero__wanna_export_tx_der", + "395": "monero__wanna_export_tx_key", + "396": "monero__wanna_export_watchkey", + "397": "monero__wanna_start_refresh", + "398": "monero__wanna_sync_key_images", + "399": "nem__absolute", + "400": "nem__activate", + "401": "nem__add", + "402": "nem__confirm_action", + "403": "nem__confirm_address", + "404": "nem__confirm_creation_fee", + "405": "nem__confirm_fee", + "406": "nem__confirm_mosaic", + "407": "nem__confirm_multisig_fee", + "408": "nem__confirm_namespace", + "409": "nem__confirm_payload", + "410": "nem__confirm_properties", + "411": "nem__confirm_rental_fee", + "412": "nem__confirm_transfer_of", + "413": "nem__convert_account_to_multisig", + "414": "nem__cosign_transaction_for", + "415": "nem__cosignatory", + "416": "nem__create_mosaic", + "417": "nem__create_namespace", + "418": "nem__deactivate", + "419": "nem__decrease", + "420": "nem__description", + "421": "nem__divisibility_and_levy_cannot_be_shown", + "422": "nem__encrypted", + "423": "nem__final_confirm", + "424": "nem__immutable", + "425": "nem__increase", + "426": "nem__initial_supply", + "427": "nem__initiate_transaction_for", + "428": "nem__levy_divisibility", + "429": "nem__levy_fee", + "430": "nem__levy_fee_of", + "431": "nem__levy_mosaic", + "432": "nem__levy_namespace", + "433": "nem__levy_recipient", + "434": "nem__levy_type", + "435": "nem__modify_supply_for", + "436": "nem__modify_the_number_of_cosignatories_by", + "437": "nem__mutable", + "438": "nem__no", + "439": "nem__of", + "440": "nem__percentile", + "441": "nem__raw_units_template", + "442": "nem__remote_harvesting", + "443": "nem__remove", + "444": "nem__set_minimum_cosignatories_to", + "445": "nem__sign_tx_fee_template", + "446": "nem__supply_change", + "447": "nem__supply_units_template", + "448": "nem__transferable", + "449": "nem__under_namespace", + "450": "nem__unencrypted", + "451": "nem__unknown_mosaic", + "452": "nem__yes", + "453": "passphrase__access_hidden_wallet", + "454": "passphrase__always_on_device", + "455": "passphrase__from_host_not_shown", + "456": "passphrase__hidden_wallet", + "457": "passphrase__hide", + "458": "passphrase__next_screen_will_show_passphrase", + "459": "passphrase__please_enter", + "460": "passphrase__revoke_on_device", + "461": "passphrase__title_confirm", + "462": "passphrase__title_enter", + "463": "passphrase__title_hide", + "464": "passphrase__title_settings", + "465": "passphrase__title_source", + "466": "passphrase__turn_off", + "467": "passphrase__turn_on", + "468": "pin__change", + "469": "pin__changed", + "470": "pin__cursor_will_change", + "471": "pin__diff_from_wipe_code", + "472": "pin__disabled", + "473": "pin__enabled", + "474": "pin__enter", + "475": "pin__enter_new", + "476": "pin__entered_not_valid", + "477": "pin__info", + "478": "pin__invalid_pin", + "479": "pin__last_attempt", + "480": "pin__mismatch", + "481": "pin__pin_mismatch", + "482": "pin__please_check_again", + "483": "pin__reenter_new", + "484": "pin__reenter_to_confirm", + "485": "pin__should_be_long", + "486": "pin__title_check_pin", + "487": "pin__title_settings", + "488": "pin__title_wrong_pin", + "489": "pin__tries_left", + "490": "pin__turn_off", + "491": "pin__turn_on", + "492": "pin__wrong_pin", + "493": "progress__authenticity_check", + "494": "progress__done", + "495": "progress__loading_transaction", + "496": "progress__one_second_left", + "497": "progress__please_wait", + "498": "progress__processing", + "499": "progress__refreshing", + "500": "progress__signing_transaction", + "501": "progress__syncing", + "502": "progress__x_seconds_left_template", + "503": "reboot_to_bootloader__restart", + "504": "reboot_to_bootloader__title", + "505": "reboot_to_bootloader__version_by_template", + "506": "recovery__cancel_dry_run", + "507": "recovery__check_dry_run", + "508": "recovery__cursor_will_change", + "509": "recovery__dry_run_bip39_valid_match", + "510": "recovery__dry_run_bip39_valid_mismatch", + "511": "recovery__dry_run_slip39_valid_match", + "512": "recovery__dry_run_slip39_valid_mismatch", + "513": "recovery__enter_any_share", + "514": "recovery__enter_backup", + "515": "recovery__enter_different_share", + "516": "recovery__enter_share_from_diff_group", + "517": "recovery__group_num_template", + "518": "recovery__group_threshold_reached", + "519": "recovery__invalid_seed_entered", + "520": "recovery__invalid_share_entered", + "521": "recovery__more_shares_needed", + "522": "recovery__num_of_words", + "523": "recovery__only_first_n_letters", + "524": "recovery__progress_will_be_lost", + "525": "recovery__select_num_of_words", + "526": "recovery__share_already_entered", + "527": "recovery__share_from_another_shamir", + "528": "recovery__share_num_template", + "529": "recovery__title", + "530": "recovery__title_cancel_dry_run", + "531": "recovery__title_cancel_recovery", + "532": "recovery__title_dry_run", + "533": "recovery__title_recover", + "534": "recovery__title_remaining_shares", + "535": "recovery__type_word_x_of_y_template", + "536": "recovery__wallet_recovered", + "537": "recovery__wanna_cancel_dry_run", + "538": "recovery__wanna_cancel_recovery", + "539": "recovery__word_count_template", + "540": "recovery__word_x_of_y_template", + "541": "recovery__x_of_y_entered_template", + "542": "recovery__you_have_entered", + "543": "reset__advanced_group_threshold_info", + "544": "reset__all_x_of_y_template", + "545": "reset__any_x_of_y_template", + "546": "reset__button_create", + "547": "reset__button_recover", + "548": "reset__by_continuing", + "549": "reset__check_backup_title", + "550": "reset__check_group_share_title_template", + "551": "reset__check_seed_title", + "552": "reset__check_share_title_template", + "553": "reset__continue_with_next_share", + "554": "reset__continue_with_share_template", + "555": "reset__finished_verifying_group_template", + "556": "reset__finished_verifying_seed", + "557": "reset__finished_verifying_shares", + "558": "reset__group_description", + "559": "reset__group_info", + "560": "reset__group_share_checked_successfully_template", + "561": "reset__group_share_title_template", + "562": "reset__more_info_at", + "563": "reset__need_all_share_template", + "564": "reset__need_any_share_template", + "565": "reset__needed_to_form_a_group", + "566": "reset__needed_to_recover_your_wallet", + "567": "reset__never_make_digital_copy", + "568": "reset__num_of_share_holders_template", + "569": "reset__num_of_shares_advanced_info_template", + "570": "reset__num_of_shares_basic_info", + "571": "reset__num_shares_for_group_template", + "572": "reset__number_of_shares_info", + "573": "reset__one_share", + "574": "reset__only_one_share_will_be_created", + "575": "reset__recovery_seed_title", + "576": "reset__recovery_share_title_template", + "577": "reset__required_number_of_groups", + "578": "reset__select_correct_word", + "579": "reset__select_word_template", + "580": "reset__select_word_x_of_y_template", + "581": "reset__set_it_to_count_template", + "582": "reset__share_checked_successfully_template", + "583": "reset__share_words_title", + "584": "reset__slip39_checklist_num_groups", + "585": "reset__slip39_checklist_num_shares", + "586": "reset__slip39_checklist_set_num_groups", + "587": "reset__slip39_checklist_set_num_shares", + "588": "reset__slip39_checklist_set_sizes", + "589": "reset__slip39_checklist_set_sizes_longer", + "590": "reset__slip39_checklist_set_threshold", + "591": "reset__slip39_checklist_title", + "592": "reset__slip39_checklist_write_down", + "593": "reset__slip39_checklist_write_down_recovery", + "594": "reset__the_threshold_sets_the_number_of_shares", + "595": "reset__threshold_info", + "596": "reset__title_backup_is_done", + "597": "reset__title_create_wallet", + "598": "reset__title_create_wallet_shamir", + "599": "reset__title_group_threshold", + "600": "reset__title_number_of_groups", + "601": "reset__title_number_of_shares", + "602": "reset__title_set_group_threshold", + "603": "reset__title_set_number_of_groups", + "604": "reset__title_set_number_of_shares", + "605": "reset__title_set_threshold", + "606": "reset__to_form_group_template", + "607": "reset__tos_link", + "608": "reset__total_number_of_shares_in_group_template", + "609": "reset__use_your_backup", + "610": "reset__write_down_words_template", + "611": "reset__wrong_word_selected", + "612": "reset__you_need_one_share", + "613": "reset__your_backup_is_done", + "614": "ripple__confirm_tag", + "615": "ripple__destination_tag_template", + "616": "rotation__change_template", + "617": "rotation__east", + "618": "rotation__north", + "619": "rotation__south", + "620": "rotation__title_change", + "621": "rotation__west", + "622": "safety_checks__approve_unsafe_always", + "623": "safety_checks__approve_unsafe_temporary", + "624": "safety_checks__enforce_strict", + "625": "safety_checks__title", + "626": "safety_checks__title_safety_override", + "627": "sd_card__all_data_will_be_lost", + "628": "sd_card__card_required", + "629": "sd_card__disable", + "630": "sd_card__disabled", + "631": "sd_card__enable", + "632": "sd_card__enabled", + "633": "sd_card__error", + "634": "sd_card__format_card", + "635": "sd_card__insert_correct_card", + "636": "sd_card__please_insert", + "637": "sd_card__please_unplug_and_insert", + "638": "sd_card__problem_accessing", + "639": "sd_card__refresh", + "640": "sd_card__refreshed", + "641": "sd_card__restart", + "642": "sd_card__title", + "643": "sd_card__title_problem", + "644": "sd_card__unknown_filesystem", + "645": "sd_card__unplug_and_insert_correct", + "646": "sd_card__use_different_card", + "647": "sd_card__wanna_format", + "648": "sd_card__wrong_sd_card", + "649": "send__address_path", + "650": "send__amount", + "651": "send__confirm_sending", + "652": "send__from_multiple_accounts", + "653": "send__including_fee", + "654": "send__maximum_fee", + "655": "send__receiving_to_multisig", + "656": "send__title_amount", + "657": "send__title_confirm_sending", + "658": "send__title_joint_transaction", + "659": "send__title_receiving_to", + "660": "send__title_recipient", + "661": "send__title_sending", + "662": "send__title_sending_amount", + "663": "send__title_sending_to", + "664": "send__to_the_total_amount", + "665": "send__total_amount", + "666": "send__transaction_id", + "667": "send__you_are_contributing", + "668": "share_words__words_in_order", + "669": "share_words__wrote_down_all", + "670": "sign_message__bytes_template", + "671": "sign_message__confirm_address", + "672": "sign_message__confirm_message", + "673": "sign_message__message_size", + "674": "sign_message__verify_address", + "675": "stellar__account", + "676": "stellar__account_merge", + "677": "stellar__account_thresholds", + "678": "stellar__add_signer", + "679": "stellar__add_trust", + "680": "stellar__all_will_be_sent_to", + "681": "stellar__allow_trust", + "682": "stellar__asset", + "683": "stellar__bump_sequence", + "684": "stellar__buying", + "685": "stellar__clear_data", + "686": "stellar__clear_flags", + "687": "stellar__confirm_issuer", + "688": "stellar__confirm_memo", + "689": "stellar__confirm_network", + "690": "stellar__confirm_operation", + "691": "stellar__confirm_stellar", + "692": "stellar__confirm_timebounds", + "693": "stellar__create_account", + "694": "stellar__debited_amount", + "695": "stellar__delete", + "696": "stellar__delete_passive_offer", + "697": "stellar__delete_trust", + "698": "stellar__destination", + "699": "stellar__exchanges_require_memo", + "700": "stellar__final_confirm", + "701": "stellar__hash", + "702": "stellar__high", + "703": "stellar__home_domain", + "704": "stellar__inflation", + "705": "stellar__initial_balance", + "706": "stellar__initialize_signing_with", + "707": "stellar__issuer_template", + "708": "stellar__key", + "709": "stellar__limit", + "710": "stellar__low", + "711": "stellar__master_weight", + "712": "stellar__medium", + "713": "stellar__new_offer", + "714": "stellar__new_passive_offer", + "715": "stellar__no_memo_set", + "716": "stellar__no_restriction", + "717": "stellar__on_network_template", + "718": "stellar__path_pay", + "719": "stellar__path_pay_at_least", + "720": "stellar__pay", + "721": "stellar__pay_at_most", + "722": "stellar__preauth_transaction", + "723": "stellar__price_per_template", + "724": "stellar__private_network", + "725": "stellar__remove_signer", + "726": "stellar__revoke_trust", + "727": "stellar__selling", + "728": "stellar__set_data", + "729": "stellar__set_flags", + "730": "stellar__set_sequence_to_template", + "731": "stellar__sign_tx_count_template", + "732": "stellar__sign_tx_fee_template", + "733": "stellar__source_account", + "734": "stellar__testnet_network", + "735": "stellar__trusted_account", + "736": "stellar__update", + "737": "stellar__valid_from", + "738": "stellar__valid_to", + "739": "stellar__value_sha256", + "740": "stellar__wanna_clean_value_key_template", + "741": "stellar__your_account", + "742": "tezos__address", + "743": "tezos__amount", + "744": "tezos__baker_address", + "745": "tezos__balance", + "746": "tezos__ballot", + "747": "tezos__confirm_delegation", + "748": "tezos__confirm_origination", + "749": "tezos__delegator", + "750": "tezos__fee", + "751": "tezos__proposal", + "752": "tezos__register_delegate", + "753": "tezos__remove_delegation", + "754": "tezos__submit_ballot", + "755": "tezos__submit_proposal", + "756": "tezos__submit_proposals", + "757": "tutorial__middle_click", + "758": "tutorial__press_and_hold", + "759": "tutorial__ready_to_use", + "760": "tutorial__scroll_down", + "761": "tutorial__sure_you_want_skip", + "762": "tutorial__title_hello", + "763": "tutorial__title_screen_scroll", + "764": "tutorial__title_skip", + "765": "tutorial__title_tutorial_complete", + "766": "tutorial__use_trezor", + "767": "tutorial__welcome_press_right", + "768": "u2f__get", + "769": "u2f__set_template", + "770": "u2f__title_get", + "771": "u2f__title_set", + "772": "wipe__info", + "773": "wipe__title", + "774": "wipe__want_to_wipe", + "775": "wipe_code__change", + "776": "wipe_code__changed", + "777": "wipe_code__diff_from_pin", + "778": "wipe_code__disabled", + "779": "wipe_code__enabled", + "780": "wipe_code__enter_new", + "781": "wipe_code__info", + "782": "wipe_code__invalid", + "783": "wipe_code__mismatch", + "784": "wipe_code__reenter", + "785": "wipe_code__reenter_to_confirm", + "786": "wipe_code__title_check", + "787": "wipe_code__title_invalid", + "788": "wipe_code__title_settings", + "789": "wipe_code__turn_off", + "790": "wipe_code__turn_on", + "791": "wipe_code__wipe_code_mismatch", + "792": "word_count__title", + "793": "words__are_you_sure", + "794": "words__buying", + "795": "words__continue_anyway", + "796": "words__continue_with", + "797": "words__error", + "798": "words__from", + "799": "words__keep_it_safe", + "800": "words__know_what_your_doing", + "801": "words__my_trezor", + "802": "words__outputs", + "803": "words__please_check_again", + "804": "words__please_try_again", + "805": "words__really_wanna", + "806": "words__sign", + "807": "words__title_check", + "808": "words__title_group", + "809": "words__title_information", + "810": "words__title_remember", + "811": "words__title_share", + "812": "words__title_shares", + "813": "words__title_success", + "814": "words__title_summary", + "815": "words__title_threshold", + "816": "words__unknown", + "817": "words__warning", + "818": "plurals__sign_x_actions", + "819": "plurals__contains_x_keys", + "820": "words__contains", + "821": "plurals__x_shares_needed", + "822": "words__array_of", + "823": "recovery__x_more_shares_needed_template_plural", + "824": "plurals__transaction_of_x_operations", + "825": "plurals__x_groups_needed", + "826": "recovery__x_more_items_starting_template_plural", + "827": "plurals__lock_after_x_hours", + "828": "plurals__lock_after_x_milliseconds", + "829": "plurals__lock_after_x_minutes", + "830": "plurals__lock_after_x_seconds", + "831": "words__confirm" +} diff --git a/core/embed/rust/src/ui/translations/order.py b/core/embed/rust/src/ui/translations/order.py new file mode 100644 index 000000000..df36859d4 --- /dev/null +++ b/core/embed/rust/src/ui/translations/order.py @@ -0,0 +1,42 @@ +from __future__ import annotations +import json + +from pathlib import Path + +HERE = Path(__file__).parent + +language_file = HERE / "en.json" +output_file = HERE / "order.json" + +def generate_new_order() -> None: + translations = json.loads(language_file.read_text())["translations"] + old_order: dict[str, str] = json.loads(output_file.read_text()) + + old_unique_items: set[str] = set() + for _, item in old_order.items(): + old_unique_items.add(item) + + new_unique_items: set[str] = set() + for section_name, section in translations.items(): + for k in section: + name = f"{section_name}__{k}" + new_unique_items.add(name) + + new_items = sorted(new_unique_items - old_unique_items) + if new_items: + print("Found new items:") + for item in new_items: + print(item) + + first_update_index = len(old_order) + for index, item in enumerate(new_items): + new_index = str(first_update_index + index) + print(f"Adding {item} to {new_index}") + old_order[new_index] = item + + output_file.write_text(json.dumps(old_order, indent=2) + "\n") + else: + print("No new items found.") + +if __name__ == "__main__": + generate_new_order() diff --git a/core/embed/rust/trezorhal.h b/core/embed/rust/trezorhal.h index e34711d70..fd64ba5b8 100644 --- a/core/embed/rust/trezorhal.h +++ b/core/embed/rust/trezorhal.h @@ -12,6 +12,7 @@ #include "secbool.h" #include "storage.h" #include "touch.h" +#include "translations.h" #include "usb.h" #include "bip39.h" diff --git a/core/embed/trezorhal/stm32f4/mpu.c b/core/embed/trezorhal/stm32f4/mpu.c index 483eb780c..98de82371 100644 --- a/core/embed/trezorhal/stm32f4/mpu.c +++ b/core/embed/trezorhal/stm32f4/mpu.c @@ -125,12 +125,24 @@ void mpu_config_firmware(void) { MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH | LL_MPU_REGION_SIZE_64KB | LL_MPU_REGION_FULL_ACCESS | MPU_RASR_XN_Msk; - // Storage#2 (0x08110000 - 0x0811FFFF, 64 KiB, read-write, execute never) + +#ifdef USE_OPTIGA + // Translations + Storage#2 - secret (0x08104000 - 0x0811FFFF, 112 KiB, + // read-write, execute never) MPU->RNR = MPU_REGION_NUMBER2; - MPU->RBAR = FLASH_BASE + 0x110000; + MPU->RBAR = FLASH_BASE + 0x100000; MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH | - LL_MPU_REGION_SIZE_64KB | LL_MPU_REGION_FULL_ACCESS | + LL_MPU_REGION_SIZE_128KB | LL_MPU_REGION_FULL_ACCESS | + MPU_RASR_XN_Msk | MPU_SUBREGION_DISABLE(0x01); +#else + // Translations + Storage#2 (0x08100000 - 0x0811FFFF, 128 KiB, read-write, + // execute never) + MPU->RNR = MPU_REGION_NUMBER2; + MPU->RBAR = FLASH_BASE + 0x100000; + MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH | + LL_MPU_REGION_SIZE_128KB | LL_MPU_REGION_FULL_ACCESS | MPU_RASR_XN_Msk; +#endif // Firmware (0x08040000 - 0x080FFFFF, 6 * 128 KiB = 1024 KiB except 2/8 at // start = 768 KiB, read-only) diff --git a/core/embed/trezorhal/stm32f4/translations.c b/core/embed/trezorhal/stm32f4/translations.c new file mode 100644 index 000000000..b09b4b4b9 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/translations.c @@ -0,0 +1,31 @@ +#include "translations.h" +#include +#include +#include "common.h" +#include "flash.h" +#include "model.h" + +void translations_write(uint8_t* data, uint32_t offset, uint32_t len) { + ensure(flash_unlock_write(), "translations_write unlock"); + for (int i = 0; i < len; i++) { + ensure(flash_area_write_byte(&TRANSLATIONS_AREA, offset + i, data[i]), + "translations_write write"); + } + ensure(flash_lock_write(), "translations_write lock"); +} + +const uint8_t* translations_read(uint32_t* len, uint32_t offset) { + // TODO: _Static_assert was not happy with TRANSLATIONS_AREA.num_subareas == 1 + // error: expression in static assertion is not constant + assert(TRANSLATIONS_AREA.num_subareas == 1); + *len = flash_area_get_size(&TRANSLATIONS_AREA) - offset; + return flash_area_get_address(&TRANSLATIONS_AREA, offset, 0); +} + +void translations_erase(void) { + ensure(flash_area_erase(&TRANSLATIONS_AREA, NULL), "translations erase"); +} + +uint32_t translations_area_bytesize(void) { + return flash_area_get_size(&TRANSLATIONS_AREA); +} diff --git a/core/embed/trezorhal/translations.h b/core/embed/trezorhal/translations.h new file mode 100644 index 000000000..bf82d1859 --- /dev/null +++ b/core/embed/trezorhal/translations.h @@ -0,0 +1,11 @@ + +#include +#include "secbool.h" + +void translations_write(uint8_t* data, uint32_t offset, uint32_t len); + +const uint8_t* translations_read(uint32_t* len, uint32_t offset); + +void translations_erase(void); + +uint32_t translations_area_bytesize(void); diff --git a/core/embed/trezorhal/unix/translations.c b/core/embed/trezorhal/unix/translations.c new file mode 120000 index 000000000..21b03004c --- /dev/null +++ b/core/embed/trezorhal/unix/translations.c @@ -0,0 +1 @@ +../stm32f4/translations.c \ No newline at end of file diff --git a/core/embed/unix/mpconfigport.h b/core/embed/unix/mpconfigport.h index af15fe645..2cc4b7d9e 100644 --- a/core/embed/unix/mpconfigport.h +++ b/core/embed/unix/mpconfigport.h @@ -212,6 +212,7 @@ extern const struct _mp_print_t mp_stderr_print; #define MICROPY_PY_TREZORUI (1) #define MICROPY_PY_TREZORUTILS (1) #define MICROPY_PY_TREZORPROTO (1) +#define MICROPY_PY_TREZORTRANSLATE (1) #define MICROPY_PY_TREZORUI2 (1) #define MP_STATE_PORT MP_STATE_VM diff --git a/core/mocks/generated/trezorconfig.pyi b/core/mocks/generated/trezorconfig.pyi index 5b4291549..9f554abbe 100644 --- a/core/mocks/generated/trezorconfig.pyi +++ b/core/mocks/generated/trezorconfig.pyi @@ -108,6 +108,27 @@ def set(app: int, key: int, value: bytes, public: bool = False) -> None: """ +# extmod/modtrezorconfig/modtrezorconfig.c +def translations_set(blob: bytes, offset: int) -> None: + """ + Save translations data at the certain offset. + """ + + +# extmod/modtrezorconfig/modtrezorconfig.c +def translations_wipe() -> None: + """ + Wipe all the translations data before writing a new one + """ + + +# extmod/modtrezorconfig/modtrezorconfig.c +def translations_max_bytesize() -> int: + """ + How much is the maximum bytesize of translations data + """ + + # extmod/modtrezorconfig/modtrezorconfig.c def delete( app: int, key: int, public: bool = False, writable_locked: bool = False diff --git a/core/mocks/generated/trezortranslate.pyi b/core/mocks/generated/trezortranslate.pyi new file mode 100644 index 000000000..a3185d0d2 --- /dev/null +++ b/core/mocks/generated/trezortranslate.pyi @@ -0,0 +1,840 @@ +from typing import * + + +# rust/src/ui/translations/export.rs +def language_name() -> str: + """Get the name of the current language.""" + + +# rust/src/ui/translations/export.rs +class TR: + """Translation object with attributes.""" + addr_mismatch__contact_support: str + addr_mismatch__key_mismatch: str + addr_mismatch__mismatch: str + addr_mismatch__support_url: str + addr_mismatch__title: str + addr_mismatch__title_key_mismatch: str + addr_mismatch__wrong_derication_path: str + addr_mismatch__xpub_mismatch: str + address__address: str + address__public_key: str + address__title_cosigner: str + address__title_receive_address: str + address__title_yours: str + address_details__account: str + address_details__derivation_path: str + address_details__title_receive_address: str + address_details__title_receiving_to: str + authenticate__confirm_template: str + authenticate__header: str + auto_lock__change_template: str + auto_lock__title: str + backup__can_back_up_anytime: str + backup__it_should_be_backed_up: str + backup__it_should_be_backed_up_now: str + backup__new_wallet_created: str + backup__new_wallet_successfully_created: str + backup__recover_anytime: str + backup__title_backup_wallet: str + backup__title_skip: str + backup__want_to_skip: str + binance__buy: str + binance__confirm_cancel: str + binance__confirm_input: str + binance__confirm_order: str + binance__confirm_output: str + binance__order_id: str + binance__pair: str + binance__price: str + binance__quantity: str + binance__sell: str + binance__sender_address: str + binance__side: str + binance__unknown: str + bitcoin__commitment_data: str + bitcoin__confirm_locktime: str + bitcoin__create_proof_of_ownership: str + bitcoin__high_mining_fee_template: str + bitcoin__locktime_no_effect: str + bitcoin__locktime_set_to: str + bitcoin__locktime_set_to_blockheight: str + bitcoin__lot_of_change_outputs: str + bitcoin__multiple_accounts: str + bitcoin__new_fee_rate: str + bitcoin__simple_send_of: str + bitcoin__ticket_amount: str + bitcoin__title_confirm_details: str + bitcoin__title_finalize_transaction: str + bitcoin__title_high_mining_fee: str + bitcoin__title_meld_transaction: str + bitcoin__title_modify_amount: str + bitcoin__title_payjoin: str + bitcoin__title_proof_of_ownership: str + bitcoin__title_purchase_ticket: str + bitcoin__title_update_transaction: str + bitcoin__unknown_path: str + bitcoin__unknown_transaction: str + bitcoin__unusually_high_fee: str + bitcoin__unverified_external_inputs: str + bitcoin__valid_signature: str + bitcoin__voting_rights: str + buttons__abort: str + buttons__access: str + buttons__again: str + buttons__allow: str + buttons__back_up: str + buttons__cancel: str + buttons__change: str + buttons__check: str + buttons__check_again: str + buttons__close: str + buttons__confirm: str + buttons__continue: str + buttons__details: str + buttons__enable: str + buttons__enter: str + buttons__enter_share: str + buttons__export: str + buttons__format: str + buttons__go_back: str + buttons__hold_to_confirm: str + buttons__info: str + buttons__install: str + buttons__more_info: str + buttons__ok_i_understand: str + buttons__purchase: str + buttons__quit: str + buttons__restart: str + buttons__retry: str + buttons__select: str + buttons__set: str + buttons__show_all: str + buttons__show_words: str + buttons__skip: str + buttons__try_again: str + buttons__turn_off: str + buttons__turn_on: str + cardano__addr_base: str + cardano__addr_enterprise: str + cardano__addr_legacy: str + cardano__addr_pointer: str + cardano__addr_reward: str + cardano__address_no_staking: str + cardano__amount: str + cardano__amount_burned_decimals_unknown: str + cardano__amount_minted_decimals_unknown: str + cardano__amount_sent_decimals_unknown: str + cardano__anonymous_pool: str + cardano__asset_fingerprint: str + cardano__auxiliary_data_hash: str + cardano__block: str + cardano__catalyst: str + cardano__certificate: str + cardano__certificate_path: str + cardano__change_output: str + cardano__change_output_path: str + cardano__change_output_staking_path: str + cardano__check_all_items: str + cardano__choose_level_of_details: str + cardano__collateral_input_id: str + cardano__collateral_input_index: str + cardano__collateral_output_contains_tokens: str + cardano__collateral_return: str + cardano__confirm: str + cardano__confirm_signing_stake_pool: str + cardano__confirm_transaction: str + cardano__confirming_a_multisig_transaction: str + cardano__confirming_pool_registration: str + cardano__confirming_transction: str + cardano__cost: str + cardano__credential_mismatch: str + cardano__datum_hash: str + cardano__delegating_to: str + cardano__for_account_and_index_template: str + cardano__for_account_template: str + cardano__for_key_hash: str + cardano__for_script: str + cardano__inline_datum: str + cardano__input_id: str + cardano__input_index: str + cardano__intro_text_address: str + cardano__intro_text_change: str + cardano__intro_text_owned_by_device: str + cardano__intro_text_registration_payment: str + cardano__key_hash: str + cardano__margin: str + cardano__multisig_path: str + cardano__nested_scripts_template: str + cardano__network: str + cardano__no_output_tx: str + cardano__nonce: str + cardano__other: str + cardano__path: str + cardano__pledge: str + cardano__pointer: str + cardano__policy_id: str + cardano__pool_metadata_hash: str + cardano__pool_metadata_url: str + cardano__pool_owner: str + cardano__pool_owner_path: str + cardano__pool_reward_account: str + cardano__reference_input_id: str + cardano__reference_input_index: str + cardano__reference_script: str + cardano__required_signer: str + cardano__reward: str + cardano__reward_address: str + cardano__reward_eligibility_warning: str + cardano__rewards_go_to: str + cardano__script: str + cardano__script_all: str + cardano__script_any: str + cardano__script_data_hash: str + cardano__script_hash: str + cardano__script_invalid_before: str + cardano__script_invalid_hereafter: str + cardano__script_key: str + cardano__script_n_of_k: str + cardano__script_reward: str + cardano__sending: str + cardano__show_simple: str + cardano__sign_tx_path_template: str + cardano__stake_delegation: str + cardano__stake_deregistration: str + cardano__stake_pool_registration: str + cardano__stake_pool_registration_pool_id: str + cardano__stake_registration: str + cardano__staking_key_for_account: str + cardano__to_pool: str + cardano__token_minting_path: str + cardano__total_collateral: str + cardano__transaction: str + cardano__transaction_contains_minting_or_burning: str + cardano__transaction_contains_script_address_no_datum: str + cardano__transaction_fee: str + cardano__transaction_id: str + cardano__transaction_no_collateral_input: str + cardano__transaction_no_script_data_hash: str + cardano__transaction_output_contains_tokens: str + cardano__ttl: str + cardano__unknown: str + cardano__unknown_collateral_amount: str + cardano__unusual_path: str + cardano__valid_since: str + cardano__verify_script: str + cardano__vote_key_registration: str + cardano__vote_public_key: str + cardano__voting_purpose: str + cardano__warning: str + cardano__weight: str + cardano__withdrawal_for_address_template: str + cardano__witness_path: str + cardano__x_of_y_signatures_template: str + coinjoin__access_account: str + coinjoin__do_not_disconnect: str + coinjoin__max_mining_fee: str + coinjoin__max_rounds: str + coinjoin__title: str + coinjoin__title_do_not_disconnect: str + coinjoin__title_progress: str + coinjoin__waiting_for_others: str + confirm_total__account: str + confirm_total__fee_rate: str + confirm_total__sending_from_account: str + confirm_total__title_fee: str + confirm_total__title_sending_from: str + debug__loading_seed: str + debug__loading_seed_not_recommended: str + device_name__change_template: str + device_name__title: str + entropy__send: str + entropy__title: str + entropy__title_confirm: str + eos__about_to_sign_template: str + eos__account: str + eos__action_name: str + eos__amount: str + eos__arbitrary_data: str + eos__buy_ram: str + eos__bytes: str + eos__cancel_vote: str + eos__checksum: str + eos__code: str + eos__contract: str + eos__cpu: str + eos__creator: str + eos__delegate: str + eos__delete_auth: str + eos__from: str + eos__link_auth: str + eos__memo: str + eos__name: str + eos__net: str + eos__new_account: str + eos__no: str + eos__owner: str + eos__parent: str + eos__payer: str + eos__permission: str + eos__proxy: str + eos__receiver: str + eos__refund: str + eos__requirement: str + eos__sell_ram: str + eos__sender: str + eos__sign_transaction: str + eos__threshold: str + eos__to: str + eos__transfer: str + eos__type: str + eos__undelegate: str + eos__unlink_auth: str + eos__update_auth: str + eos__vote_for_producers: str + eos__vote_for_proxy: str + eos__voter: str + eos__yes: str + ethereum__amount_sent: str + ethereum__confirm_fee: str + ethereum__contract: str + ethereum__data_size_template: str + ethereum__gas_limit: str + ethereum__gas_price: str + ethereum__max_gas_price: str + ethereum__name_and_version: str + ethereum__new_contract: str + ethereum__no_message_field: str + ethereum__priority_fee: str + ethereum__show_full_array: str + ethereum__show_full_domain: str + ethereum__show_full_message: str + ethereum__show_full_struct: str + ethereum__sign_eip712: str + ethereum__title_confirm_data: str + ethereum__title_confirm_domain: str + ethereum__title_confirm_message: str + ethereum__title_confirm_struct: str + ethereum__title_confirm_typed_data: str + ethereum__title_signing_address: str + ethereum__units_template: str + ethereum__unknown_token: str + ethereum__valid_signature: str + experimental_mode__enable: str + experimental_mode__only_for_dev: str + experimental_mode__title: str + fido__already_registered: str + fido__device_already_registered: str + fido__device_already_registered_with_template: str + fido__device_not_registered: str + fido__does_not_belong: str + fido__erase_credentials: str + fido__export_credentials: str + fido__not_registered: str + fido__not_registered_with_template: str + fido__please_enable_pin_protection: str + fido__title_authenticate: str + fido__title_import_credential: str + fido__title_list_credentials: str + fido__title_register: str + fido__title_remove_credential: str + fido__title_reset: str + fido__title_u2f_auth: str + fido__title_u2f_register: str + fido__title_verify_user: str + fido__unable_to_verify_user: str + fido__wanna_erase_credentials: str + firmware_update__title: str + firmware_update__title_fingerprint: str + homescreen__click_to_connect: str + homescreen__click_to_unlock: str + homescreen__title_backup_failed: str + homescreen__title_backup_needed: str + homescreen__title_coinjoin_authorized: str + homescreen__title_experimental_mode: str + homescreen__title_hold_to_lock: str + homescreen__title_no_usb_connection: str + homescreen__title_pin_not_set: str + homescreen__title_seedless: str + homescreen__title_set: str + inputs__back: str + inputs__cancel: str + inputs__delete: str + inputs__enter: str + inputs__return: str + inputs__show: str + inputs__space: str + joint__title: str + joint__to_the_total_amount: str + joint__you_are_contributing: str + lockscreen__tap_to_connect: str + lockscreen__tap_to_unlock: str + lockscreen__title_locked: str + lockscreen__title_not_connected: str + misc__decrypt_value: str + misc__encrypt_value: str + misc__title_suite_labeling: str + modify_amount__address: str + modify_amount__decrease_amount: str + modify_amount__increase_amount: str + modify_amount__new_amount: str + modify_amount__title: str + modify_fee__decrease_fee: str + modify_fee__fee_rate: str + modify_fee__increase_fee: str + modify_fee__new_transaction_fee: str + modify_fee__no_change: str + modify_fee__title: str + modify_fee__transaction_fee: str + monero__confirm_export: str + monero__confirm_fee: str + monero__confirm_ki_sync: str + monero__confirm_refresh: str + monero__confirm_unlock_time: str + monero__hashing_inputs: str + monero__payment_id: str + monero__postprocessing: str + monero__processing: str + monero__processing_inputs: str + monero__processing_outputs: str + monero__signing: str + monero__signing_inputs: str + monero__unlock_time_set_template: str + monero__wanna_export_tx_der: str + monero__wanna_export_tx_key: str + monero__wanna_export_watchkey: str + monero__wanna_start_refresh: str + monero__wanna_sync_key_images: str + nem__absolute: str + nem__activate: str + nem__add: str + nem__confirm_action: str + nem__confirm_address: str + nem__confirm_creation_fee: str + nem__confirm_fee: str + nem__confirm_mosaic: str + nem__confirm_multisig_fee: str + nem__confirm_namespace: str + nem__confirm_payload: str + nem__confirm_properties: str + nem__confirm_rental_fee: str + nem__confirm_transfer_of: str + nem__convert_account_to_multisig: str + nem__cosign_transaction_for: str + nem__cosignatory: str + nem__create_mosaic: str + nem__create_namespace: str + nem__deactivate: str + nem__decrease: str + nem__description: str + nem__divisibility_and_levy_cannot_be_shown: str + nem__encrypted: str + nem__final_confirm: str + nem__immutable: str + nem__increase: str + nem__initial_supply: str + nem__initiate_transaction_for: str + nem__levy_divisibility: str + nem__levy_fee: str + nem__levy_fee_of: str + nem__levy_mosaic: str + nem__levy_namespace: str + nem__levy_recipient: str + nem__levy_type: str + nem__modify_supply_for: str + nem__modify_the_number_of_cosignatories_by: str + nem__mutable: str + nem__no: str + nem__of: str + nem__percentile: str + nem__raw_units_template: str + nem__remote_harvesting: str + nem__remove: str + nem__set_minimum_cosignatories_to: str + nem__sign_tx_fee_template: str + nem__supply_change: str + nem__supply_units_template: str + nem__transferable: str + nem__under_namespace: str + nem__unencrypted: str + nem__unknown_mosaic: str + nem__yes: str + passphrase__access_hidden_wallet: str + passphrase__always_on_device: str + passphrase__from_host_not_shown: str + passphrase__hidden_wallet: str + passphrase__hide: str + passphrase__next_screen_will_show_passphrase: str + passphrase__please_enter: str + passphrase__revoke_on_device: str + passphrase__title_confirm: str + passphrase__title_enter: str + passphrase__title_hide: str + passphrase__title_settings: str + passphrase__title_source: str + passphrase__turn_off: str + passphrase__turn_on: str + pin__change: str + pin__changed: str + pin__cursor_will_change: str + pin__diff_from_wipe_code: str + pin__disabled: str + pin__enabled: str + pin__enter: str + pin__enter_new: str + pin__entered_not_valid: str + pin__info: str + pin__invalid_pin: str + pin__last_attempt: str + pin__mismatch: str + pin__pin_mismatch: str + pin__please_check_again: str + pin__reenter_new: str + pin__reenter_to_confirm: str + pin__should_be_long: str + pin__title_check_pin: str + pin__title_settings: str + pin__title_wrong_pin: str + pin__tries_left: str + pin__turn_off: str + pin__turn_on: str + pin__wrong_pin: str + plurals__contains_x_keys: str + plurals__lock_after_x_hours: str + plurals__lock_after_x_milliseconds: str + plurals__lock_after_x_minutes: str + plurals__lock_after_x_seconds: str + plurals__sign_x_actions: str + plurals__transaction_of_x_operations: str + plurals__x_groups_needed: str + plurals__x_shares_needed: str + progress__authenticity_check: str + progress__done: str + progress__loading_transaction: str + progress__one_second_left: str + progress__please_wait: str + progress__processing: str + progress__refreshing: str + progress__signing_transaction: str + progress__syncing: str + progress__x_seconds_left_template: str + reboot_to_bootloader__restart: str + reboot_to_bootloader__title: str + reboot_to_bootloader__version_by_template: str + recovery__cancel_dry_run: str + recovery__check_dry_run: str + recovery__cursor_will_change: str + recovery__dry_run_bip39_valid_match: str + recovery__dry_run_bip39_valid_mismatch: str + recovery__dry_run_slip39_valid_match: str + recovery__dry_run_slip39_valid_mismatch: str + recovery__enter_any_share: str + recovery__enter_backup: str + recovery__enter_different_share: str + recovery__enter_share_from_diff_group: str + recovery__group_num_template: str + recovery__group_threshold_reached: str + recovery__invalid_seed_entered: str + recovery__invalid_share_entered: str + recovery__more_shares_needed: str + recovery__num_of_words: str + recovery__only_first_n_letters: str + recovery__progress_will_be_lost: str + recovery__select_num_of_words: str + recovery__share_already_entered: str + recovery__share_from_another_shamir: str + recovery__share_num_template: str + recovery__title: str + recovery__title_cancel_dry_run: str + recovery__title_cancel_recovery: str + recovery__title_dry_run: str + recovery__title_recover: str + recovery__title_remaining_shares: str + recovery__type_word_x_of_y_template: str + recovery__wallet_recovered: str + recovery__wanna_cancel_dry_run: str + recovery__wanna_cancel_recovery: str + recovery__word_count_template: str + recovery__word_x_of_y_template: str + recovery__x_more_items_starting_template_plural: str + recovery__x_more_shares_needed_template_plural: str + recovery__x_of_y_entered_template: str + recovery__you_have_entered: str + reset__advanced_group_threshold_info: str + reset__all_x_of_y_template: str + reset__any_x_of_y_template: str + reset__button_create: str + reset__button_recover: str + reset__by_continuing: str + reset__check_backup_title: str + reset__check_group_share_title_template: str + reset__check_seed_title: str + reset__check_share_title_template: str + reset__continue_with_next_share: str + reset__continue_with_share_template: str + reset__finished_verifying_group_template: str + reset__finished_verifying_seed: str + reset__finished_verifying_shares: str + reset__group_description: str + reset__group_info: str + reset__group_share_checked_successfully_template: str + reset__group_share_title_template: str + reset__more_info_at: str + reset__need_all_share_template: str + reset__need_any_share_template: str + reset__needed_to_form_a_group: str + reset__needed_to_recover_your_wallet: str + reset__never_make_digital_copy: str + reset__num_of_share_holders_template: str + reset__num_of_shares_advanced_info_template: str + reset__num_of_shares_basic_info: str + reset__num_shares_for_group_template: str + reset__number_of_shares_info: str + reset__one_share: str + reset__only_one_share_will_be_created: str + reset__recovery_seed_title: str + reset__recovery_share_title_template: str + reset__required_number_of_groups: str + reset__select_correct_word: str + reset__select_word_template: str + reset__select_word_x_of_y_template: str + reset__set_it_to_count_template: str + reset__share_checked_successfully_template: str + reset__share_words_title: str + reset__slip39_checklist_num_groups: str + reset__slip39_checklist_num_shares: str + reset__slip39_checklist_set_num_groups: str + reset__slip39_checklist_set_num_shares: str + reset__slip39_checklist_set_sizes: str + reset__slip39_checklist_set_sizes_longer: str + reset__slip39_checklist_set_threshold: str + reset__slip39_checklist_title: str + reset__slip39_checklist_write_down: str + reset__slip39_checklist_write_down_recovery: str + reset__the_threshold_sets_the_number_of_shares: str + reset__threshold_info: str + reset__title_backup_is_done: str + reset__title_create_wallet: str + reset__title_create_wallet_shamir: str + reset__title_group_threshold: str + reset__title_number_of_groups: str + reset__title_number_of_shares: str + reset__title_set_group_threshold: str + reset__title_set_number_of_groups: str + reset__title_set_number_of_shares: str + reset__title_set_threshold: str + reset__to_form_group_template: str + reset__tos_link: str + reset__total_number_of_shares_in_group_template: str + reset__use_your_backup: str + reset__write_down_words_template: str + reset__wrong_word_selected: str + reset__you_need_one_share: str + reset__your_backup_is_done: str + ripple__confirm_tag: str + ripple__destination_tag_template: str + rotation__change_template: str + rotation__east: str + rotation__north: str + rotation__south: str + rotation__title_change: str + rotation__west: str + safety_checks__approve_unsafe_always: str + safety_checks__approve_unsafe_temporary: str + safety_checks__enforce_strict: str + safety_checks__title: str + safety_checks__title_safety_override: str + sd_card__all_data_will_be_lost: str + sd_card__card_required: str + sd_card__disable: str + sd_card__disabled: str + sd_card__enable: str + sd_card__enabled: str + sd_card__error: str + sd_card__format_card: str + sd_card__insert_correct_card: str + sd_card__please_insert: str + sd_card__please_unplug_and_insert: str + sd_card__problem_accessing: str + sd_card__refresh: str + sd_card__refreshed: str + sd_card__restart: str + sd_card__title: str + sd_card__title_problem: str + sd_card__unknown_filesystem: str + sd_card__unplug_and_insert_correct: str + sd_card__use_different_card: str + sd_card__wanna_format: str + sd_card__wrong_sd_card: str + send__address_path: str + send__amount: str + send__confirm_sending: str + send__from_multiple_accounts: str + send__including_fee: str + send__maximum_fee: str + send__receiving_to_multisig: str + send__title_amount: str + send__title_confirm_sending: str + send__title_joint_transaction: str + send__title_receiving_to: str + send__title_recipient: str + send__title_sending: str + send__title_sending_amount: str + send__title_sending_to: str + send__to_the_total_amount: str + send__total_amount: str + send__transaction_id: str + send__you_are_contributing: str + share_words__words_in_order: str + share_words__wrote_down_all: str + sign_message__bytes_template: str + sign_message__confirm_address: str + sign_message__confirm_message: str + sign_message__message_size: str + sign_message__verify_address: str + stellar__account: str + stellar__account_merge: str + stellar__account_thresholds: str + stellar__add_signer: str + stellar__add_trust: str + stellar__all_will_be_sent_to: str + stellar__allow_trust: str + stellar__asset: str + stellar__bump_sequence: str + stellar__buying: str + stellar__clear_data: str + stellar__clear_flags: str + stellar__confirm_issuer: str + stellar__confirm_memo: str + stellar__confirm_network: str + stellar__confirm_operation: str + stellar__confirm_stellar: str + stellar__confirm_timebounds: str + stellar__create_account: str + stellar__debited_amount: str + stellar__delete: str + stellar__delete_passive_offer: str + stellar__delete_trust: str + stellar__destination: str + stellar__exchanges_require_memo: str + stellar__final_confirm: str + stellar__hash: str + stellar__high: str + stellar__home_domain: str + stellar__inflation: str + stellar__initial_balance: str + stellar__initialize_signing_with: str + stellar__issuer_template: str + stellar__key: str + stellar__limit: str + stellar__low: str + stellar__master_weight: str + stellar__medium: str + stellar__new_offer: str + stellar__new_passive_offer: str + stellar__no_memo_set: str + stellar__no_restriction: str + stellar__on_network_template: str + stellar__path_pay: str + stellar__path_pay_at_least: str + stellar__pay: str + stellar__pay_at_most: str + stellar__preauth_transaction: str + stellar__price_per_template: str + stellar__private_network: str + stellar__remove_signer: str + stellar__revoke_trust: str + stellar__selling: str + stellar__set_data: str + stellar__set_flags: str + stellar__set_sequence_to_template: str + stellar__sign_tx_count_template: str + stellar__sign_tx_fee_template: str + stellar__source_account: str + stellar__testnet_network: str + stellar__trusted_account: str + stellar__update: str + stellar__valid_from: str + stellar__valid_to: str + stellar__value_sha256: str + stellar__wanna_clean_value_key_template: str + stellar__your_account: str + tezos__address: str + tezos__amount: str + tezos__baker_address: str + tezos__balance: str + tezos__ballot: str + tezos__confirm_delegation: str + tezos__confirm_origination: str + tezos__delegator: str + tezos__fee: str + tezos__proposal: str + tezos__register_delegate: str + tezos__remove_delegation: str + tezos__submit_ballot: str + tezos__submit_proposal: str + tezos__submit_proposals: str + tutorial__middle_click: str + tutorial__press_and_hold: str + tutorial__ready_to_use: str + tutorial__scroll_down: str + tutorial__sure_you_want_skip: str + tutorial__title_hello: str + tutorial__title_screen_scroll: str + tutorial__title_skip: str + tutorial__title_tutorial_complete: str + tutorial__use_trezor: str + tutorial__welcome_press_right: str + u2f__get: str + u2f__set_template: str + u2f__title_get: str + u2f__title_set: str + wipe__info: str + wipe__title: str + wipe__want_to_wipe: str + wipe_code__change: str + wipe_code__changed: str + wipe_code__diff_from_pin: str + wipe_code__disabled: str + wipe_code__enabled: str + wipe_code__enter_new: str + wipe_code__info: str + wipe_code__invalid: str + wipe_code__mismatch: str + wipe_code__reenter: str + wipe_code__reenter_to_confirm: str + wipe_code__title_check: str + wipe_code__title_invalid: str + wipe_code__title_settings: str + wipe_code__turn_off: str + wipe_code__turn_on: str + wipe_code__wipe_code_mismatch: str + word_count__title: str + words__are_you_sure: str + words__array_of: str + words__buying: str + words__confirm: str + words__contains: str + words__continue_anyway: str + words__continue_with: str + words__error: str + words__from: str + words__keep_it_safe: str + words__know_what_your_doing: str + words__my_trezor: str + words__outputs: str + words__please_check_again: str + words__please_try_again: str + words__really_wanna: str + words__sign: str + words__title_check: str + words__title_group: str + words__title_information: str + words__title_remember: str + words__title_share: str + words__title_shares: str + words__title_success: str + words__title_summary: str + words__title_threshold: str + words__unknown: str + words__warning: str diff --git a/core/site_scons/boards/stm32f4_common.py b/core/site_scons/boards/stm32f4_common.py index b47e45c8f..e6a435636 100644 --- a/core/site_scons/boards/stm32f4_common.py +++ b/core/site_scons/boards/stm32f4_common.py @@ -47,6 +47,7 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/trezorhal/stm32f4/random_delays.c", "embed/trezorhal/stm32f4/rng.c", "embed/trezorhal/stm32f4/vectortable.s", + "embed/trezorhal/stm32f4/translations.c", ] # boardloader needs separate assembler for some function unencumbered by various FW+bootloader hacks diff --git a/core/src/all_modules.py b/core/src/all_modules.py index aba9d6a11..38ba3bde9 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -147,6 +147,8 @@ trezor.sdcard import trezor.sdcard trezor.strings import trezor.strings +trezor.translations +import trezor.translations trezor.ui import trezor.ui trezor.ui.layouts @@ -321,6 +323,8 @@ apps.management.backup_device import apps.management.backup_device apps.management.backup_types import apps.management.backup_types +apps.management.change_language +import apps.management.change_language apps.management.change_pin import apps.management.change_pin apps.management.change_wipe_code diff --git a/core/src/apps/base.py b/core/src/apps/base.py index 3a84c95cd..98f0c7ba6 100644 --- a/core/src/apps/base.py +++ b/core/src/apps/base.py @@ -5,6 +5,8 @@ import storage.device as storage_device from trezor import config, utils, wire, workflow from trezor.enums import HomescreenFormat, MessageType from trezor.messages import Success, UnlockPath +from trezor.ui.layouts import confirm_action +from trezortranslate import TR from . import workflow_handlers @@ -46,6 +48,7 @@ def get_features() -> Features: import storage.recovery as storage_recovery from trezor.enums import Capability from trezor.messages import Features + from trezor.translations import get_language from trezor.ui import HEIGHT, WIDTH from apps.common import mnemonic, safety_checks @@ -53,7 +56,7 @@ def get_features() -> Features: f = Features( vendor="trezor.io", fw_vendor=utils.firmware_vendor(), - language="en-US", + language=get_language(), major_version=utils.VERSION_MAJOR, minor_version=utils.VERSION_MINOR, patch_version=utils.VERSION_PATCH, @@ -221,7 +224,7 @@ async def handle_Ping(msg: Ping) -> Success: from trezor.enums import ButtonRequestType as B from trezor.ui.layouts import confirm_action - await confirm_action("ping", "Confirm", "ping", br_code=B.ProtectCall) + await confirm_action("ping", TR.words__confirm, "ping", br_code=B.ProtectCall) return Success(message=msg.message) @@ -252,7 +255,6 @@ async def handle_DoPreauthorized(msg: DoPreauthorized) -> protobuf.MessageType: async def handle_UnlockPath(msg: UnlockPath) -> protobuf.MessageType: from trezor.crypto import hmac from trezor.messages import UnlockedPathRequest - from trezor.ui.layouts import confirm_action from trezor.wire.context import call_any, get_context from apps.common.paths import SLIP25_PURPOSE @@ -285,8 +287,8 @@ async def handle_UnlockPath(msg: UnlockPath) -> protobuf.MessageType: await confirm_action( "confirm_coinjoin_access", title="Coinjoin", - description="Access your coinjoin account?", - verb="ACCESS", + description=TR.coinjoin__access_account, + verb=TR.buttons__access, ) wire_types = (MessageType.GetAddress, MessageType.GetPublicKey, MessageType.SignTx) diff --git a/core/src/apps/binance/layout.py b/core/src/apps/binance/layout.py index 115f59eeb..706893981 100644 --- a/core/src/apps/binance/layout.py +++ b/core/src/apps/binance/layout.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Sequence from trezor.enums import ButtonRequestType from trezor.strings import format_amount from trezor.ui.layouts import confirm_properties +from trezortranslate import TR from .helpers import DECIMALS @@ -29,10 +30,10 @@ async def require_confirm_transfer(msg: BinanceTransferMsg) -> None: ) for txinput in msg.inputs: - make_input_output_pages(txinput, "Confirm input") + make_input_output_pages(txinput, TR.binance__confirm_input) for txoutput in msg.outputs: - make_input_output_pages(txoutput, "Confirm output") + make_input_output_pages(txoutput, TR.binance__confirm_output) await _confirm_transfer(items, chunkify=bool(msg.chunkify)) @@ -51,11 +52,11 @@ async def _confirm_transfer( async def require_confirm_cancel(msg: BinanceCancelMsg) -> None: await confirm_properties( "confirm_cancel", - "Confirm cancel", + TR.binance__confirm_cancel, ( - ("Sender address:", str(msg.sender)), - ("Pair:", str(msg.symbol)), - ("Order ID:", str(msg.refid)), + (TR.binance__sender_address, str(msg.sender)), + (TR.binance__pair, str(msg.symbol)), + (TR.binance__order_id, str(msg.refid)), ), hold=True, br_code=ButtonRequestType.SignTx, @@ -66,21 +67,21 @@ async def require_confirm_order(msg: BinanceOrderMsg) -> None: from trezor.enums import BinanceOrderSide if msg.side == BinanceOrderSide.BUY: - side = "Buy" + side = TR.binance__buy elif msg.side == BinanceOrderSide.SELL: - side = "Sell" + side = TR.binance__sell else: - side = "Unknown" + side = TR.binance__unknown await confirm_properties( "confirm_order", - "Confirm order", + TR.binance__confirm_order, ( - ("Sender address:", str(msg.sender)), - ("Pair:", str(msg.symbol)), - ("Side:", side), - ("Quantity:", format_amount(msg.quantity, DECIMALS)), - ("Price:", format_amount(msg.price, DECIMALS)), + (TR.binance__sender_address, str(msg.sender)), + (TR.binance__pair, str(msg.symbol)), + (TR.binance__side, side), + (TR.binance__quantity, format_amount(msg.quantity, DECIMALS)), + (TR.binance__price, format_amount(msg.price, DECIMALS)), ), hold=True, br_code=ButtonRequestType.SignTx, diff --git a/core/src/apps/bitcoin/authorize_coinjoin.py b/core/src/apps/bitcoin/authorize_coinjoin.py index 2833d2bf6..8614ff3e7 100644 --- a/core/src/apps/bitcoin/authorize_coinjoin.py +++ b/core/src/apps/bitcoin/authorize_coinjoin.py @@ -23,6 +23,7 @@ async def authorize_coinjoin( from trezor.messages import Success from trezor.ui.layouts import confirm_coinjoin, confirm_metadata from trezor.wire import DataError + from trezortranslate import TR from apps.common import authorization, safety_checks from apps.common.keychain import FORBIDDEN_KEY_PATH @@ -79,8 +80,8 @@ async def authorize_coinjoin( if msg.max_fee_per_kvbyte > coin.maxfee_kb: await confirm_metadata( "fee_over_threshold", - "High mining fee", - "The mining fee of\n{}\nis unexpectedly high.", + TR.bitcoin__title_high_mining_fee, + TR.bitcoin__high_mining_fee_template, max_fee_per_vbyte, ButtonRequestType.FeeOverThreshold, ) diff --git a/core/src/apps/bitcoin/get_address.py b/core/src/apps/bitcoin/get_address.py index caeb990ed..a394d4260 100644 --- a/core/src/apps/bitcoin/get_address.py +++ b/core/src/apps/bitcoin/get_address.py @@ -34,6 +34,7 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad from trezor.enums import InputScriptType from trezor.messages import Address from trezor.ui.layouts import show_address, show_warning + from trezortranslate import TR from apps.common.address_mac import get_address_mac from apps.common.paths import address_n_to_str, validate_path @@ -105,8 +106,8 @@ async def get_address(msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Ad await show_warning( "warning_multisig", - "Receiving to a multisig address.", - "Continue anyway?", + TR.send__receiving_to_multisig, + TR.words__continue_anyway, ) await show_address( diff --git a/core/src/apps/bitcoin/get_ownership_proof.py b/core/src/apps/bitcoin/get_ownership_proof.py index 187d5a4f1..a96067f6e 100644 --- a/core/src/apps/bitcoin/get_ownership_proof.py +++ b/core/src/apps/bitcoin/get_ownership_proof.py @@ -22,6 +22,7 @@ async def get_ownership_proof( from trezor.messages import OwnershipProof from trezor.ui.layouts import confirm_action, confirm_blob from trezor.wire import DataError, ProcessError + from trezortranslate import TR from apps.common.paths import validate_path @@ -71,15 +72,15 @@ async def get_ownership_proof( if msg.user_confirmation and not authorization: await confirm_action( "confirm_ownership_proof", - "Proof of ownership", - description="Do you want to create a proof of ownership?", + TR.bitcoin__title_proof_of_ownership, + description=TR.bitcoin__create_proof_of_ownership, ) if msg.commitment_data: await confirm_blob( "confirm_ownership_proof", - "Proof of ownership", + TR.bitcoin__title_proof_of_ownership, msg.commitment_data, - "Commitment data:", + TR.bitcoin__commitment_data, ) ownership_proof, signature = generate_proof( diff --git a/core/src/apps/bitcoin/get_public_key.py b/core/src/apps/bitcoin/get_public_key.py index 83c08c34a..3f5236815 100644 --- a/core/src/apps/bitcoin/get_public_key.py +++ b/core/src/apps/bitcoin/get_public_key.py @@ -11,6 +11,7 @@ async def get_public_key( from trezor import wire from trezor.enums import InputScriptType from trezor.messages import HDNodeType, PublicKey, UnlockPath + from trezortranslate import TR from apps.common import coininfo, paths from apps.common.keychain import FORBIDDEN_KEY_PATH, get_keychain @@ -101,7 +102,7 @@ async def get_public_key( "XPUB", account=account, path=path, - mismatch_title="XPUB mismatch?", + mismatch_title=TR.addr_mismatch__xpub_mismatch, br_type="show_xpub", ) diff --git a/core/src/apps/bitcoin/keychain.py b/core/src/apps/bitcoin/keychain.py index 653f4dbe2..832564862 100644 --- a/core/src/apps/bitcoin/keychain.py +++ b/core/src/apps/bitcoin/keychain.py @@ -464,9 +464,11 @@ def address_n_to_name_or_unknown( account_level: bool = False, show_account_str: bool = False, ) -> str: + from trezortranslate import TR + account_name = address_n_to_name(coin, address_n, script_type) if account_name is None: - return "Unknown path" + return TR.bitcoin__unknown_path elif account_name == "": return coin.coin_shortcut else: diff --git a/core/src/apps/bitcoin/sign_tx/approvers.py b/core/src/apps/bitcoin/sign_tx/approvers.py index 27ac1cebc..703433034 100644 --- a/core/src/apps/bitcoin/sign_tx/approvers.py +++ b/core/src/apps/bitcoin/sign_tx/approvers.py @@ -257,16 +257,18 @@ class BasicApprover(Approver): def _replacement_title( self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo] ) -> str: + from trezortranslate import TR + if self.is_payjoin(): - return "PayJoin" + return TR.bitcoin__title_payjoin elif tx_info.rbf_disabled() and any( not orig.rbf_disabled() for orig in orig_txs ): - return "Finalize transaction" + return TR.bitcoin__title_finalize_transaction elif len(orig_txs) > 1: - return "Meld transactions" + return TR.bitcoin__title_meld_transaction else: - return "Update transaction" + return TR.bitcoin__title_update_transaction async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None: from trezor.wire import NotEnoughFunds diff --git a/core/src/apps/bitcoin/sign_tx/layout.py b/core/src/apps/bitcoin/sign_tx/layout.py index 4323cc410..84dde034f 100644 --- a/core/src/apps/bitcoin/sign_tx/layout.py +++ b/core/src/apps/bitcoin/sign_tx/layout.py @@ -5,6 +5,7 @@ from trezor.enums import ButtonRequestType from trezor.strings import format_amount from trezor.ui import layouts from trezor.ui.layouts import confirm_metadata +from trezortranslate import TR from apps.common.paths import address_n_to_str @@ -50,7 +51,7 @@ def format_coin_amount(amount: int, coin: CoinInfo, amount_unit: AmountUnit) -> def account_label(coin: CoinInfo, address_n: Bip32Path | None) -> str: return ( - "Multiple accounts" + TR.bitcoin__multiple_accounts if address_n is None else address_n_to_name(coin, list(address_n) + [0] * BIP32_WALLET_DEPTH) or f"Path {address_n_to_str(address_n)}" @@ -77,7 +78,7 @@ async def confirm_output( "omni_transaction", "OMNI transaction", omni.parse(data), - verb="Confirm", + verb=TR.buttons__confirm, br_code=ButtonRequestType.ConfirmOutput, ) else: @@ -92,7 +93,7 @@ async def confirm_output( assert output.address is not None address_short = addresses.address_short(coin, output.address) if output.payment_req_index is not None: - title = "Confirm details" + title = TR.bitcoin__title_confirm_details else: title = None @@ -110,7 +111,7 @@ async def confirm_output( script_type, show_account_str=show_account_str, ) - or f"address path {address_n_to_str(output.address_n)}" + or f"{TR.send__address_path} {address_n_to_str(output.address_n)}" ) layout = layouts.confirm_output( @@ -133,21 +134,21 @@ async def confirm_decred_sstx_submission( amount = format_coin_amount(output.amount, coin, amount_unit) await layouts.confirm_value( - "Purchase ticket", + TR.bitcoin__title_purchase_ticket, amount, - "Ticket amount:", + TR.bitcoin__ticket_amount, "confirm_decred_sstx_submission", ButtonRequestType.ConfirmOutput, - verb="CONFIRM", + verb=TR.buttons__confirm, ) await layouts.confirm_value( - "Purchase ticket", + TR.bitcoin__title_purchase_ticket, address_short, - "Voting rights to:", + TR.bitcoin__voting_rights, "confirm_decred_sstx_submission", ButtonRequestType.ConfirmOutput, - verb="PURCHASE", + verb=TR.buttons__purchase, ) @@ -165,7 +166,7 @@ async def confirm_payment_request( elif m.refund_memo is not None: pass elif m.coin_purchase_memo is not None: - memo_texts.append(f"Buying {m.coin_purchase_memo.amount}.") + memo_texts.append(f"{TR.words__buying} {m.coin_purchase_memo.amount}.") else: raise wire.DataError("Unrecognized memo type in payment request memo.") @@ -256,7 +257,7 @@ async def confirm_feeoverthreshold( fee_amount = format_coin_amount(fee, coin, amount_unit) await layouts.show_warning( "fee_over_threshold", - "Unusually high fee.", + TR.bitcoin__unusually_high_fee, fee_amount, br_code=ButtonRequestType.FeeOverThreshold, ) @@ -265,8 +266,8 @@ async def confirm_feeoverthreshold( async def confirm_change_count_over_threshold(change_count: int) -> None: await layouts.show_warning( "change_count_over_threshold", - "A lot of change-outputs.", - f"{str(change_count)} outputs", + TR.bitcoin__lot_of_change_outputs, + f"{str(change_count)} {TR.words__outputs}", br_code=ButtonRequestType.SignTx, ) @@ -274,9 +275,9 @@ async def confirm_change_count_over_threshold(change_count: int) -> None: async def confirm_unverified_external_input() -> None: await layouts.show_warning( "unverified_external_input", - "The transaction contains unverified external inputs.", - "Continue anyway?", - button="Continue", + TR.bitcoin__unverified_external_inputs, + TR.words__continue_anyway, + button=TR.buttons__continue, br_code=ButtonRequestType.SignTx, ) @@ -284,9 +285,9 @@ async def confirm_unverified_external_input() -> None: async def confirm_multiple_accounts() -> None: await layouts.show_warning( "sending_from_multiple_accounts", - "Sending from multiple accounts.", - "Continue anyway?", - button="Continue", + TR.send__from_multiple_accounts, + TR.words__continue_anyway, + button=TR.buttons__continue, br_code=ButtonRequestType.SignTx, ) @@ -297,23 +298,23 @@ async def confirm_nondefault_locktime(lock_time: int, lock_time_disabled: bool) if lock_time_disabled: await layouts.show_warning( "nondefault_locktime", - "Locktime is set but will have no effect.", - "Continue anyway?", - button="Continue", + TR.bitcoin__locktime_no_effect, + TR.words__continue_anyway, + button=TR.buttons__continue, br_code=ButtonRequestType.SignTx, ) else: if lock_time < _LOCKTIME_TIMESTAMP_MIN_VALUE: - text = "Locktime set to blockheight:" + text = TR.bitcoin__locktime_set_to_blockheight value = str(lock_time) else: - text = "Locktime set to:" + text = TR.bitcoin__locktime_set_to value = format_timestamp(lock_time) await layouts.confirm_value( - "Confirm locktime", + TR.bitcoin__confirm_locktime, value, text, "nondefault_locktime", br_code=ButtonRequestType.SignTx, - verb="Confirm", + verb=TR.buttons__confirm, ) diff --git a/core/src/apps/bitcoin/sign_tx/omni.py b/core/src/apps/bitcoin/sign_tx/omni.py index ef5dfcd12..3a19dd552 100644 --- a/core/src/apps/bitcoin/sign_tx/omni.py +++ b/core/src/apps/bitcoin/sign_tx/omni.py @@ -18,6 +18,7 @@ def parse(data: bytes) -> str: from ustruct import unpack from trezor.strings import format_amount + from trezortranslate import TR if not is_valid(data): raise ValueError # tried to parse data that fails validation @@ -25,7 +26,9 @@ def parse(data: bytes) -> str: if tx_version == 0 and tx_type == 0 and len(data) == 20: # OMNI simple send currency, amount = unpack(">IQ", data[8:20]) suffix, decimals = currencies.get(currency, ("UNKN", 0)) - return f"Simple send of {format_amount(amount, decimals)} {suffix}" + return ( + f"{TR.bitcoin__simple_send_of} {format_amount(amount, decimals)} {suffix}" + ) else: # unknown OMNI transaction - return "Unknown transaction" + return TR.bitcoin__unknown_transaction diff --git a/core/src/apps/bitcoin/sign_tx/progress.py b/core/src/apps/bitcoin/sign_tx/progress.py index af8dc3683..0994dbabb 100644 --- a/core/src/apps/bitcoin/sign_tx/progress.py +++ b/core/src/apps/bitcoin/sign_tx/progress.py @@ -114,10 +114,15 @@ class Progress: def report_init(self) -> None: from trezor import workflow from trezor.ui.layouts.progress import bitcoin_progress, coinjoin_progress + from trezortranslate import TR progress_layout = coinjoin_progress if self.is_coinjoin else bitcoin_progress workflow.close_others() - text = "Signing transaction..." if self.signing else "Loading transaction..." + text = ( + TR.progress__signing_transaction + if self.signing + else TR.progress__loading_transaction + ) self.progress_layout = progress_layout(text) def report(self) -> None: diff --git a/core/src/apps/bitcoin/verify_message.py b/core/src/apps/bitcoin/verify_message.py index c90e608bc..bbd7c64dc 100644 --- a/core/src/apps/bitcoin/verify_message.py +++ b/core/src/apps/bitcoin/verify_message.py @@ -57,6 +57,7 @@ async def verify_message(msg: VerifyMessage) -> Success: from trezor.messages import Success from trezor.ui.layouts import confirm_signverify, show_success from trezor.wire import ProcessError + from trezortranslate import TR from apps.common import coins from apps.common.signverify import decode_message, message_digest @@ -116,5 +117,5 @@ async def verify_message(msg: VerifyMessage) -> Success: verify=True, ) - await show_success("verify_message", "The signature is valid.") + await show_success("verify_message", TR.bitcoin__valid_signature) return Success(message="Message verified") diff --git a/core/src/apps/cardano/get_public_key.py b/core/src/apps/cardano/get_public_key.py index 5d637fe78..120c0e936 100644 --- a/core/src/apps/cardano/get_public_key.py +++ b/core/src/apps/cardano/get_public_key.py @@ -1,6 +1,8 @@ from typing import TYPE_CHECKING from ubinascii import hexlify +from trezortranslate import TR + from . import seed if TYPE_CHECKING: @@ -38,7 +40,7 @@ async def get_public_key( from apps.common.paths import address_n_to_str path = address_n_to_str(address_n) - await show_pubkey(key.xpub, "Public key", path=path) + await show_pubkey(key.xpub, TR.address__public_key, path=path) return key diff --git a/core/src/apps/cardano/helpers/credential.py b/core/src/apps/cardano/helpers/credential.py index 540498b88..b53e3914e 100644 --- a/core/src/apps/cardano/helpers/credential.py +++ b/core/src/apps/cardano/helpers/credential.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from trezor.enums import CardanoAddressType +from trezortranslate import TR from .paths import SCHEMA_PAYMENT @@ -163,13 +164,13 @@ class Credential: def get_title(self) -> str: if self.path: - return "path" + return TR.cardano__path elif self.key_hash: - return "key hash" + return TR.cardano__key_hash elif self.script_hash: - return "script" + return TR.cardano__script elif self.pointer: - return "pointer" + return TR.cardano__pointer else: return "" @@ -192,9 +193,9 @@ class Credential: return [(None, bech32.encode(bech32.HRP_SCRIPT_HASH, self.script_hash))] elif pointer: return [ - (f"Block: {pointer.block_index}", None), - (f"Transaction: {pointer.tx_index}", None), - (f"Certificate: {pointer.certificate_index}", None), + (f"{TR.cardano__block}: {pointer.block_index}", None), + (f"{TR.cardano__transaction}: {pointer.tx_index}", None), + (f"{TR.cardano__certificate}: {pointer.certificate_index}", None), ] else: return [] diff --git a/core/src/apps/cardano/helpers/paths.py b/core/src/apps/cardano/helpers/paths.py index cfcf300d3..37c965832 100644 --- a/core/src/apps/cardano/helpers/paths.py +++ b/core/src/apps/cardano/helpers/paths.py @@ -31,3 +31,12 @@ CHANGE_OUTPUT_STAKING_PATH_NAME = "Change output staking path" CERTIFICATE_PATH_NAME = "Certificate path" POOL_OWNER_STAKING_PATH_NAME = "Pool owner staking path" WITNESS_PATH_NAME = "Witness path" + +# TODO: these appear also in DataErrors being thrown, +# so device tests would fail with translations + +# CHANGE_OUTPUT_PATH_NAME = TR.cardano__change_output_path +# CHANGE_OUTPUT_STAKING_PATH_NAME = TR.cardano__change_output_staking_path +# CERTIFICATE_PATH_NAME = TR.cardano__certificate_path +# POOL_OWNER_STAKING_PATH_NAME = TR.cardano__pool_owner_path +# WITNESS_PATH_NAME = TR.cardano__witness_path diff --git a/core/src/apps/cardano/helpers/protocol_magics.py b/core/src/apps/cardano/helpers/protocol_magics.py index 513c73b98..109f7183d 100644 --- a/core/src/apps/cardano/helpers/protocol_magics.py +++ b/core/src/apps/cardano/helpers/protocol_magics.py @@ -1,5 +1,7 @@ from micropython import const +from trezortranslate import TR + # https://book.world.dev.cardano.org/environments.html MAINNET = const(764824073) TESTNET_PREPROD = const(1) @@ -19,4 +21,4 @@ def is_mainnet(protocol_magic: int) -> bool: def to_ui_string(value: int) -> str: - return NAMES.get(value, "Unknown") + return NAMES.get(value, TR.cardano__unknown) diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index 4b7c69aeb..f0bd37e66 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -10,6 +10,7 @@ from trezor.enums import ( from trezor.strings import format_amount from trezor.ui import layouts from trezor.ui.layouts import confirm_metadata, confirm_properties +from trezortranslate import TR from apps.common.paths import address_n_to_str @@ -34,40 +35,38 @@ if TYPE_CHECKING: ADDRESS_TYPE_NAMES = { - CardanoAddressType.BYRON: "Legacy", - CardanoAddressType.BASE: "Base", - CardanoAddressType.BASE_SCRIPT_KEY: "Base", - CardanoAddressType.BASE_KEY_SCRIPT: "Base", - CardanoAddressType.BASE_SCRIPT_SCRIPT: "Base", - CardanoAddressType.POINTER: "Pointer", - CardanoAddressType.POINTER_SCRIPT: "Pointer", - CardanoAddressType.ENTERPRISE: "Enterprise", - CardanoAddressType.ENTERPRISE_SCRIPT: "Enterprise", - CardanoAddressType.REWARD: "Reward", - CardanoAddressType.REWARD_SCRIPT: "Reward", + CardanoAddressType.BYRON: TR.cardano__addr_legacy, + CardanoAddressType.BASE: TR.cardano__addr_base, + CardanoAddressType.BASE_SCRIPT_KEY: TR.cardano__addr_base, + CardanoAddressType.BASE_KEY_SCRIPT: TR.cardano__addr_base, + CardanoAddressType.BASE_SCRIPT_SCRIPT: TR.cardano__addr_base, + CardanoAddressType.POINTER: TR.cardano__addr_pointer, + CardanoAddressType.POINTER_SCRIPT: TR.cardano__addr_pointer, + CardanoAddressType.ENTERPRISE: TR.cardano__addr_enterprise, + CardanoAddressType.ENTERPRISE_SCRIPT: TR.cardano__addr_enterprise, + CardanoAddressType.REWARD: TR.cardano__addr_reward, + CardanoAddressType.REWARD_SCRIPT: TR.cardano__addr_reward, } SCRIPT_TYPE_NAMES = { - CardanoNativeScriptType.PUB_KEY: "Key", - CardanoNativeScriptType.ALL: "All", - CardanoNativeScriptType.ANY: "Any", - CardanoNativeScriptType.N_OF_K: "N of K", - CardanoNativeScriptType.INVALID_BEFORE: "Invalid before", - CardanoNativeScriptType.INVALID_HEREAFTER: "Invalid hereafter", + CardanoNativeScriptType.PUB_KEY: TR.cardano__script_key, + CardanoNativeScriptType.ALL: TR.cardano__script_all, + CardanoNativeScriptType.ANY: TR.cardano__script_any, + CardanoNativeScriptType.N_OF_K: TR.cardano__script_n_of_k, + CardanoNativeScriptType.INVALID_BEFORE: TR.cardano__script_invalid_before, + CardanoNativeScriptType.INVALID_HEREAFTER: TR.cardano__script_invalid_hereafter, } CERTIFICATE_TYPE_NAMES = { - CardanoCertificateType.STAKE_REGISTRATION: "Stake key registration", - CardanoCertificateType.STAKE_DEREGISTRATION: "Stake key deregistration", - CardanoCertificateType.STAKE_DELEGATION: "Stake delegation", - CardanoCertificateType.STAKE_POOL_REGISTRATION: "Stakepool registration", + CardanoCertificateType.STAKE_REGISTRATION: TR.cardano__stake_registration, + CardanoCertificateType.STAKE_DEREGISTRATION: TR.cardano__stake_deregistration, + CardanoCertificateType.STAKE_DELEGATION: TR.cardano__stake_delegation, + CardanoCertificateType.STAKE_POOL_REGISTRATION: TR.cardano__stake_pool_registration, } BRT_Other = ButtonRequestType.Other # global_import_cache -CVOTE_REWARD_ELIGIBILITY_WARNING = ( - "Warning: The address is not a payment address, it is not eligible for rewards." -) +CVOTE_REWARD_ELIGIBILITY_WARNING = TR.cardano__reward_eligibility_warning def format_coin_amount(amount: int, network_id: int) -> str: @@ -118,7 +117,9 @@ async def show_native_script( assert script.required_signatures_count is not None # validate_script append( ( - f"Requires {script.required_signatures_count} out of {len(scripts)} signatures.", + TR.cardano__x_of_y_signatures_template.format( + script.required_signatures_count, len(scripts) + ), None, ) ) @@ -135,11 +136,11 @@ async def show_native_script( CNST.N_OF_K, ): assert scripts # validate_script - append((f"Contains {len(scripts)} nested scripts.", None)) + append((TR.cardano__nested_scripts_template.format(len(scripts)), None)) await confirm_properties( "verify_script", - "Verify script", + TR.cardano__verify_script, props, br_code=BRT_Other, ) @@ -162,32 +163,37 @@ async def show_script_hash( if display_format == CardanoNativeScriptHashDisplayFormat.BECH32: await confirm_properties( "verify_script", - "Verify script", - (("Script hash:", bech32.encode(bech32.HRP_SCRIPT_HASH, script_hash)),), + TR.cardano__verify_script, + ( + ( + TR.cardano__script_hash, + bech32.encode(bech32.HRP_SCRIPT_HASH, script_hash), + ), + ), br_code=BRT_Other, ) elif display_format == CardanoNativeScriptHashDisplayFormat.POLICY_ID: await layouts.confirm_blob( "verify_script", - "Verify script", + TR.cardano__verify_script, script_hash, - "Policy ID:", + TR.cardano__policy_id, br_code=BRT_Other, ) async def show_tx_init(title: str) -> bool: should_show_details = await layouts.should_show_more( - "Confirm transaction", + TR.cardano__confirm_transaction, ( ( ui.DEMIBOLD, title, ), - (ui.NORMAL, "Choose level of details:"), + (ui.NORMAL, TR.cardano__choose_level_of_details), ), - "Show All", - confirm="Show Simple", + TR.buttons__show_all, + confirm=TR.cardano__show_simple, ) return should_show_details @@ -196,10 +202,10 @@ async def show_tx_init(title: str) -> bool: async def confirm_input(input: messages.CardanoTxInput) -> None: await confirm_properties( "confirm_input", - "Confirm transaction", + TR.cardano__confirm_transaction, ( - ("Input ID:", input.prev_hash), - ("Input index:", str(input.prev_index)), + (TR.cardano__input_id, input.prev_hash), + (TR.cardano__input_index, str(input.prev_index)), ), br_code=BRT_Other, ) @@ -213,11 +219,11 @@ async def confirm_sending( chunkify: bool, ) -> None: if output_type == "address": - title = "Sending" + title = TR.cardano__sending elif output_type == "change": - title = "Change output" + title = TR.cardano__change_output elif output_type == "collateral-return": - title = "Collateral return" + title = TR.cardano__collateral_return else: raise RuntimeError # should be unreachable @@ -235,16 +241,16 @@ async def confirm_sending_token(policy_id: bytes, token: messages.CardanoToken) await confirm_properties( "confirm_token", - "Confirm transaction", + TR.cardano__confirm_transaction, ( ( - "Asset fingerprint:", + TR.cardano__asset_fingerprint, format_asset_fingerprint( policy_id=policy_id, asset_name_bytes=token.asset_name_bytes, ), ), - ("Amount sent (decimals unknown):", format_amount(token.amount, 0)), + (TR.cardano__amount_sent_decimals_unknown, format_amount(token.amount, 0)), ), br_code=BRT_Other, ) @@ -253,10 +259,10 @@ async def confirm_sending_token(policy_id: bytes, token: messages.CardanoToken) async def confirm_datum_hash(datum_hash: bytes) -> None: await confirm_properties( "confirm_datum_hash", - "Confirm transaction", + TR.cardano__confirm_transaction, ( ( - "Datum hash:", + TR.cardano__datum_hash, bech32.encode(bech32.HRP_OUTPUT_DATUM_HASH, datum_hash), ), ), @@ -267,7 +273,7 @@ async def confirm_datum_hash(datum_hash: bytes) -> None: async def confirm_inline_datum(first_chunk: bytes, inline_datum_size: int) -> None: await _confirm_data_chunk( "confirm_inline_datum", - "Inline datum", + TR.cardano__inline_datum, first_chunk, inline_datum_size, ) @@ -278,7 +284,7 @@ async def confirm_reference_script( ) -> None: await _confirm_data_chunk( "confirm_reference_script", - "Reference script", + TR.cardano__reference_script, first_chunk, reference_script_size, ) @@ -300,7 +306,7 @@ async def _confirm_data_chunk( props.append(("...", None)) await confirm_properties( br_type, - title="Confirm transaction", + title=TR.cardano__confirm_transaction, props=props, br_code=BRT_Other, ) @@ -310,7 +316,7 @@ async def show_credentials( payment_credential: Credential, stake_credential: Credential, ) -> None: - intro_text = "Address" + intro_text = TR.cardano__intro_text_address await _show_credential(payment_credential, intro_text, purpose="address") await _show_credential(stake_credential, intro_text, purpose="address") @@ -319,7 +325,7 @@ async def show_change_output_credentials( payment_credential: Credential, stake_credential: Credential, ) -> None: - intro_text = "The following address is a change address. Its" + intro_text = TR.cardano__intro_text_change await _show_credential(payment_credential, intro_text, purpose="output") await _show_credential(stake_credential, intro_text, purpose="output") @@ -329,7 +335,7 @@ async def show_device_owned_output_credentials( stake_credential: Credential, show_both_credentials: bool, ) -> None: - intro_text = "The following address is owned by this device. Its" + intro_text = TR.cardano__intro_text_owned_by_device await _show_credential(payment_credential, intro_text, purpose="output") if show_both_credentials: await _show_credential(stake_credential, intro_text, purpose="output") @@ -341,9 +347,8 @@ async def show_cvote_registration_payment_credentials( show_both_credentials: bool, show_payment_warning: bool, ) -> None: - intro_text = ( - "The vote key registration payment address is owned by this device. Its" - ) + intro_text = TR.cardano__intro_text_registration_payment + await _show_credential( payment_credential, intro_text, purpose="cvote_reg_payment_address" ) @@ -365,8 +370,8 @@ async def _show_credential( ) -> None: title = { "address": f"{ADDRESS_TYPE_NAMES[credential.address_type]} address", - "output": "Confirm transaction", - "cvote_reg_payment_address": "Confirm transaction", + "output": TR.cardano__confirm_transaction, + "cvote_reg_payment_address": TR.cardano__confirm_transaction, }[purpose] props: list[PropertyType] = [] @@ -377,6 +382,7 @@ async def _show_credential( # show some of the "props". if credential.is_set(): credential_title = credential.get_title() + # TODO: handle translation append( ( f"{intro_text} {credential.type_name} credential is a {credential_title}:", @@ -386,16 +392,16 @@ async def _show_credential( props.extend(credential.format()) if credential.is_unusual_path: - append((None, "Path is unusual.")) + append((None, TR.cardano__unusual_path)) if credential.is_mismatch: - append((None, "Credential doesn't match payment credential.")) + append((None, TR.cardano__credential_mismatch)) if credential.is_reward and purpose != "cvote_reg_payment_address": # for cvote registrations, this is handled by extra_text at the end - append(("Address is a reward address.", None)) + append((TR.cardano__reward_address, None)) if credential.is_no_staking: append( ( - f"{ADDRESS_TYPE_NAMES[credential.address_type]} address - no staking rewards.", + f"{ADDRESS_TYPE_NAMES[credential.address_type]} {TR.cardano__address_no_staking}", None, ) ) @@ -418,13 +424,13 @@ async def warn_path(path: list[int], title: str) -> None: async def warn_tx_output_contains_tokens(is_collateral_return: bool = False) -> None: content = ( - "The collateral return output contains tokens." + TR.cardano__collateral_output_contains_tokens if is_collateral_return - else "The following transaction output contains tokens." + else TR.cardano__transaction_output_contains_tokens ) await confirm_metadata( "confirm_tokens", - "Confirm transaction", + TR.cardano__confirm_transaction, content, br_code=BRT_Other, ) @@ -433,8 +439,8 @@ async def warn_tx_output_contains_tokens(is_collateral_return: bool = False) -> async def warn_tx_contains_mint() -> None: await confirm_metadata( "confirm_tokens", - "Confirm transaction", - "The transaction contains minting or burning of tokens.", + TR.cardano__confirm_transaction, + TR.cardano__transaction_contains_minting_or_burning, br_code=BRT_Other, ) @@ -442,8 +448,8 @@ async def warn_tx_contains_mint() -> None: async def warn_tx_output_no_datum() -> None: await confirm_metadata( "confirm_no_datum_hash", - "Confirm transaction", - "The following transaction output contains a script address, but does not contain a datum.", + TR.cardano__confirm_transaction, + TR.cardano__transaction_contains_script_address_no_datum, br_code=BRT_Other, ) @@ -451,8 +457,8 @@ async def warn_tx_output_no_datum() -> None: async def warn_no_script_data_hash() -> None: await confirm_metadata( "confirm_no_script_data_hash", - "Confirm transaction", - "The transaction contains no script data hash. Plutus script will not be able to run.", + TR.cardano__confirm_transaction, + TR.cardano__transaction_no_script_data_hash, br_code=BRT_Other, ) @@ -460,8 +466,8 @@ async def warn_no_script_data_hash() -> None: async def warn_no_collateral_inputs() -> None: await confirm_metadata( "confirm_no_collateral_inputs", - "Confirm transaction", - "The transaction contains no collateral inputs. Plutus script will not be able to run.", + TR.cardano__confirm_transaction, + TR.cardano__transaction_no_collateral_input, br_code=BRT_Other, ) @@ -469,8 +475,8 @@ async def warn_no_collateral_inputs() -> None: async def warn_unknown_total_collateral() -> None: await layouts.show_warning( "confirm_unknown_total_collateral", - "Unknown collateral amount.", - "Check all items carefully.", + TR.cardano__unknown_collateral_amount, + TR.cardano__check_all_items, br_code=BRT_Other, ) @@ -481,17 +487,17 @@ async def confirm_witness_request( from . import seed if seed.is_multisig_path(witness_path): - path_title = "multi-sig path" + path_title = TR.cardano__multisig_path elif seed.is_minting_path(witness_path): - path_title = "token minting path" + path_title = TR.cardano__token_minting_path else: - path_title = "path" + path_title = TR.cardano__path await layouts.confirm_text( "confirm_total", - "Confirm transaction", + TR.cardano__confirm_transaction, address_n_to_str(witness_path), - f"Sign transaction with {path_title}:", + TR.cardano__sign_tx_path_template.format(path_title), BRT_Other, ) @@ -507,25 +513,40 @@ async def confirm_tx( tx_hash: bytes | None, ) -> None: props: list[PropertyType] = [ - ("Transaction fee:", format_coin_amount(fee, network_id)), + (TR.cardano__transaction_fee, format_coin_amount(fee, network_id)), ] append = props.append # local_cache_attribute if total_collateral is not None: - append(("Total collateral:", format_coin_amount(total_collateral, network_id))) + append( + ( + TR.cardano__total_collateral, + format_coin_amount(total_collateral, network_id), + ) + ) if is_network_id_verifiable: - append((f"Network: {protocol_magics.to_ui_string(protocol_magic)}", None)) + append( + ( + f"{TR.cardano__network} {protocol_magics.to_ui_string(protocol_magic)}", + None, + ) + ) - append((f"Valid since: {format_optional_int(validity_interval_start)}", None)) - append((f"TTL: {format_optional_int(ttl)}", None)) + append( + ( + f"{TR.cardano__valid_since} {format_optional_int(validity_interval_start)}", + None, + ) + ) + append((f"{TR.cardano__ttl} {format_optional_int(ttl)}", None)) if tx_hash: - append(("Transaction ID:", tx_hash)) + append((TR.cardano__transaction_id, tx_hash)) await confirm_properties( "confirm_total", - "Confirm transaction", + TR.cardano__confirm_transaction, props, hold=True, br_code=BRT_Other, @@ -538,7 +559,7 @@ async def confirm_certificate(certificate: messages.CardanoTxCertificate) -> Non assert certificate.type != CardanoCertificateType.STAKE_POOL_REGISTRATION props: list[PropertyType] = [ - ("Confirm:", CERTIFICATE_TYPE_NAMES[certificate.type]), + (TR.cardano__confirm, CERTIFICATE_TYPE_NAMES[certificate.type]), _format_stake_credential( certificate.path, certificate.script_hash, certificate.key_hash ), @@ -546,11 +567,11 @@ async def confirm_certificate(certificate: messages.CardanoTxCertificate) -> Non if certificate.type == CardanoCertificateType.STAKE_DELEGATION: assert certificate.pool is not None # validate_certificate - props.append(("to pool:", format_stake_pool_id(certificate.pool))) + props.append((TR.cardano__to_pool, format_stake_pool_id(certificate.pool))) await confirm_properties( "confirm_certificate", - "Confirm transaction", + TR.cardano__confirm_transaction, props, br_code=BRT_Other, ) @@ -566,17 +587,17 @@ async def confirm_stake_pool_parameters( percentage_formatted = str(float(margin_percentage)).rstrip("0").rstrip(".") await confirm_properties( "confirm_pool_registration", - "Confirm transaction", + TR.cardano__confirm_transaction, ( ( - "Stake pool registration\nPool ID:", + TR.cardano__stake_pool_registration_pool_id, format_stake_pool_id(pool_parameters.pool_id), ), - ("Pool reward account:", pool_parameters.reward_account), + (TR.cardano__pool_reward_account, pool_parameters.reward_account), ( - f"Pledge: {format_coin_amount(pool_parameters.pledge, network_id)}\n" - + f"Cost: {format_coin_amount(pool_parameters.cost, network_id)}\n" - + f"Margin: {percentage_formatted}%", + f"{TR.cardano__pledge}: {format_coin_amount(pool_parameters.pledge, network_id)}\n" + + f"{TR.cardano__cost}: {format_coin_amount(pool_parameters.cost, network_id)}\n" + + f"{TR.cardano__margin}: {percentage_formatted}%", None, ), ), @@ -594,7 +615,7 @@ async def confirm_stake_pool_owner( props: list[tuple[str, str | None]] = [] if owner.staking_key_path: - props.append(("Pool owner:", address_n_to_str(owner.staking_key_path))) + props.append((TR.cardano__pool_owner, address_n_to_str(owner.staking_key_path))) props.append( ( addresses.derive_human_readable( @@ -613,7 +634,7 @@ async def confirm_stake_pool_owner( assert owner.staking_key_hash is not None # validate_pool_owners props.append( ( - "Pool owner:", + TR.cardano__pool_owner, addresses.derive_human_readable( keychain, messages.CardanoAddressParametersType( @@ -628,7 +649,7 @@ async def confirm_stake_pool_owner( await confirm_properties( "confirm_pool_owners", - "Confirm transaction", + TR.cardano__confirm_transaction, props, br_code=BRT_Other, ) @@ -640,18 +661,18 @@ async def confirm_stake_pool_metadata( if metadata is None: await confirm_properties( "confirm_pool_metadata", - "Confirm transaction", - (("Pool has no metadata (anonymous pool)", None),), + TR.cardano__confirm_transaction, + ((TR.cardano__anonymous_pool, None),), br_code=BRT_Other, ) return await confirm_properties( "confirm_pool_metadata", - "Confirm transaction", + TR.cardano__confirm_transaction, ( - ("Pool metadata url:", metadata.url), - ("Pool metadata hash:", metadata.hash), + (TR.cardano__pool_metadata_url, metadata.url), + (TR.cardano__pool_metadata_hash, metadata.hash), ), br_code=BRT_Other, ) @@ -664,12 +685,15 @@ async def confirm_stake_pool_registration_final( ) -> None: await confirm_properties( "confirm_pool_final", - "Confirm transaction", + TR.cardano__confirm_transaction, ( - ("Confirm signing the stake pool registration as an owner.", None), - ("Network:", protocol_magics.to_ui_string(protocol_magic)), - ("Valid since:", format_optional_int(validity_interval_start)), - ("TTL:", format_optional_int(ttl)), + (TR.cardano__confirm_signing_stake_pool, None), + (TR.cardano__network, protocol_magics.to_ui_string(protocol_magic)), + ( + TR.cardano__valid_since, + format_optional_int(validity_interval_start), + ), + (TR.cardano__ttl, format_optional_int(ttl)), ), hold=True, br_code=BRT_Other, @@ -681,10 +705,15 @@ async def confirm_withdrawal( address_bytes: bytes, network_id: int, ) -> None: - address_type_name = "script reward" if withdrawal.script_hash else "reward" + address_type_name = ( + TR.cardano__script_reward if withdrawal.script_hash else TR.cardano__reward + ) address = addresses.encode_human_readable(address_bytes) props: list[PropertyType] = [ - (f"Confirm withdrawal for {address_type_name} address:", address), + ( + TR.cardano__withdrawal_for_address_template.format(address_type_name), + address, + ), ] if withdrawal.path: @@ -694,11 +723,13 @@ async def confirm_withdrawal( ) ) - props.append(("Amount:", format_coin_amount(withdrawal.amount, network_id))) + props.append( + (TR.cardano__amount, format_coin_amount(withdrawal.amount, network_id)) + ) await confirm_properties( "confirm_withdrawal", - "Confirm transaction", + TR.cardano__confirm_transaction, props, br_code=BRT_Other, ) @@ -714,17 +745,25 @@ def _format_stake_credential( address_index = path[ADDRESS_INDEX_PATH_INDEX] if address_index == RECOMMENDED_ADDRESS_INDEX: return ( - f"for account {account_number}:", + TR.cardano__for_account_template.format(account_number), address_n_to_str(path), ) return ( - f"for account {account_number} and index {address_index}:", + TR.cardano__for_account_and_index_template.format( + account_number, address_index + ), address_n_to_str(path), ) elif key_hash: - return ("for key hash:", bech32.encode(bech32.HRP_STAKE_KEY_HASH, key_hash)) + return ( + TR.cardano__for_key_hash, + bech32.encode(bech32.HRP_STAKE_KEY_HASH, key_hash), + ) elif script_hash: - return ("for script:", bech32.encode(bech32.HRP_SCRIPT_HASH, script_hash)) + return ( + TR.cardano__for_script, + bech32.encode(bech32.HRP_SCRIPT_HASH, script_hash), + ) else: # should be unreachable unless there's a bug in validation raise ValueError @@ -735,15 +774,15 @@ async def confirm_cvote_registration_delegation( weight: int, ) -> None: props: list[PropertyType] = [ - ("Vote key registration (CIP-36)", None), - ("Delegating to:", public_key), + (TR.cardano__vote_key_registration, None), + (TR.cardano__delegating_to, public_key), ] if weight is not None: - props.append(("Weight:", str(weight))) + props.append((TR.cardano__weight, str(weight))) await confirm_properties( "confirm_cvote_registration_delegation", - title="Confirm transaction", + title=TR.cardano__confirm_transaction, props=props, br_code=ButtonRequestType.Other, ) @@ -754,14 +793,14 @@ async def confirm_cvote_registration_payment_address( should_show_payment_warning: bool, ) -> None: props = [ - ("Vote key registration (CIP-36)", None), - ("Rewards go to:", payment_address), + (TR.cardano__vote_key_registration, None), + (TR.cardano__rewards_go_to, payment_address), ] if should_show_payment_warning: props.append((CVOTE_REWARD_ELIGIBILITY_WARNING, None)) await confirm_properties( "confirm_cvote_registration_payment_address", - title="Confirm transaction", + title=TR.cardano__confirm_transaction, props=props, br_code=ButtonRequestType.Other, ) @@ -773,29 +812,31 @@ async def confirm_cvote_registration( nonce: int, voting_purpose: int | None, ) -> None: - props: list[PropertyType] = [("Vote key registration (CIP-36)", None)] + props: list[PropertyType] = [(TR.cardano__vote_key_registration, None)] if vote_public_key is not None: - props.append(("Vote public key:", vote_public_key)) + props.append((TR.cardano__vote_public_key, vote_public_key)) props.extend( [ ( - f"Staking key for account {format_account_number(staking_path)}:", + f"{TR.cardano__staking_key_for_account} {format_account_number(staking_path)}:", address_n_to_str(staking_path), ), - ("Nonce:", str(nonce)), + (TR.cardano__nonce, str(nonce)), ] ) if voting_purpose is not None: props.append( ( - "Voting purpose:", - "Catalyst" if voting_purpose == 0 else f"{voting_purpose} (other)", + TR.cardano__voting_purpose, + TR.cardano__catalyst + if voting_purpose == 0 + else f"{voting_purpose} ({TR.cardano__other})", ) ) await confirm_properties( "confirm_cvote_registration", - title="Confirm transaction", + title=TR.cardano__confirm_transaction, props=props, br_code=ButtonRequestType.Other, ) @@ -804,8 +845,8 @@ async def confirm_cvote_registration( async def show_auxiliary_data_hash(auxiliary_data_hash: bytes) -> None: await confirm_properties( "confirm_auxiliary_data", - "Confirm transaction", - (("Auxiliary data hash:", auxiliary_data_hash),), + TR.cardano__confirm_transaction, + ((TR.cardano__auxiliary_data_hash, auxiliary_data_hash),), br_code=BRT_Other, ) @@ -814,19 +855,19 @@ async def confirm_token_minting(policy_id: bytes, token: messages.CardanoToken) assert token.mint_amount is not None # _validate_token await confirm_properties( "confirm_mint", - "Confirm transaction", + TR.cardano__confirm_transaction, ( ( - "Asset fingerprint:", + TR.cardano__asset_fingerprint, format_asset_fingerprint( policy_id, token.asset_name_bytes, ), ), ( - "Amount minted (decimals unknown):" + TR.cardano__amount_minted_decimals_unknown if token.mint_amount >= 0 - else "Amount burned (decimals unknown):", + else TR.cardano__amount_burned_decimals_unknown, format_amount(token.mint_amount, 0), ), ), @@ -837,8 +878,8 @@ async def confirm_token_minting(policy_id: bytes, token: messages.CardanoToken) async def warn_tx_network_unverifiable() -> None: await confirm_metadata( "warning_no_outputs", - "Warning", - "Transaction has no outputs, network cannot be verified.", + TR.cardano__warning, + TR.cardano__no_output_tx, br_code=BRT_Other, ) @@ -846,10 +887,10 @@ async def warn_tx_network_unverifiable() -> None: async def confirm_script_data_hash(script_data_hash: bytes) -> None: await confirm_properties( "confirm_script_data_hash", - "Confirm transaction", + TR.cardano__confirm_transaction, ( ( - "Script data hash:", + TR.cardano__script_data_hash, bech32.encode(bech32.HRP_SCRIPT_DATA_HASH, script_data_hash), ), ), @@ -862,10 +903,13 @@ async def confirm_collateral_input( ) -> None: await confirm_properties( "confirm_collateral_input", - "Confirm transaction", + TR.cardano__confirm_transaction, ( - ("Collateral input ID:", collateral_input.prev_hash), - ("Collateral input index:", str(collateral_input.prev_index)), + (TR.cardano__collateral_input_id, collateral_input.prev_hash), + ( + TR.cardano__collateral_input_index, + str(collateral_input.prev_index), + ), ), br_code=BRT_Other, ) @@ -876,10 +920,10 @@ async def confirm_reference_input( ) -> None: await confirm_properties( "confirm_reference_input", - "Confirm transaction", + TR.cardano__confirm_transaction, ( - ("Reference input ID:", reference_input.prev_hash), - ("Reference input index:", str(reference_input.prev_index)), + (TR.cardano__reference_input_id, reference_input.prev_hash), + (TR.cardano__reference_input_index, str(reference_input.prev_index)), ), br_code=BRT_Other, ) @@ -899,8 +943,8 @@ async def confirm_required_signer( await confirm_properties( "confirm_required_signer", - "Confirm transaction", - (("Required signer", formatted_signer),), + TR.cardano__confirm_transaction, + ((TR.cardano__required_signer, formatted_signer),), br_code=BRT_Other, ) diff --git a/core/src/apps/cardano/sign_tx/multisig_signer.py b/core/src/apps/cardano/sign_tx/multisig_signer.py index dda7518df..9fb56ca1f 100644 --- a/core/src/apps/cardano/sign_tx/multisig_signer.py +++ b/core/src/apps/cardano/sign_tx/multisig_signer.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from trezor.wire import ProcessError +from trezortranslate import TR from .signer import Signer @@ -13,7 +14,7 @@ class MultisigSigner(Signer): The multisig signing mode only allows signing with multisig (and minting) keys. """ - SIGNING_MODE_TITLE = "Confirming a multisig transaction." + SIGNING_MODE_TITLE = TR.cardano__confirming_a_multisig_transaction def _validate_tx_init(self) -> None: msg = self.msg # local_cache_attribute diff --git a/core/src/apps/cardano/sign_tx/ordinary_signer.py b/core/src/apps/cardano/sign_tx/ordinary_signer.py index 40c8f7d4d..b13783181 100644 --- a/core/src/apps/cardano/sign_tx/ordinary_signer.py +++ b/core/src/apps/cardano/sign_tx/ordinary_signer.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from trezor.wire import ProcessError +from trezortranslate import TR from .. import layout from ..helpers.paths import SCHEMA_MINT @@ -16,7 +17,7 @@ class OrdinarySigner(Signer): controlled by 1852' keys, dealing with staking and minting/burning tokens. """ - SIGNING_MODE_TITLE = "Confirming a transaction." + SIGNING_MODE_TITLE = TR.cardano__confirming_transction def _validate_tx_init(self) -> None: msg = self.msg # local_cache_attribute diff --git a/core/src/apps/cardano/sign_tx/pool_owner_signer.py b/core/src/apps/cardano/sign_tx/pool_owner_signer.py index 4c42a9f16..500ee7b8b 100644 --- a/core/src/apps/cardano/sign_tx/pool_owner_signer.py +++ b/core/src/apps/cardano/sign_tx/pool_owner_signer.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from trezor.wire import ProcessError +from trezortranslate import TR from .signer import Signer @@ -21,7 +22,7 @@ class PoolOwnerSigner(Signer): staking key in the list of pool owners. """ - SIGNING_MODE_TITLE = "Confirming pool registration as owner." + SIGNING_MODE_TITLE = TR.cardano__confirming_pool_registration def _validate_tx_init(self) -> None: msg = self.msg # local_cache_attribute diff --git a/core/src/apps/common/passphrase.py b/core/src/apps/common/passphrase.py index c24ad333d..4d46e6169 100644 --- a/core/src/apps/common/passphrase.py +++ b/core/src/apps/common/passphrase.py @@ -33,6 +33,7 @@ async def _request_on_host() -> str: from trezor.messages import PassphraseAck, PassphraseRequest from trezor.ui.layouts import request_passphrase_on_host from trezor.wire.context import call + from trezortranslate import TR request_passphrase_on_host() @@ -58,23 +59,22 @@ async def _request_on_host() -> str: # We want to hide the passphrase, or show it, according to settings. if storage_device.get_hide_passphrase_from_host(): - explanation = "Passphrase provided by host will be used but will not be displayed due to the device settings." await confirm_action( "passphrase_host1_hidden", - "Hidden wallet", - description=f"Access hidden wallet?\n{explanation}", + TR.passphrase__hidden_wallet, + description=f"{TR.passphrase__access_hidden_wallet}\n{TR.passphrase__from_host_not_shown}", ) else: await confirm_action( "passphrase_host1", - "Hidden wallet", - description="Next screen will show the passphrase.", - verb="Continue", + TR.passphrase__hidden_wallet, + description=TR.passphrase__next_screen_will_show_passphrase, + verb=TR.buttons__continue, ) await confirm_blob( "passphrase_host2", - "Confirm passphrase", + TR.passphrase__title_confirm, passphrase, ) diff --git a/core/src/apps/common/request_pin.py b/core/src/apps/common/request_pin.py index fb9ab1519..b45d862cf 100644 --- a/core/src/apps/common/request_pin.py +++ b/core/src/apps/common/request_pin.py @@ -4,6 +4,7 @@ from typing import Any, NoReturn import storage.cache as storage_cache from trezor import config, utils, wire from trezor.ui.layouts import show_error_and_raise +from trezortranslate import TR async def _request_sd_salt( @@ -53,9 +54,9 @@ async def request_pin_confirm(*args: Any, **kwargs: Any) -> str: from trezor.ui.layouts import confirm_reenter_pin, pin_mismatch_popup while True: - pin1 = await request_pin("Enter new PIN", *args, **kwargs) + pin1 = await request_pin(TR.pin__enter_new, *args, **kwargs) await confirm_reenter_pin() - pin2 = await request_pin("Re-enter new PIN", *args, **kwargs) + pin2 = await request_pin(TR.pin__reenter_new, *args, **kwargs) if pin1 == pin2: return pin1 await pin_mismatch_popup() @@ -80,8 +81,11 @@ def _set_last_unlock_time() -> None: storage_cache.set_int(storage_cache.APP_COMMON_REQUEST_PIN_LAST_UNLOCK, now) +_DEF_ARG_PIN_ENTER: str = TR.pin__enter + + async def verify_user_pin( - prompt: str = "Enter PIN", + prompt: str = _DEF_ARG_PIN_ENTER, allow_cancel: bool = True, retry: bool = True, cache_time_ms: int = 0, @@ -116,7 +120,7 @@ async def verify_user_pin( while retry: pin = await request_pin_on_device( # type: ignore ["request_pin_on_device" is possibly unbound] - "Enter PIN", config.get_pin_rem(), allow_cancel, wrong_pin=True + TR.pin__enter, config.get_pin_rem(), allow_cancel, wrong_pin=True ) if config.unlock(pin, salt): _set_last_unlock_time() @@ -128,8 +132,8 @@ async def verify_user_pin( async def error_pin_invalid() -> NoReturn: await show_error_and_raise( "warning_wrong_pin", - "The PIN you have entered is not valid.", - "Wrong PIN", # header + TR.pin__entered_not_valid, + TR.pin__wrong_pin, # header exc=wire.PinInvalid, ) assert False @@ -138,8 +142,8 @@ async def error_pin_invalid() -> NoReturn: async def error_pin_matches_wipe_code() -> NoReturn: await show_error_and_raise( "warning_invalid_new_pin", - "The new PIN must be different from your wipe code.", - "Invalid PIN", # header + TR.pin__diff_from_wipe_code, + TR.pin__invalid_pin, # header exc=wire.PinInvalid, ) assert False diff --git a/core/src/apps/common/sdcard.py b/core/src/apps/common/sdcard.py index b1d7c71b5..bc09e3700 100644 --- a/core/src/apps/common/sdcard.py +++ b/core/src/apps/common/sdcard.py @@ -1,6 +1,7 @@ from storage.sd_salt import SD_CARD_HOT_SWAPPABLE from trezor import io, wire from trezor.ui.layouts import confirm_action, show_error_and_raise +from trezortranslate import TR class SdCardUnavailable(wire.ProcessError): @@ -11,18 +12,18 @@ async def _confirm_retry_wrong_card() -> None: if SD_CARD_HOT_SWAPPABLE: await confirm_action( "warning_wrong_sd", - "SD card protection", - "Wrong SD card.", - "Please insert the correct SD card for this device.", - verb="Retry", - verb_cancel="Abort", + TR.sd_card__title, + TR.sd_card__wrong_sd_card, + TR.sd_card__insert_correct_card, + verb=TR.buttons__retry, + verb_cancel=TR.buttons__abort, exc=SdCardUnavailable("Wrong SD card."), ) else: await show_error_and_raise( "warning_wrong_sd", - "Please unplug the device and insert the correct SD card.", - "Wrong SD card.", + TR.sd_card__unplug_and_insert_correct, + TR.sd_card__wrong_sd_card, exc=SdCardUnavailable("Wrong SD card."), ) @@ -31,18 +32,18 @@ async def _confirm_retry_insert_card() -> None: if SD_CARD_HOT_SWAPPABLE: await confirm_action( "warning_no_sd", - "SD card protection", - "SD card required.", - "Please insert your SD card.", - verb="Retry", - verb_cancel="Abort", + TR.sd_card__title, + TR.sd_card__card_required, + TR.sd_card__please_insert, + verb=TR.buttons__retry, + verb_cancel=TR.buttons__abort, exc=SdCardUnavailable("SD card required."), ) else: await show_error_and_raise( "warning_no_sd", - "Please unplug the device and insert your SD card.", - "SD card required.", + TR.sd_card__please_unplug_and_insert, + TR.sd_card__card_required, exc=SdCardUnavailable("SD card required."), ) @@ -51,22 +52,22 @@ async def _confirm_format_card() -> None: # Format card? yes/no await confirm_action( "warning_format_sd", - "SD card error", - "Unknown filesystem.", - "Use a different card or format the SD card to the FAT32 filesystem.", - verb="Format", - verb_cancel="Cancel", + TR.sd_card__error, + TR.sd_card__unknown_filesystem, + TR.sd_card__use_different_card, + verb=TR.buttons__format, + verb_cancel=TR.buttons__cancel, exc=SdCardUnavailable("SD card not formatted."), ) # Confirm formatting await confirm_action( "confirm_format_sd", - "Format SD card", - "All data on the SD card will be lost.", - "Do you really want to format the SD card?", + TR.sd_card__format_card, + TR.sd_card__all_data_will_be_lost, + TR.sd_card__wanna_format, reverse=True, - verb="Format SD card", + verb=TR.sd_card__format_card, hold=True, exc=SdCardUnavailable("SD card not formatted."), ) @@ -77,11 +78,11 @@ async def confirm_retry_sd( ) -> None: await confirm_action( "warning_sd_retry", - "SD card problem", + TR.sd_card__title_problem, None, - "There was a problem accessing the SD card.", - verb="Retry", - verb_cancel="Abort", + TR.sd_card__problem_accessing, + verb=TR.buttons__retry, + verb_cancel=TR.buttons__abort, exc=exc, ) diff --git a/core/src/apps/debug/__init__.py b/core/src/apps/debug/__init__.py index e6426045c..f220e5ddf 100644 --- a/core/src/apps/debug/__init__.py +++ b/core/src/apps/debug/__init__.py @@ -113,9 +113,12 @@ if __debug__: break elif event_id > awaited_event_id: # Sanity check - raise RuntimeError( - f"Waiting for event that already happened - {event_id} > {awaited_event_id}" - ) + pass + # TODO: find out why this sometimes happens on TR when running tests with + # "physical" emulator (./emu.py) + # raise RuntimeError( + # f"Waiting for event that already happened - {event_id} > {awaited_event_id}" + # ) if awaited_event_id is not None: # Updating last result diff --git a/core/src/apps/debug/load_device.py b/core/src/apps/debug/load_device.py index ce873e321..b9f55c9d7 100644 --- a/core/src/apps/debug/load_device.py +++ b/core/src/apps/debug/load_device.py @@ -12,6 +12,7 @@ async def load_device(msg: LoadDevice) -> Success: from trezor.messages import Success from trezor.ui.layouts import confirm_action from trezor.wire import ProcessError, UnexpectedMessage + from trezortranslate import TR from apps.management import backup_types @@ -39,9 +40,9 @@ async def load_device(msg: LoadDevice) -> Success: # _warn await confirm_action( "warn_loading_seed", - "Loading seed", - "Loading private seed is not recommended.", - "Continue only if you know what you are doing!", + TR.debug__loading_seed, + TR.debug__loading_seed_not_recommended, + TR.words__know_what_your_doing, ) # END _warn diff --git a/core/src/apps/eos/actions/layout.py b/core/src/apps/eos/actions/layout.py index 22a8659cb..b876e5003 100644 --- a/core/src/apps/eos/actions/layout.py +++ b/core/src/apps/eos/actions/layout.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING from trezor.enums import ButtonRequestType from trezor.ui.layouts import confirm_properties +from trezortranslate import TR from ..helpers import eos_asset_to_string, eos_name_to_string @@ -51,11 +52,11 @@ async def _confirm_properties( async def confirm_action_buyram(msg: EosActionBuyRam) -> None: await _confirm_properties( "confirm_buyram", - "Buy RAM", + TR.eos__buy_ram, ( - ("Payer:", eos_name_to_string(msg.payer)), - ("Receiver:", eos_name_to_string(msg.receiver)), - ("Amount:", eos_asset_to_string(msg.quantity)), + (TR.eos__payer, eos_name_to_string(msg.payer)), + (TR.eos__receiver, eos_name_to_string(msg.receiver)), + (TR.eos__amount, eos_asset_to_string(msg.quantity)), ), ) @@ -63,32 +64,32 @@ async def confirm_action_buyram(msg: EosActionBuyRam) -> None: async def confirm_action_buyrambytes(msg: EosActionBuyRamBytes) -> None: await _confirm_properties( "confirm_buyrambytes", - "Buy RAM", + TR.eos__buy_ram, ( - ("Payer:", eos_name_to_string(msg.payer)), - ("Receiver:", eos_name_to_string(msg.receiver)), - ("Bytes:", str(msg.bytes)), + (TR.eos__payer, eos_name_to_string(msg.payer)), + (TR.eos__receiver, eos_name_to_string(msg.receiver)), + (TR.eos__bytes, str(msg.bytes)), ), ) async def confirm_action_delegate(msg: EosActionDelegate) -> None: props = [ - ("Sender:", eos_name_to_string(msg.sender)), - ("Receiver:", eos_name_to_string(msg.receiver)), - ("CPU:", eos_asset_to_string(msg.cpu_quantity)), - ("NET:", eos_asset_to_string(msg.net_quantity)), + (TR.eos__sender, eos_name_to_string(msg.sender)), + (TR.eos__receiver, eos_name_to_string(msg.receiver)), + (TR.eos__cpu, eos_asset_to_string(msg.cpu_quantity)), + (TR.eos__net, eos_asset_to_string(msg.net_quantity)), ] append = props.append # local_cache_attribute if msg.transfer: - append(("Transfer:", "Yes")) - append(("Receiver:", eos_name_to_string(msg.receiver))) + append((TR.eos__transfer, TR.eos__yes)) + append((TR.eos__receiver, eos_name_to_string(msg.receiver))) else: - append(("Transfer:", "No")) + append((TR.eos__transfer, TR.eos__no)) await _confirm_properties( "confirm_delegate", - "Delegate", + TR.eos__delegate, props, ) @@ -96,10 +97,10 @@ async def confirm_action_delegate(msg: EosActionDelegate) -> None: async def confirm_action_sellram(msg: EosActionSellRam) -> None: await _confirm_properties( "confirm_sellram", - "Sell RAM", + TR.eos__sell_ram, ( - ("Receiver:", eos_name_to_string(msg.account)), - ("Bytes:", str(msg.bytes)), + (TR.eos__receiver, eos_name_to_string(msg.account)), + (TR.eos__bytes, str(msg.bytes)), ), ) @@ -107,12 +108,12 @@ async def confirm_action_sellram(msg: EosActionSellRam) -> None: async def confirm_action_undelegate(msg: EosActionUndelegate) -> None: await _confirm_properties( "confirm_undelegate", - "Undelegate", + TR.eos__undelegate, ( - ("Sender:", eos_name_to_string(msg.sender)), - ("Receiver:", eos_name_to_string(msg.receiver)), - ("CPU:", eos_asset_to_string(msg.cpu_quantity)), - ("NET:", eos_asset_to_string(msg.net_quantity)), + (TR.eos__sender, eos_name_to_string(msg.sender)), + (TR.eos__receiver, eos_name_to_string(msg.receiver)), + (TR.eos__cpu, eos_asset_to_string(msg.cpu_quantity)), + (TR.eos__net, eos_asset_to_string(msg.net_quantity)), ), ) @@ -120,8 +121,8 @@ async def confirm_action_undelegate(msg: EosActionUndelegate) -> None: async def confirm_action_refund(msg: EosActionRefund) -> None: await _confirm_properties( "confirm_refund", - "Refund", - (("Owner:", eos_name_to_string(msg.owner)),), + TR.eos__refund, + ((TR.eos__owner, eos_name_to_string(msg.owner)),), ) @@ -132,10 +133,10 @@ async def confirm_action_voteproducer(msg: EosActionVoteProducer) -> None: # PROXY await _confirm_properties( "confirm_voteproducer", - "Vote for proxy", + TR.eos__vote_for_proxy, ( - ("Voter:", eos_name_to_string(msg.voter)), - ("Proxy:", eos_name_to_string(msg.proxy)), + (TR.eos__voter, eos_name_to_string(msg.voter)), + (TR.eos__proxy, eos_name_to_string(msg.proxy)), ), ) @@ -143,7 +144,7 @@ async def confirm_action_voteproducer(msg: EosActionVoteProducer) -> None: # PRODUCERS await _confirm_properties( "confirm_voteproducer", - "Vote for producers", + TR.eos__vote_for_producers, ( (f"{wi:2d}. {eos_name_to_string(producer)}", None) for wi, producer in enumerate(producers, 1) @@ -154,37 +155,37 @@ async def confirm_action_voteproducer(msg: EosActionVoteProducer) -> None: # Cancel vote await _confirm_properties( "confirm_voteproducer", - "Cancel vote", - (("Voter:", eos_name_to_string(msg.voter)),), + TR.eos__cancel_vote, + ((TR.eos__voter, eos_name_to_string(msg.voter)),), ) async def confirm_action_transfer(msg: EosActionTransfer, account: str) -> None: props = [ - ("From:", eos_name_to_string(msg.sender)), - ("To:", eos_name_to_string(msg.receiver)), - ("Amount:", eos_asset_to_string(msg.quantity)), - ("Contract:", account), + (TR.eos__from, eos_name_to_string(msg.sender)), + (TR.eos__to, eos_name_to_string(msg.receiver)), + (TR.eos__amount, eos_asset_to_string(msg.quantity)), + (TR.eos__contract, account), ] if msg.memo is not None: - props.append(("Memo", msg.memo[:512])) + props.append((TR.eos__memo, msg.memo[:512])) await _confirm_properties( "confirm_transfer", - "Transfer", + TR.eos__transfer.replace(":", ""), props, ) async def confirm_action_updateauth(msg: EosActionUpdateAuth) -> None: props: list[PropertyType] = [ - ("Account:", eos_name_to_string(msg.account)), - ("Permission:", eos_name_to_string(msg.permission)), - ("Parent:", eos_name_to_string(msg.parent)), + (TR.eos__account, eos_name_to_string(msg.account)), + (TR.eos__permission, eos_name_to_string(msg.permission)), + (TR.eos__parent, eos_name_to_string(msg.parent)), ] props.extend(authorization_fields(msg.auth)) await _confirm_properties( "confirm_updateauth", - "Update Auth", + TR.eos__update_auth, props, ) @@ -192,10 +193,10 @@ async def confirm_action_updateauth(msg: EosActionUpdateAuth) -> None: async def confirm_action_deleteauth(msg: EosActionDeleteAuth) -> None: await _confirm_properties( "confirm_deleteauth", - "Delete Auth", + TR.eos__delete_auth, ( - ("Account:", eos_name_to_string(msg.account)), - ("Permission:", eos_name_to_string(msg.permission)), + (TR.eos__account, eos_name_to_string(msg.account)), + (TR.eos__permission, eos_name_to_string(msg.permission)), ), ) @@ -203,12 +204,12 @@ async def confirm_action_deleteauth(msg: EosActionDeleteAuth) -> None: async def confirm_action_linkauth(msg: EosActionLinkAuth) -> None: await _confirm_properties( "confirm_linkauth", - "Link Auth", + TR.eos__link_auth, ( - ("Account:", eos_name_to_string(msg.account)), - ("Code:", eos_name_to_string(msg.code)), - ("Type:", eos_name_to_string(msg.type)), - ("Requirement:", eos_name_to_string(msg.requirement)), + (TR.eos__account, eos_name_to_string(msg.account)), + (TR.eos__code, eos_name_to_string(msg.code)), + (TR.eos__type, eos_name_to_string(msg.type)), + (TR.eos__requirement, eos_name_to_string(msg.requirement)), ), ) @@ -216,25 +217,25 @@ async def confirm_action_linkauth(msg: EosActionLinkAuth) -> None: async def confirm_action_unlinkauth(msg: EosActionUnlinkAuth) -> None: await _confirm_properties( "confirm_unlinkauth", - "Unlink Auth", + TR.eos__unlink_auth, ( - ("Account:", eos_name_to_string(msg.account)), - ("Code:", eos_name_to_string(msg.code)), - ("Type:", eos_name_to_string(msg.type)), + (TR.eos__account, eos_name_to_string(msg.account)), + (TR.eos__code, eos_name_to_string(msg.code)), + (TR.eos__type, eos_name_to_string(msg.type)), ), ) async def confirm_action_newaccount(msg: EosActionNewAccount) -> None: props: list[PropertyType] = [ - ("Creator:", eos_name_to_string(msg.creator)), - ("Name:", eos_name_to_string(msg.name)), + (TR.eos__creator, eos_name_to_string(msg.creator)), + (TR.eos__name, eos_name_to_string(msg.name)), ] props.extend(authorization_fields(msg.owner)) props.extend(authorization_fields(msg.active)) await _confirm_properties( "confirm_newaccount", - "New Account", + TR.eos__new_account, props, ) @@ -242,11 +243,11 @@ async def confirm_action_newaccount(msg: EosActionNewAccount) -> None: async def confirm_action_unknown(action: EosActionCommon, checksum: bytes) -> None: await confirm_properties( "confirm_unknown", - "Arbitrary data", + TR.eos__arbitrary_data, ( - ("Contract:", eos_name_to_string(action.account)), - ("Action Name:", eos_name_to_string(action.name)), - ("Checksum:", checksum), + (TR.eos__contract, eos_name_to_string(action.account)), + (TR.eos__action_name, eos_name_to_string(action.name)), + (TR.eos__checksum, checksum), ), hold=is_last, br_code=ButtonRequestType.ConfirmOutput, @@ -261,7 +262,7 @@ def authorization_fields(auth: EosAuthorization) -> list[PropertyType]: fields: list[PropertyType] = [] append = fields.append # local_cache_attribute - append(("Threshold:", str(auth.threshold))) + append((TR.eos__threshold, str(auth.threshold))) # NOTE: getting rid of f-strings saved almost 100 bytes @@ -283,6 +284,7 @@ def authorization_fields(auth: EosAuthorization) -> list[PropertyType]: _permission = eos_name_to_string(account.account.permission) i = str(i) + # TODO: handle translation a_header = "Account #" + i + ":" p_header = "Acc Permission #" + i + ":" w_header = "Account #" + i + " weight:" diff --git a/core/src/apps/eos/layout.py b/core/src/apps/eos/layout.py index 5ea24236f..05778ade4 100644 --- a/core/src/apps/eos/layout.py +++ b/core/src/apps/eos/layout.py @@ -10,11 +10,14 @@ async def require_sign_tx(num_actions: int) -> None: from trezor.enums import ButtonRequestType from trezor.strings import format_plural from trezor.ui.layouts import confirm_action + from trezortranslate import TR await confirm_action( "confirm_tx", - "Sign transaction", - description="You are about to sign {}.", - description_param=format_plural("{count} {plural}", num_actions, "action"), + TR.eos__sign_transaction, + description=TR.eos__about_to_sign_template, + description_param=format_plural( + "{count} {plural}", num_actions, TR.plurals__sign_x_actions + ), br_code=ButtonRequestType.SignTx, ) diff --git a/core/src/apps/ethereum/layout.py b/core/src/apps/ethereum/layout.py index b8b0f5b1f..8ebf09e4c 100644 --- a/core/src/apps/ethereum/layout.py +++ b/core/src/apps/ethereum/layout.py @@ -2,13 +2,13 @@ from typing import TYPE_CHECKING from trezor import ui from trezor.enums import ButtonRequestType -from trezor.strings import format_plural from trezor.ui.layouts import ( confirm_blob, confirm_ethereum_tx, confirm_text, should_show_more, ) +from trezortranslate import TR from .helpers import address_from_bytes, decode_typed_data @@ -35,19 +35,19 @@ async def require_confirm_tx( if to_bytes: to_str = address_from_bytes(to_bytes, network) else: - to_str = "new contract?" + to_str = TR.ethereum__new_contract chunkify = False total_amount = format_ethereum_amount(value, token, network) maximum_fee = format_ethereum_amount(gas_price * gas_limit, None, network) - gas_limit_str = f"{gas_limit} units" + gas_limit_str = TR.ethereum__units_template.format(gas_limit) gas_price_str = format_ethereum_amount( gas_price, None, network, force_unit_gwei=True ) items = ( - ("Gas limit:", gas_limit_str), - ("Gas price:", gas_price_str), + (TR.ethereum__gas_limit, gas_limit_str), + (TR.ethereum__gas_price, gas_price_str), ) await confirm_ethereum_tx( @@ -68,12 +68,12 @@ async def require_confirm_tx_eip1559( if to_bytes: to_str = address_from_bytes(to_bytes, network) else: - to_str = "new contract?" + to_str = TR.ethereum__new_contract chunkify = False total_amount = format_ethereum_amount(value, token, network) maximum_fee = format_ethereum_amount(max_gas_fee * gas_limit, None, network) - gas_limit_str = f"{gas_limit} units" + gas_limit_str = TR.ethereum__units_template.format(gas_limit) max_gas_fee_str = format_ethereum_amount( max_gas_fee, None, network, force_unit_gwei=True ) @@ -81,10 +81,10 @@ async def require_confirm_tx_eip1559( max_priority_fee, None, network, force_unit_gwei=True ) - items = ( - ("Gas limit:", gas_limit_str), - ("Max gas price:", max_gas_fee_str), - ("Priority fee:", max_priority_fee_str), + items: tuple[tuple[str, str], ...] = ( + (TR.ethereum__gas_limit, gas_limit_str), + (TR.ethereum__max_gas_price, max_gas_fee_str), + (TR.ethereum__priority_fee, max_priority_fee_str), ) await confirm_ethereum_tx( @@ -99,9 +99,9 @@ def require_confirm_unknown_token(address_bytes: bytes) -> Awaitable[None]: contract_address_hex = "0x" + hexlify(address_bytes).decode() return confirm_address( - "Unknown token", + TR.ethereum__unknown_token, contract_address_hex, - "Contract:", + TR.ethereum__contract, "unknown_token", br_code=ButtonRequestType.SignTx, ) @@ -114,7 +114,7 @@ def require_confirm_address(address_bytes: bytes) -> Awaitable[None]: address_hex = "0x" + hexlify(address_bytes).decode() return confirm_address( - "Signing address", + TR.ethereum__title_signing_address, address_hex, br_code=ButtonRequestType.SignTx, ) @@ -123,9 +123,9 @@ def require_confirm_address(address_bytes: bytes) -> Awaitable[None]: def require_confirm_data(data: bytes, data_total: int) -> Awaitable[None]: return confirm_blob( "confirm_data", - "Confirm data", + TR.ethereum__title_confirm_data, data, - f"Size: {data_total} bytes", + TR.ethereum__data_size_template.format(data_total), br_code=ButtonRequestType.SignTx, ask_pagination=True, ) @@ -136,9 +136,9 @@ async def confirm_typed_data_final() -> None: await confirm_action( "confirm_typed_data_final", - "Confirm typed data", - "Really sign EIP-712 typed data?", - verb="Hold to confirm", + TR.ethereum__title_confirm_typed_data, + TR.ethereum__sign_eip712, + verb=TR.buttons__hold_to_confirm, hold=True, ) @@ -146,9 +146,9 @@ async def confirm_typed_data_final() -> None: def confirm_empty_typed_message() -> Awaitable[None]: return confirm_text( "confirm_empty_typed_message", - "Confirm message", + TR.ethereum__title_confirm_message, "", - "No message field", + TR.ethereum__no_message_field, ) @@ -157,14 +157,14 @@ async def should_show_domain(name: bytes, version: bytes) -> bool: domain_version = decode_typed_data(version, "string") para = ( - (ui.NORMAL, "Name and version"), + (ui.NORMAL, TR.ethereum__name_and_version), (ui.DEMIBOLD, domain_name), (ui.DEMIBOLD, domain_version), ) return await should_show_more( - "Confirm domain", + TR.ethereum__title_confirm_domain, para, - "Show full domain", + TR.ethereum__show_full_domain, "should_show_domain", ) @@ -172,14 +172,24 @@ async def should_show_domain(name: bytes, version: bytes) -> bool: async def should_show_struct( description: str, data_members: list[EthereumStructMember], - title: str = "Confirm struct", - button_text: str = "Show full struct", + title: str | None = None, + button_text: str | None = None, ) -> bool: + from trezor.strings import format_plural + + title = title or TR.ethereum__title_confirm_struct # def_arg + button_text = button_text or TR.ethereum__show_full_struct # def_arg + + plural = format_plural( + "{count} {plural}", len(data_members), TR.plurals__contains_x_keys + ) + contains_plural = f"{TR.words__contains} {plural}" + para = ( (ui.DEMIBOLD, description), ( ui.NORMAL, - format_plural("Contains {count} {plural}", len(data_members), "key"), + contains_plural, ), (ui.NORMAL, ", ".join(field.name for field in data_members)), ) @@ -196,11 +206,16 @@ async def should_show_array( data_type: str, size: int, ) -> bool: - para = ((ui.NORMAL, format_plural("Array of {count} {plural}", size, data_type)),) + from trezor.strings import format_plural_english + + # Leaving english plural form because of dynamic noun - data_type + plural = format_plural_english("{count} {plural}", size, data_type) + array_of_plural = f"{TR.words__array_of} {plural}" + para = ((ui.NORMAL, array_of_plural),) return await should_show_more( limit_str(".".join(parent_objects)), para, - "Show full array", + TR.ethereum__show_full_array, "should_show_array", ) diff --git a/core/src/apps/ethereum/sign_typed_data.py b/core/src/apps/ethereum/sign_typed_data.py index e03f889d8..9b45588a2 100644 --- a/core/src/apps/ethereum/sign_typed_data.py +++ b/core/src/apps/ethereum/sign_typed_data.py @@ -67,6 +67,8 @@ async def _generate_typed_data_hash( metamask_v4_compat - a flag that enables compatibility with MetaMask's signTypedData_v4 method """ + from trezortranslate import TR + from .layout import ( confirm_empty_typed_message, confirm_typed_data_final, @@ -98,8 +100,8 @@ async def _generate_typed_data_hash( show_message = await should_show_struct( primary_type, typed_data_envelope.types[primary_type].members, - "Confirm message", - "Show full message", + TR.ethereum__title_confirm_message, + TR.ethereum__show_full_message, ) message_hash = await typed_data_envelope.hash_struct( primary_type, diff --git a/core/src/apps/ethereum/verify_message.py b/core/src/apps/ethereum/verify_message.py index b8d8e8304..eeec54606 100644 --- a/core/src/apps/ethereum/verify_message.py +++ b/core/src/apps/ethereum/verify_message.py @@ -10,6 +10,7 @@ async def verify_message(msg: EthereumVerifyMessage) -> Success: from trezor.messages import Success from trezor.ui.layouts import confirm_signverify, show_success from trezor.wire import DataError + from trezortranslate import TR from apps.common.signverify import decode_message @@ -36,5 +37,5 @@ async def verify_message(msg: EthereumVerifyMessage) -> Success: await confirm_signverify(decode_message(msg.message), address, verify=True) - await show_success("verify_message", "The signature is valid.") + await show_success("verify_message", TR.ethereum__valid_signature) return Success(message="Message verified") diff --git a/core/src/apps/homescreen/__init__.py b/core/src/apps/homescreen/__init__.py index 1690cb035..5a9abccc1 100644 --- a/core/src/apps/homescreen/__init__.py +++ b/core/src/apps/homescreen/__init__.py @@ -16,27 +16,31 @@ async def busyscreen() -> None: async def homescreen() -> None: + from trezortranslate import TR + if storage.device.is_initialized(): label = storage.device.get_label() else: label = None + # TODO: add notification that translations are out of date + notification = None notification_is_error = False if is_set_any_session(MessageType.AuthorizeCoinJoin): - notification = "COINJOIN AUTHORIZED" + notification = TR.homescreen__title_coinjoin_authorized elif storage.device.is_initialized() and storage.device.no_backup(): - notification = "SEEDLESS" + notification = TR.homescreen__title_seedless notification_is_error = True elif storage.device.is_initialized() and storage.device.unfinished_backup(): - notification = "BACKUP FAILED" + notification = TR.homescreen__title_backup_failed notification_is_error = True elif storage.device.is_initialized() and storage.device.needs_backup(): - notification = "BACKUP NEEDED" + notification = TR.homescreen__title_backup_needed elif storage.device.is_initialized() and not config.has_pin(): - notification = "PIN NOT SET" + notification = TR.homescreen__title_pin_not_set elif storage.device.get_experimental_features(): - notification = "EXPERIMENTAL MODE" + notification = TR.homescreen__title_experimental_mode await Homescreen( label=label, diff --git a/core/src/apps/management/apply_settings.py b/core/src/apps/management/apply_settings.py index 88819750e..46cf8231d 100644 --- a/core/src/apps/management/apply_settings.py +++ b/core/src/apps/management/apply_settings.py @@ -1,9 +1,11 @@ from typing import TYPE_CHECKING +import storage.device as storage_device import trezorui2 from trezor.enums import ButtonRequestType from trezor.ui.layouts import confirm_action from trezor.wire import DataError +from trezortranslate import TR if TYPE_CHECKING: from trezor.enums import SafetyCheckLevel @@ -28,7 +30,6 @@ def _validate_homescreen(homescreen: bytes) -> None: async def apply_settings(msg: ApplySettings) -> Success: - import storage.device as storage_device from trezor.messages import Success from trezor.wire import NotInitialized, ProcessError @@ -37,6 +38,7 @@ async def apply_settings(msg: ApplySettings) -> Success: if not storage_device.is_initialized(): raise NotInitialized("Device is not initialized") + homescreen = msg.homescreen # local_cache_attribute label = msg.label # local_cache_attribute auto_lock_delay_ms = msg.auto_lock_delay_ms # local_cache_attribute @@ -128,20 +130,19 @@ async def _require_confirm_change_label(label: str) -> None: await confirm_single( "set_label", - "Device name", - description="Change device name to {}?", + TR.device_name__title, + description=TR.device_name__change_template, description_param=label, - verb="Change", + verb=TR.buttons__change, ) async def _require_confirm_change_passphrase(use: bool) -> None: - on_or_off = "on" if use else "off" - description = f"Turn {on_or_off} passphrase protection?" - verb = f"Turn {on_or_off}" + description = TR.passphrase__turn_on if use else TR.passphrase__turn_off + verb = TR.buttons__turn_on if use else TR.buttons__turn_off await confirm_action( "set_passphrase", - "Passphrase settings", + TR.passphrase__title_settings, description=description, verb=verb, br_code=BRT_PROTECT_CALL, @@ -152,13 +153,13 @@ async def _require_confirm_change_passphrase_source( passphrase_always_on_device: bool, ) -> None: description = ( - "Do you really want to enter passphrase always on the device?" + TR.passphrase__always_on_device if passphrase_always_on_device - else "Do you want to revoke the passphrase on device setting?" + else TR.passphrase__revoke_on_device ) await confirm_action( "set_passphrase_source", - "Passphrase source", + TR.passphrase__title_source, description=description, br_code=BRT_PROTECT_CALL, ) @@ -166,20 +167,20 @@ async def _require_confirm_change_passphrase_source( async def _require_confirm_change_display_rotation(rotation: int) -> None: if rotation == 0: - label = "north" + label = TR.rotation__north elif rotation == 90: - label = "east" + label = TR.rotation__east elif rotation == 180: - label = "south" + label = TR.rotation__south elif rotation == 270: - label = "west" + label = TR.rotation__west else: raise DataError("Unsupported display rotation") await confirm_action( "set_rotation", - "Change rotation", - description="Do you want to change device rotation to {}?", + TR.rotation__title_change, + description=TR.rotation__change_template, description_param=label, br_code=BRT_PROTECT_CALL, ) @@ -188,11 +189,18 @@ async def _require_confirm_change_display_rotation(rotation: int) -> None: async def _require_confirm_change_autolock_delay(delay_ms: int) -> None: from trezor.strings import format_duration_ms + unit_plurals = { + "millisecond": TR.plurals__lock_after_x_milliseconds, + "second": TR.plurals__lock_after_x_seconds, + "minute": TR.plurals__lock_after_x_minutes, + "hour": TR.plurals__lock_after_x_hours, + } + await confirm_action( "set_autolock_delay", - "Auto-lock delay", - description="Auto-lock your Trezor after {} of inactivity?", - description_param=format_duration_ms(delay_ms), + TR.auto_lock__title, + description=TR.auto_lock__change_template, + description_param=format_duration_ms(delay_ms, unit_plurals), br_code=BRT_PROTECT_CALL, ) @@ -203,26 +211,23 @@ async def _require_confirm_safety_checks(level: SafetyCheckLevel) -> None: if level == SafetyCheckLevel.Strict: await confirm_action( "set_safety_checks", - "Safety checks", - description="Do you really want to enforce strict safety checks (recommended)?", + TR.safety_checks__title, + description=TR.safety_checks__enforce_strict, br_code=BRT_PROTECT_CALL, ) elif level in (SafetyCheckLevel.PromptAlways, SafetyCheckLevel.PromptTemporarily): - # Reusing most stuff for both levels - template = ( - "Trezor will{}allow you to approve some actions which might be unsafe." + description = ( + TR.safety_checks__approve_unsafe_temporary + if level == SafetyCheckLevel.PromptTemporarily + else TR.safety_checks__approve_unsafe_always ) - description = template.format( - " temporarily " if level == SafetyCheckLevel.PromptTemporarily else " " - ) - await confirm_action( "set_safety_checks", - "Safety override", - "Are you sure?", + TR.safety_checks__title_safety_override, + TR.words__are_you_sure, description, hold=True, - verb="Hold to confirm", + verb=TR.buttons__hold_to_confirm, reverse=True, br_code=BRT_PROTECT_CALL, ) @@ -234,9 +239,9 @@ async def _require_confirm_experimental_features(enable: bool) -> None: if enable: await confirm_action( "set_experimental_features", - "Experimental mode", - "Only for development and beta testing!", - "Enable experimental features?", + TR.experimental_mode__title, + TR.experimental_mode__only_for_dev, + TR.experimental_mode__enable, reverse=True, br_code=BRT_PROTECT_CALL, ) @@ -246,7 +251,7 @@ async def _require_confirm_hide_passphrase_from_host(enable: bool) -> None: if enable: await confirm_action( "set_hide_passphrase_from_host", - "Hide passphrase", - description="Hide passphrase coming from host?", + TR.passphrase__title_hide, + description=TR.passphrase__hide, br_code=BRT_PROTECT_CALL, ) diff --git a/core/src/apps/management/authenticate_device.py b/core/src/apps/management/authenticate_device.py index 084289258..b5e33df7f 100644 --- a/core/src/apps/management/authenticate_device.py +++ b/core/src/apps/management/authenticate_device.py @@ -14,6 +14,7 @@ async def authenticate_device(msg: AuthenticateDevice) -> AuthenticityProof: from trezor.ui.layouts import confirm_action from trezor.ui.layouts.progress import progress from trezor.utils import BufferReader, bootloader_locked + from trezortranslate import TR from apps.common.writers import write_compact_size @@ -22,9 +23,9 @@ async def authenticate_device(msg: AuthenticateDevice) -> AuthenticityProof: await confirm_action( "authenticate_device", - "Authenticate device", - description=f"Allow connected computer to confirm your {utils.MODEL_FULL_NAME} is genuine?", - verb="Allow", + TR.authenticate__header, + description=TR.authenticate__confirm_template.format(utils.MODEL_FULL_NAME), + verb=TR.buttons__allow, ) header = b"AuthenticateDevice:" @@ -34,7 +35,7 @@ async def authenticate_device(msg: AuthenticateDevice) -> AuthenticityProof: write_compact_size(h, len(msg.challenge)) h.extend(msg.challenge) - spinner = progress("", description="Checking authenticity...") + spinner = progress("", description=TR.progress__authenticity_check) spinner.report(0) try: diff --git a/core/src/apps/management/change_language.py b/core/src/apps/management/change_language.py new file mode 100644 index 000000000..19511969a --- /dev/null +++ b/core/src/apps/management/change_language.py @@ -0,0 +1,256 @@ +from micropython import const +from typing import TYPE_CHECKING + +from trezor.crypto.hashlib import sha256 +from trezor.wire import DataError + +if TYPE_CHECKING: + from trezor.messages import ChangeLanguage, Success + +_CHUNK_SIZE = const(1024) +_HEADER_SIZE = const(256) +_FILL_BYTE = b"\x00" + + +THRESHOLD = 2 +PUBLIC_KEYS = ( + b"\x43\x34\x99\x63\x43\x62\x3e\x46\x2f\x0f\xc9\x33\x11\xfe\xf1\x48\x4c\xa2\x3d\x2f\xf1\xee\xc6\xdf\x1f\xa8\xeb\x7e\x35\x73\xb3\xdb", + b"\xa9\xa2\x2c\xc2\x65\xa0\xcb\x1d\x6c\xb3\x29\xbc\x0e\x60\xbc\x45\xdf\x76\xb9\xab\x28\xfb\x87\xb6\x11\x36\xfe\xaf\x8d\x8f\xdc\x96", + b"\xb8\xd2\xb2\x1d\xe2\x71\x24\xf0\x51\x1f\x90\x3a\xe7\xe6\x0e\x07\x96\x18\x10\xa0\xb8\xf2\x8e\xa7\x55\xfa\x50\x36\x7a\x8a\x2b\x8b", +) + +if __debug__: + DEV_PUBLIC_KEYS = ( + b"\x68\x46\x0e\xbe\xf3\xb1\x38\x16\x4e\xc7\xfd\x86\x10\xe9\x58\x00\xdf\x75\x98\xf7\x0f\x2f\x2e\xa7\xdb\x51\x72\xac\x74\xeb\xc1\x44", + b"\x8d\x4a\xbe\x07\x4f\xef\x92\x29\xd3\xb4\x41\xdf\xea\x4f\x98\xf8\x05\xb1\xa2\xb3\xa0\x6a\xe6\x45\x81\x0e\xfe\xce\x77\xfd\x50\x44", + b"\x97\xf7\x13\x5a\x9a\x26\x90\xe7\x3b\xeb\x26\x55\x6f\x1c\xb1\x63\xbe\xa2\x53\x2a\xff\xa1\xe7\x78\x24\x30\xbe\x98\xc0\xe5\x68\x12", + ) + + +class TranslationsHeader: + MAGIC = b"TRTR" + VERSION_LEN = 16 + LANG_LEN = 32 + DATA_HASH_LEN = 32 + CHANGE_LANGUAGE_TITLE_LEN = 20 + CHANGE_LANGUAGE_PROMPT_LEN = 40 + SIGNATURE_LEN = 64 + 1 + + def __init__( + self, + raw_data: bytes, + version: str, + language: str, + data_length: int, + translations_length: int, + translations_num: int, + data_hash: bytes, + change_language_title: str, + change_language_prompt: str, + sigmask: int, + signature: bytes, + ): + self.raw_data = raw_data + self.version = version + self.language = language + self.data_length = data_length + self.translations_length = translations_length + self.translations_num = translations_num + self.data_hash = data_hash + self.change_language_title = change_language_title + self.change_language_prompt = change_language_prompt + self.sigmask = sigmask + self.signature = signature + + @classmethod + def from_bytes(cls, data: bytes) -> "TranslationsHeader": + from trezor.utils import BufferReader + + from apps.common import readers + + if len(data) != _HEADER_SIZE: + raise DataError("Invalid header length") + + try: + r = BufferReader(data) + + magic = r.read(len(cls.MAGIC)) + if magic != cls.MAGIC: + raise DataError("Invalid header magic") + + version = r.read(cls.VERSION_LEN).rstrip(_FILL_BYTE).decode() + language = r.read(cls.LANG_LEN).rstrip(_FILL_BYTE).decode() + data_length = readers.read_uint16_le(r) + translations_length = readers.read_uint16_le(r) + translations_num = readers.read_uint16_le(r) + data_hash = r.read(cls.DATA_HASH_LEN) + change_language_title = ( + r.read(cls.CHANGE_LANGUAGE_TITLE_LEN).rstrip(_FILL_BYTE).decode() + ) + change_language_prompt = ( + r.read(cls.CHANGE_LANGUAGE_PROMPT_LEN).rstrip(_FILL_BYTE).decode() + ) + + # Signature occupies last 65 bytes (sigmask + signature itself) + rest = r.read() + if len(rest) < cls.SIGNATURE_LEN: + raise DataError("Invalid header data") + + zeros = rest[: -cls.SIGNATURE_LEN] + signature_part = rest[-cls.SIGNATURE_LEN :] + + sigmask = signature_part[0] + signature = signature_part[1:] + + # Rest must be empty bytes + for b in zeros: + if b != 0: + raise DataError("Invalid header data") + + return cls( + raw_data=data, + language=language, + version=version, + data_length=data_length, + translations_length=translations_length, + translations_num=translations_num, + data_hash=data_hash, + change_language_title=change_language_title, + change_language_prompt=change_language_prompt, + sigmask=sigmask, + signature=signature, + ) + except EOFError: + raise DataError("Invalid header data") + + def version_tuple(self) -> tuple[int, int, int]: + try: + version_parts = self.version.split(".") + major = int(version_parts[0]) + minor = int(version_parts[1]) + patch = int(version_parts[2]) + return major, minor, patch + except (ValueError, IndexError): + raise DataError("Invalid header version") + + def check_signature(self) -> bool: + from trezor.crypto.cosi import verify as cosi_verify + + # Nullifying the signature data themselves + value_to_hash = ( + self.raw_data[: -self.SIGNATURE_LEN] + b"\x00" * self.SIGNATURE_LEN + ) + hasher = sha256() + hasher.update(value_to_hash) + hash: bytes = hasher.digest() + sig_result = cosi_verify( + self.signature, hash, THRESHOLD, PUBLIC_KEYS, self.sigmask + ) + if __debug__: + debug_sig_result = cosi_verify( + self.signature, hash, THRESHOLD, DEV_PUBLIC_KEYS, self.sigmask + ) + sig_result = sig_result or debug_sig_result + return sig_result + + +async def change_language(msg: ChangeLanguage) -> Success: + from trezor import translations, utils + from trezor.messages import Success + from trezor.ui.layouts.progress import progress + + data_length = msg.data_length # local_cache_attribute + + # When empty data, reverting the language to default (english) + if data_length == 0: + await _require_confirm_change_language( + "Change language", "Do you want to change language to English?" + ) + translations.wipe() + return Success(message="Language reverted to default") + + if data_length > translations.data_max_size(): + raise DataError("Translations too long") + if data_length < _HEADER_SIZE: + raise DataError("Translations too short") + + # Getting and parsing the header + header_data = await get_data_chunk(_HEADER_SIZE, 0) + header = TranslationsHeader.from_bytes(header_data[:]) + + # Verifying header information + if header.data_length + _HEADER_SIZE != data_length: + raise DataError("Invalid header data length") + # TODO: how to handle the version updates - numbers have to be bumped in cs.json and others + # (or have this logic in a separate blob-creating tool) + if header.version_tuple() != ( + utils.VERSION_MAJOR, + utils.VERSION_MINOR, + utils.VERSION_PATCH, + ): + raise DataError("Invalid translations version") + + # Verify signature + if not header.check_signature(): + raise DataError("Invalid translations signature") + + # Confirm with user + await _require_confirm_change_language( + header.change_language_title, header.change_language_prompt + ) + + # Show indeterminate loader + progress(None, None, True) + + # Loading all the data at once, so we can verify its fingerprint + # If we saved it gradually to the storage and only checked the fingerprint at the end + # (with the idea of deleting the data if the fingerprint does not match), + # attackers could still write some data into storage and then unplug the device. + blob = utils.empty_bytearray(translations.data_max_size()) + + # Write the header + blob.extend(header_data) + + # Requesting the data in chunks and storing them in the blob + # Also checking the hash of the data for consistency + data_left = data_length - len(header_data) + offset = len(header_data) + hash_writer = utils.HashWriter(sha256()) + while data_left > 0: + data_chunk = await get_data_chunk(data_left, offset) + blob.extend(data_chunk) + hash_writer.write(data_chunk) + data_left -= len(data_chunk) + offset += len(data_chunk) + + # When the data do not match the hash, do not write anything + if hash_writer.get_digest() != header.data_hash: + raise DataError("Invalid data hash") + + translations.wipe() + translations.write(blob, 0) + + return Success(message="Language changed") + + +async def get_data_chunk(data_left: int, offset: int) -> bytes: + from trezor.messages import TranslationDataAck, TranslationDataRequest + from trezor.wire.context import call + + data_length = min(data_left, _CHUNK_SIZE) + req = TranslationDataRequest(data_length=data_length, data_offset=offset) + res = await call(req, TranslationDataAck) + return res.data_chunk + + +async def _require_confirm_change_language(title: str, description: str) -> None: + from trezor.enums import ButtonRequestType + from trezor.ui.layouts import confirm_action + + await confirm_action( + "set_language", + title, + description=description, + verb="OK", # going for an international word, so it does not need translations + br_code=ButtonRequestType.ProtectCall, + ) diff --git a/core/src/apps/management/change_pin.py b/core/src/apps/management/change_pin.py index e16994353..118e63ee5 100644 --- a/core/src/apps/management/change_pin.py +++ b/core/src/apps/management/change_pin.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from trezor import config, wire +from trezortranslate import TR if TYPE_CHECKING: from typing import Awaitable @@ -27,7 +28,7 @@ async def change_pin(msg: ChangePin) -> Success: await _require_confirm_change_pin(msg) # get old pin - curpin, salt = await request_pin_and_sd_salt("Enter PIN") + curpin, salt = await request_pin_and_sd_salt(TR.pin__enter) # if changing pin, pre-check the entered pin before getting new pin if curpin and not msg.remove: @@ -49,13 +50,13 @@ async def change_pin(msg: ChangePin) -> Success: if newpin: if curpin: - msg_screen = "PIN changed." + msg_screen = TR.pin__changed msg_wire = "PIN changed" else: - msg_screen = "PIN protection\nturned on." + msg_screen = TR.pin__enabled msg_wire = "PIN enabled" else: - msg_screen = "PIN protection\nturned off." + msg_screen = TR.pin__disabled msg_wire = "PIN removed" await show_success("success_pin", msg_screen) @@ -67,30 +68,28 @@ def _require_confirm_change_pin(msg: ChangePin) -> Awaitable[None]: has_pin = config.has_pin() - title = "PIN settings" - if msg.remove and has_pin: # removing pin return confirm_action( "disable_pin", - title, - description="Are you sure you want to turn off PIN protection?", - verb="Turn off", + TR.pin__title_settings, + description=TR.pin__turn_off, + verb=TR.buttons__turn_off, ) if not msg.remove and has_pin: # changing pin return confirm_action( "change_pin", - title, - description="Change PIN?", - verb="Change", + TR.pin__title_settings, + description=TR.pin__change, + verb=TR.buttons__change, ) if not msg.remove and not has_pin: # setting new pin return confirm_set_new_pin( "set_pin", - title, - "PIN", - "PIN will be required to access this device.", + TR.pin__title_settings, + TR.pin__turn_on, + TR.pin__info, ) # removing non-existing PIN diff --git a/core/src/apps/management/change_wipe_code.py b/core/src/apps/management/change_wipe_code.py index fb7318b50..88e247d2d 100644 --- a/core/src/apps/management/change_wipe_code.py +++ b/core/src/apps/management/change_wipe_code.py @@ -1,5 +1,7 @@ from typing import TYPE_CHECKING +from trezortranslate import TR + if TYPE_CHECKING: from typing import Awaitable @@ -23,7 +25,7 @@ async def change_wipe_code(msg: ChangeWipeCode) -> Success: await _require_confirm_action(msg, has_wipe_code) # Get the unlocking PIN. - pin, salt = await request_pin_and_sd_salt("Enter PIN") + pin, salt = await request_pin_and_sd_salt(TR.pin__enter) if not msg.remove: # Pre-check the entered PIN. @@ -41,13 +43,13 @@ async def change_wipe_code(msg: ChangeWipeCode) -> Success: if wipe_code: if has_wipe_code: - msg_screen = "Wipe code changed." + msg_screen = TR.wipe_code__changed msg_wire = "Wipe code changed" else: - msg_screen = "Wipe code enabled." + msg_screen = TR.wipe_code__enabled msg_wire = "Wipe code set" else: - msg_screen = "Wipe code disabled." + msg_screen = TR.wipe_code__disabled msg_wire = "Wipe code removed" await show_success("success_wipe_code", msg_screen) @@ -60,30 +62,28 @@ def _require_confirm_action( from trezor.ui.layouts import confirm_action, confirm_set_new_pin from trezor.wire import ProcessError - title = "Wipe code settings" - if msg.remove and has_wipe_code: return confirm_action( "disable_wipe_code", - title, - description="Turn off wipe code protection?", - verb="Turn off", + TR.wipe_code__title_settings, + description=TR.wipe_code__turn_off, + verb=TR.buttons__turn_off, ) if not msg.remove and has_wipe_code: return confirm_action( "change_wipe_code", - title, - description="Change wipe code?", - verb="Change", + TR.wipe_code__title_settings, + description=TR.wipe_code__change, + verb=TR.buttons__change, ) if not msg.remove and not has_wipe_code: return confirm_set_new_pin( "set_wipe_code", - title, - "wipe code", - "Wipe code can be used to erase all data from this device.", + TR.wipe_code__title_settings, + TR.wipe_code__turn_on, + TR.wipe_code__info, ) # Removing non-existing wipe code. @@ -100,12 +100,12 @@ async def _request_wipe_code_confirm(pin: str) -> str: from apps.common.request_pin import request_pin while True: - code1 = await request_pin("Enter new wipe code") + code1 = await request_pin(TR.wipe_code__enter_new) if code1 == pin: await wipe_code_same_as_pin_popup() continue await confirm_reenter_pin(is_wipe_code=True) - code2 = await request_pin("Re-enter wipe code") + code2 = await request_pin(TR.wipe_code__reenter) if code1 == code2: return code1 await pin_mismatch_popup(is_wipe_code=True) diff --git a/core/src/apps/management/get_next_u2f_counter.py b/core/src/apps/management/get_next_u2f_counter.py index 4c79f8e87..b273da9ab 100644 --- a/core/src/apps/management/get_next_u2f_counter.py +++ b/core/src/apps/management/get_next_u2f_counter.py @@ -10,14 +10,15 @@ async def get_next_u2f_counter(msg: GetNextU2FCounter) -> NextU2FCounter: from trezor.messages import NextU2FCounter from trezor.ui.layouts import confirm_action from trezor.wire import NotInitialized + from trezortranslate import TR if not storage_device.is_initialized(): raise NotInitialized("Device is not initialized") await confirm_action( "get_u2f_counter", - "Get U2F counter", - description="Increase and retrieve the U2F counter?", + TR.u2f__title_get, + description=TR.u2f__get, br_code=ButtonRequestType.ProtectCall, ) diff --git a/core/src/apps/management/reboot_to_bootloader.py b/core/src/apps/management/reboot_to_bootloader.py index 1acdcc9ca..fa5db2e08 100644 --- a/core/src/apps/management/reboot_to_bootloader.py +++ b/core/src/apps/management/reboot_to_bootloader.py @@ -14,6 +14,7 @@ async def reboot_to_bootloader(msg: RebootToBootloader) -> NoReturn: from trezor.messages import Success from trezor.ui.layouts import confirm_action, confirm_firmware_update from trezor.wire.context import get_context + from trezortranslate import TR # Bootloader will only allow the INSTALL_UPGRADE flow for official images. # This is to prevent a problematic custom signed firmware from self-updating @@ -49,7 +50,9 @@ async def reboot_to_bootloader(msg: RebootToBootloader) -> NoReturn: version_str = ".".join(map(str, hdr["version"])) await confirm_firmware_update( - description=f"Firmware version {version_str}\nby {hdr['vendor']}", + description=TR.reboot_to_bootloader__version_by_template.format( + version_str, hdr["vendor"] + ), fingerprint=hexlify(hdr["fingerprint"]).decode(), ) boot_command = BootCommand.INSTALL_UPGRADE @@ -58,9 +61,9 @@ async def reboot_to_bootloader(msg: RebootToBootloader) -> NoReturn: else: await confirm_action( "reboot", - "Go to bootloader", - "Trezor will restart in bootloader mode.", - verb="Restart", + TR.reboot_to_bootloader__title, + TR.reboot_to_bootloader__restart, + verb=TR.buttons__restart, ) boot_command = BootCommand.STOP_AND_WAIT boot_args = None diff --git a/core/src/apps/management/recovery_device/__init__.py b/core/src/apps/management/recovery_device/__init__.py index d44c5f57a..b95629afe 100644 --- a/core/src/apps/management/recovery_device/__init__.py +++ b/core/src/apps/management/recovery_device/__init__.py @@ -22,6 +22,7 @@ async def recovery_device(msg: RecoveryDevice) -> Success: from trezor import config, wire, workflow from trezor.enums import ButtonRequestType from trezor.ui.layouts import confirm_action, confirm_reset_device + from trezortranslate import TR from apps.common.request_pin import ( error_pin_invalid, @@ -57,14 +58,14 @@ async def recovery_device(msg: RecoveryDevice) -> Success: # -------------------------------------------------------- # _continue_dialog if not dry_run: - await confirm_reset_device("Recover wallet", recovery=True) + await confirm_reset_device(TR.recovery__title_recover, recovery=True) else: await confirm_action( "confirm_seedcheck", - "Backup check", - description="Check your backup?", + TR.recovery__title_dry_run, + description=TR.recovery__check_dry_run, br_code=ButtonRequestType.ProtectCall, - verb="Check", + verb=TR.buttons__check, ) # END _continue_dialog # -------------------------------------------------------- @@ -75,7 +76,7 @@ async def recovery_device(msg: RecoveryDevice) -> Success: # for dry run pin needs to be entered if dry_run: - curpin, salt = await request_pin_and_sd_salt("Enter PIN") + curpin, salt = await request_pin_and_sd_salt(TR.pin__enter) if not config.check_pin(curpin, salt): await error_pin_invalid() diff --git a/core/src/apps/management/recovery_device/homescreen.py b/core/src/apps/management/recovery_device/homescreen.py index ef44dfe59..440f8b55f 100644 --- a/core/src/apps/management/recovery_device/homescreen.py +++ b/core/src/apps/management/recovery_device/homescreen.py @@ -5,6 +5,7 @@ import storage.recovery as storage_recovery import storage.recovery_shares as storage_recovery_shares from trezor import wire from trezor.messages import Success +from trezortranslate import TR from .. import backup_types from . import layout, recover @@ -68,7 +69,7 @@ async def _continue_recovery_process() -> Success: # For TT, just continuing straight to word count keyboard if utils.INTERNAL_MODEL == "T2B1": await layout.homescreen_dialog( - "Continue", "Select the number of words in your backup." + TR.buttons__continue, TR.recovery__num_of_words ) # ask for the number of words word_count = await layout.request_word_count(dry_run) @@ -158,7 +159,7 @@ async def _finish_recovery(secret: bytes, backup_type: BackupType) -> Success: storage_recovery.end_progress() - await show_success("success_recovery", "Wallet recovered successfully") + await show_success("success_recovery", TR.recovery__wallet_recovered) return Success(message="Device recovered") @@ -189,16 +190,16 @@ async def _request_share_first_screen(word_count: int) -> None: await _request_share_next_screen() else: await layout.homescreen_dialog( - "Enter share", - "Enter any share", - f"({word_count} words)", + TR.buttons__enter_share, + TR.recovery__enter_any_share, + TR.recovery__word_count_template.format(word_count), show_info=True, ) else: # BIP-39 await layout.homescreen_dialog( - "Continue", - "Enter your backup.", - f"({word_count} words)", + TR.buttons__continue, + TR.recovery__enter_backup, + TR.recovery__word_count_template.format(word_count), show_info=True, ) @@ -214,21 +215,24 @@ async def _request_share_next_screen() -> None: if group_count > 1: await layout.homescreen_dialog( - "Enter", - "More shares needed", + TR.buttons__enter, + TR.recovery__more_shares_needed, info_func=_show_remaining_groups_and_shares, ) else: still_needed_shares = remaining[0] already_entered_shares = len(storage_recovery_shares.fetch_group(0)) overall_needed = still_needed_shares + already_entered_shares - entered = ( - f"{already_entered_shares} of {overall_needed} shares entered successfully." + # TODO: consider kwargs in format here + entered = TR.recovery__x_of_y_entered_template.format( + already_entered_shares, overall_needed ) needed = strings.format_plural( - "{count} more {plural} needed.", still_needed_shares, "share" + TR.recovery__x_more_shares_needed_template_plural, + still_needed_shares, + TR.plurals__x_shares_needed, ) - await layout.homescreen_dialog("Enter share", entered, needed) + await layout.homescreen_dialog(TR.buttons__enter_share, entered, needed) async def _show_remaining_groups_and_shares() -> None: diff --git a/core/src/apps/management/recovery_device/layout.py b/core/src/apps/management/recovery_device/layout.py index 0bdf859c4..019d85f20 100644 --- a/core/src/apps/management/recovery_device/layout.py +++ b/core/src/apps/management/recovery_device/layout.py @@ -8,6 +8,7 @@ from trezor.ui.layouts.recovery import ( # noqa: F401 show_recovery_warning, show_remaining_shares, ) +from trezortranslate import TR from .. import backup_types @@ -21,18 +22,19 @@ async def _confirm_abort(dry_run: bool = False) -> None: if dry_run: await confirm_action( "abort_recovery", - "Cancel backup check", - description="Are you sure you want to cancel the backup check?", - verb="CANCEL", + TR.recovery__title_cancel_dry_run, + TR.recovery__cancel_dry_run, + description=TR.recovery__wanna_cancel_dry_run, + verb=TR.buttons__cancel, br_code=ButtonRequestType.ProtectCall, ) else: await confirm_action( "abort_recovery", - "Cancel recovery", - "All progress will be lost.", - "Are you sure you want to cancel the recovery process?", - verb="CANCEL", + TR.recovery__title_cancel_recovery, + TR.recovery__progress_will_be_lost, + TR.recovery__wanna_cancel_recovery, + verb=TR.buttons__cancel, reverse=True, br_code=ButtonRequestType.ProtectCall, ) @@ -61,23 +63,23 @@ async def request_mnemonic( # show_share_already_added await show_recovery_warning( "warning_known_share", - "Share already entered", - "Please enter a different share.", + TR.recovery__share_already_entered, + TR.recovery__enter_different_share, ) return None except word_validity.IdentifierMismatch: # show_identifier_mismatch await show_recovery_warning( "warning_mismatched_share", - "You have entered a share from another Shamir Backup.", + TR.recovery__share_from_another_shamir, ) return None except word_validity.ThresholdReached: # show_group_threshold_reached await show_recovery_warning( "warning_group_threshold", - "Group threshold reached.", - "Enter share from a different group.", + TR.recovery__group_threshold_reached, + TR.recovery__enter_share_from_diff_group, ) return None @@ -89,32 +91,32 @@ async def show_dry_run_result(result: bool, is_slip39: bool) -> None: if result: if is_slip39: - text = "The entered recovery shares are valid and match what is currently in the device." + text = TR.recovery__dry_run_slip39_valid_match else: - text = ( - "The entered recovery seed is valid and matches the one in the device." - ) - await show_success("success_dry_recovery", text, button="Continue") + text = TR.recovery__dry_run_bip39_valid_match + await show_success("success_dry_recovery", text, button=TR.buttons__continue) else: if is_slip39: - text = "The entered recovery shares are valid but do not match what is currently in the device." + text = TR.recovery__dry_run_slip39_valid_mismatch else: - text = "The entered recovery seed is valid but does not match the one in the device." - await show_recovery_warning("warning_dry_recovery", "", text, button="Continue") + text = TR.recovery__dry_run_bip39_valid_mismatch + await show_recovery_warning( + "warning_dry_recovery", "", text, button=TR.buttons__continue + ) async def show_invalid_mnemonic(word_count: int) -> None: if backup_types.is_slip39_word_count(word_count): await show_recovery_warning( "warning_invalid_share", - "Invalid recovery share entered.", - "Please try again", + TR.recovery__invalid_share_entered, + TR.words__please_try_again, ) else: await show_recovery_warning( "warning_invalid_seed", - "Invalid recovery seed entered.", - "Please try again", + TR.recovery__invalid_seed_entered, + TR.words__please_try_again, ) diff --git a/core/src/apps/management/reset_device/__init__.py b/core/src/apps/management/reset_device/__init__.py index 7a58f9554..9693077c4 100644 --- a/core/src/apps/management/reset_device/__init__.py +++ b/core/src/apps/management/reset_device/__init__.py @@ -28,6 +28,7 @@ async def reset_device(msg: ResetDevice) -> Success: from trezor.pin import render_empty_loader from trezor.ui.layouts import confirm_reset_device, prompt_backup from trezor.wire.context import call + from trezortranslate import TR from apps.common.request_pin import request_pin_confirm @@ -38,13 +39,13 @@ async def reset_device(msg: ResetDevice) -> Success: # make sure user knows they're setting up a new wallet if backup_type in (BAK_T_SLIP39_BASIC, BAK_T_SLIP39_ADVANCED): - title = "Create wallet (Shamir)" + title = TR.reset__title_create_wallet_shamir else: - title = "Create wallet" + title = TR.reset__title_create_wallet await confirm_reset_device(title) # Rendering empty loader so users do not feel a freezing screen - render_empty_loader("PROCESSING", "") + render_empty_loader(TR.progress__processing, "") # wipe storage to make sure the device is in a clear state storage.reset() diff --git a/core/src/apps/management/reset_device/layout.py b/core/src/apps/management/reset_device/layout.py index 6653cfee1..d37616f9d 100644 --- a/core/src/apps/management/reset_device/layout.py +++ b/core/src/apps/management/reset_device/layout.py @@ -11,6 +11,7 @@ from trezor.ui.layouts.reset import ( # noqa: F401 slip39_prompt_threshold, slip39_show_checklist, ) +from trezortranslate import TR _NUM_OF_CHOICES = const(3) @@ -20,7 +21,7 @@ async def show_internal_entropy(entropy: bytes) -> None: await confirm_blob( "entropy", - "Internal entropy", + TR.entropy__title, entropy, br_code=ButtonRequestType.ResetDevice, ) @@ -110,23 +111,29 @@ async def _show_confirmation_success( group_index: int | None = None, ) -> None: if share_index is None or num_of_shares is None: # it is a BIP39 backup - subheader = "You have finished verifying your recovery seed." + subheader = TR.reset__finished_verifying_seed text = "" elif share_index == num_of_shares - 1: if group_index is None: - subheader = "You have finished verifying your recovery shares." + subheader = TR.reset__finished_verifying_shares else: - subheader = f"You have finished verifying your recovery shares for group {group_index + 1}." + subheader = TR.reset__finished_verifying_group_template.format( + group_index + 1 + ) text = "" else: if group_index is None: - subheader = f"Recovery share #{share_index + 1} checked successfully." - text = f"Continue with share #{share_index + 2}." + subheader = TR.reset__share_checked_successfully_template.format( + share_index + 1 + ) + text = TR.reset__continue_with_share_template.format(share_index + 2) else: - subheader = f"Group {group_index + 1} - Share {share_index + 1} checked successfully." - text = "Continue with the next share." + subheader = TR.reset__group_share_checked_successfully_template.format( + group_index + 1, share_index + 1 + ) + text = TR.reset__continue_with_next_share return await show_success("success_recovery", text, subheader) @@ -136,9 +143,9 @@ async def _show_confirmation_failure() -> None: await show_reset_warning( "warning_backup_check", - "Please check again", - "Wrong word selected!", - "Check again", + TR.words__please_check_again, + TR.reset__wrong_word_selected, + TR.buttons__check_again, ButtonRequestType.ResetDevice, ) diff --git a/core/src/apps/management/sd_protect.py b/core/src/apps/management/sd_protect.py index 73ba26b26..22c588c63 100644 --- a/core/src/apps/management/sd_protect.py +++ b/core/src/apps/management/sd_protect.py @@ -7,6 +7,7 @@ from trezor.enums import SdProtectOperationType from trezor.messages import Success from trezor.ui.layouts import show_success from trezor.wire import ProcessError +from trezortranslate import TR from apps.common.request_pin import error_pin_invalid, request_pin_and_sd_salt from apps.common.sdcard import ensure_sdcard @@ -67,7 +68,7 @@ async def _sd_protect_enable(msg: SdProtect) -> Success: # Get the current PIN. if config.has_pin(): - pin = await request_pin("Enter PIN", config.get_pin_rem()) + pin = await request_pin(TR.pin__enter, config.get_pin_rem()) else: pin = "" @@ -88,7 +89,7 @@ async def _sd_protect_enable(msg: SdProtect) -> Success: storage_device.set_sd_salt_auth_key(salt_auth_key) - await show_success("success_sd", "You have successfully enabled SD protection.") + await show_success("success_sd", TR.sd_card__enabled) return Success(message="SD card protection enabled") @@ -103,7 +104,7 @@ async def _sd_protect_disable(msg: SdProtect) -> Success: await require_confirm_sd_protect(msg) # Get the current PIN and salt from the SD card. - pin, salt = await request_pin_and_sd_salt("Enter PIN") + pin, salt = await request_pin_and_sd_salt(TR.pin__enter) # Check PIN and remove salt. if not config.change_pin(pin, pin, salt, None): @@ -120,7 +121,7 @@ async def _sd_protect_disable(msg: SdProtect) -> Success: # because overall SD-protection was successfully disabled. pass - await show_success("success_sd", "You have successfully disabled SD protection.") + await show_success("success_sd", TR.sd_card__disabled) return Success(message="SD card protection disabled") @@ -135,7 +136,7 @@ async def _sd_protect_refresh(msg: SdProtect) -> Success: await ensure_sdcard() # Get the current PIN and salt from the SD card. - pin, old_salt = await request_pin_and_sd_salt("Enter PIN") + pin, old_salt = await request_pin_and_sd_salt(TR.pin__enter) # Check PIN and change salt. new_salt, new_auth_key, new_salt_tag = _make_salt() @@ -155,7 +156,7 @@ async def _sd_protect_refresh(msg: SdProtect) -> Success: # SD-protection was successfully refreshed. pass - await show_success("success_sd", "You have successfully refreshed SD protection.") + await show_success("success_sd", TR.sd_card__refreshed) return Success(message="SD card protection refreshed") @@ -163,12 +164,12 @@ def require_confirm_sd_protect(msg: SdProtect) -> Awaitable[None]: from trezor.ui.layouts import confirm_action if msg.operation == SdProtectOperationType.ENABLE: - text = "Do you really want to secure your device with SD card protection?" + text = TR.sd_card__enable elif msg.operation == SdProtectOperationType.DISABLE: - text = "Do you really want to remove SD card protection from your device?" + text = TR.sd_card__disable elif msg.operation == SdProtectOperationType.REFRESH: - text = "Do you really want to replace the current SD card secret with a newly generated one?" + text = TR.sd_card__refresh else: raise ProcessError("Unknown operation") - return confirm_action("set_sd", "SD card protection", description=text) + return confirm_action("set_sd", TR.sd_card__title, description=text) diff --git a/core/src/apps/management/set_u2f_counter.py b/core/src/apps/management/set_u2f_counter.py index e32b30666..e042004f0 100644 --- a/core/src/apps/management/set_u2f_counter.py +++ b/core/src/apps/management/set_u2f_counter.py @@ -10,6 +10,7 @@ async def set_u2f_counter(msg: SetU2FCounter) -> Success: from trezor.enums import ButtonRequestType from trezor.messages import Success from trezor.ui.layouts import confirm_action + from trezortranslate import TR if not storage_device.is_initialized(): raise wire.NotInitialized("Device is not initialized") @@ -18,10 +19,10 @@ async def set_u2f_counter(msg: SetU2FCounter) -> Success: await confirm_action( "set_u2f_counter", - "Set U2F counter", - description="Set the U2F counter to {}?", + TR.u2f__title_set, + description=TR.u2f__set_template, description_param=str(msg.u2f_counter), - verb="SET", + verb=TR.buttons__set, br_code=ButtonRequestType.ProtectCall, ) diff --git a/core/src/apps/management/wipe_device.py b/core/src/apps/management/wipe_device.py index 5a2f3e6ad..166221326 100644 --- a/core/src/apps/management/wipe_device.py +++ b/core/src/apps/management/wipe_device.py @@ -9,16 +9,17 @@ async def wipe_device(msg: WipeDevice) -> Success: from trezor.enums import ButtonRequestType from trezor.messages import Success from trezor.ui.layouts import confirm_action + from trezortranslate import TR from apps.base import reload_settings_from_storage await confirm_action( "confirm_wipe", - "Wipe device", - "All data will be erased.", - "Do you really want to wipe the device?\n", + TR.wipe__title, + TR.wipe__info, + TR.wipe__want_to_wipe, reverse=True, - verb="Hold to confirm", + verb=TR.buttons__hold_to_confirm, hold=True, hold_danger=True, br_code=ButtonRequestType.WipeDevice, diff --git a/core/src/apps/misc/cipher_key_value.py b/core/src/apps/misc/cipher_key_value.py index 3bf22aee3..559107e0a 100644 --- a/core/src/apps/misc/cipher_key_value.py +++ b/core/src/apps/misc/cipher_key_value.py @@ -12,6 +12,7 @@ async def cipher_key_value(msg: CipherKeyValue) -> CipheredKeyValue: from trezor.messages import CipheredKeyValue from trezor.ui.layouts import confirm_action from trezor.wire import DataError + from trezortranslate import TR from apps.common.keychain import get_keychain from apps.common.paths import AlwaysMatchingSchema @@ -26,14 +27,14 @@ async def cipher_key_value(msg: CipherKeyValue) -> CipheredKeyValue: if (encrypt and msg.ask_on_encrypt) or (decrypt and msg.ask_on_decrypt): # Special case for Trezor Suite, which asks for setting up labels if msg.key == "Enable labeling?": - title = "SUITE LABELING" - verb = "ENABLE" + title = TR.misc__title_suite_labeling + verb = TR.buttons__enable else: if encrypt: - title = "Encrypt value" + title = TR.misc__encrypt_value else: - title = "Decrypt value" - verb = "CONFIRM" + title = TR.misc__decrypt_value + verb = TR.buttons__confirm await confirm_action("cipher_key_value", title, description=msg.key, verb=verb) diff --git a/core/src/apps/misc/get_entropy.py b/core/src/apps/misc/get_entropy.py index ae4c41bea..a52d010f1 100644 --- a/core/src/apps/misc/get_entropy.py +++ b/core/src/apps/misc/get_entropy.py @@ -9,12 +9,13 @@ async def get_entropy(msg: GetEntropy) -> Entropy: from trezor.enums import ButtonRequestType from trezor.messages import Entropy from trezor.ui.layouts import confirm_action + from trezortranslate import TR await confirm_action( "get_entropy", - "Confirm entropy", - "Do you really want to send entropy?", - "Continue only if you know what you are doing!", + TR.entropy__title_confirm, + TR.entropy__send, + TR.words__know_what_your_doing, br_code=ButtonRequestType.ProtectCall, ) diff --git a/core/src/apps/monero/layout.py b/core/src/apps/monero/layout.py index a0c2e01e7..1b212b16b 100644 --- a/core/src/apps/monero/layout.py +++ b/core/src/apps/monero/layout.py @@ -7,6 +7,7 @@ from trezor.ui.layouts.progress import ( # noqa: F401 monero_live_refresh_progress, monero_transaction_progress_inner, ) +from trezortranslate import TR DUMMY_PAYMENT_ID = b"\x00\x00\x00\x00\x00\x00\x00\x00" @@ -28,21 +29,23 @@ class MoneroTransactionProgress: def step(self, state: State, step: int, sub_step: int = 0) -> None: if step == 0 or self.inner is None: self.inner = monero_transaction_progress_inner() - info = "Signing..." + info = TR.monero__signing elif step == state.STEP_INP: - info = f"Processing inputs\n{sub_step + 1}/{state.input_count}" + info = f"{TR.monero__processing_inputs}\n{sub_step + 1}/{state.input_count}" elif step == state.STEP_VINI: - info = f"Hashing inputs\n{sub_step + 1}/{state.input_count}" + info = f"{TR.monero__hashing_inputs}n{sub_step + 1}/{state.input_count}" elif step == state.STEP_ALL_IN: - info = "Processing..." + info = TR.monero__processing elif step == state.STEP_OUT: - info = f"Processing outputs\n{sub_step + 1}/{state.output_count}" + info = ( + f"{TR.monero__processing_outputs}\n{sub_step + 1}/{state.output_count}" + ) elif step == state.STEP_ALL_OUT: - info = "Postprocessing..." + info = TR.monero__postprocessing elif step == state.STEP_SIGN: - info = f"Signing inputs\n{sub_step + 1}/{state.input_count}" + info = f"{TR.monero__signing_inputs}\n{sub_step + 1}/{state.input_count}" else: - info = "Processing..." + info = TR.monero__processing state.progress_cur += 1 @@ -59,8 +62,8 @@ def _format_amount(value: int) -> str: async def require_confirm_watchkey() -> None: await confirm_action( "get_watchkey", - "Confirm export", - description="Do you really want to export watch-only credentials?", + TR.monero__confirm_export, + description=TR.monero__wanna_export_watchkey, br_code=BRT_SignTx, ) @@ -68,8 +71,8 @@ async def require_confirm_watchkey() -> None: async def require_confirm_keyimage_sync() -> None: await confirm_action( "key_image_sync", - "Confirm ki sync", - description="Do you really want to\nsync key images?", + TR.monero__confirm_ki_sync, + description=TR.monero__wanna_sync_key_images, br_code=BRT_SignTx, ) @@ -77,21 +80,19 @@ async def require_confirm_keyimage_sync() -> None: async def require_confirm_live_refresh() -> None: await confirm_action( "live_refresh", - "Confirm refresh", - description="Do you really want to\nstart refresh?", + TR.monero__confirm_refresh, + description=TR.monero__wanna_start_refresh, br_code=BRT_SignTx, ) async def require_confirm_tx_key(export_key: bool = False) -> None: description = ( - "Do you really want to export tx_key?" - if export_key - else "Do you really want to export tx_der\nfor tx_proof?" + TR.monero__wanna_export_tx_key if export_key else TR.monero__wanna_export_tx_der ) await confirm_action( "export_tx_key", - "Confirm export", + TR.monero__confirm_export, description=description, br_code=BRT_SignTx, ) @@ -173,7 +174,7 @@ async def _require_confirm_payment_id(payment_id: bytes) -> None: await confirm_blob( "confirm_payment_id", - "Payment ID", + TR.monero__payment_id, payment_id, br_code=BRT_SignTx, ) @@ -182,7 +183,7 @@ async def _require_confirm_payment_id(payment_id: bytes) -> None: async def _require_confirm_fee(fee: int) -> None: await confirm_metadata( "confirm_final", - "Confirm fee", + TR.monero__confirm_fee, "{}", _format_amount(fee), hold=True, @@ -192,8 +193,8 @@ async def _require_confirm_fee(fee: int) -> None: async def _require_confirm_unlock_time(unlock_time: int) -> None: await confirm_metadata( "confirm_locktime", - "Confirm unlock time", - "Unlock time for this transaction is set to {}", + TR.monero__confirm_unlock_time, + TR.monero__unlock_time_set_template, str(unlock_time), BRT_SignTx, ) diff --git a/core/src/apps/nem/layout.py b/core/src/apps/nem/layout.py index 28f3d7e47..ca6f011cd 100644 --- a/core/src/apps/nem/layout.py +++ b/core/src/apps/nem/layout.py @@ -1,6 +1,7 @@ from trezor.enums import ButtonRequestType from trezor.strings import format_amount from trezor.ui.layouts import confirm_metadata +from trezortranslate import TR from .helpers import NEM_MAX_DIVISIBILITY @@ -8,7 +9,7 @@ from .helpers import NEM_MAX_DIVISIBILITY async def require_confirm_text(action: str) -> None: await confirm_metadata( "confirm_nem", - "Confirm action", + TR.nem__confirm_action, action, br_code=ButtonRequestType.ConfirmOutput, ) @@ -17,7 +18,7 @@ async def require_confirm_text(action: str) -> None: async def require_confirm_fee(action: str, fee: int) -> None: await confirm_metadata( "confirm_fee", - "Confirm fee", + TR.nem__confirm_fee, action + "\n{}", f"{format_amount(fee, NEM_MAX_DIVISIBILITY)} XEM", ButtonRequestType.ConfirmOutput, @@ -38,8 +39,8 @@ async def require_confirm_final(fee: int) -> None: # we use SignTx, not ConfirmOutput, for compatibility with T1 await confirm_metadata( "confirm_final", - "Final confirm", - "Sign this transaction\n{}\nfor network fee?", - f"and pay {format_amount(fee, NEM_MAX_DIVISIBILITY)} XEM", + TR.nem__final_confirm, + TR.nem__sign_tx_fee_template, + f"{format_amount(fee, NEM_MAX_DIVISIBILITY)} XEM", hold=True, ) diff --git a/core/src/apps/nem/mosaic/layout.py b/core/src/apps/nem/mosaic/layout.py index 20b7f240b..fc531a6f6 100644 --- a/core/src/apps/nem/mosaic/layout.py +++ b/core/src/apps/nem/mosaic/layout.py @@ -1,5 +1,7 @@ from typing import TYPE_CHECKING +from trezortranslate import TR + from ..layout import require_confirm_content, require_confirm_final if TYPE_CHECKING: @@ -17,12 +19,12 @@ async def ask_mosaic_creation( from ..layout import require_confirm_fee creation_message = [ - ("Create mosaic", creation.definition.mosaic), - ("under namespace", creation.definition.namespace), + (TR.nem__create_mosaic, creation.definition.mosaic), + (TR.nem__under_namespace, creation.definition.namespace), ] - await require_confirm_content("Create mosaic", creation_message) + await require_confirm_content(TR.nem__create_mosaic, creation_message) await _require_confirm_properties(creation.definition) - await require_confirm_fee("Confirm creation fee", creation.fee) + await require_confirm_fee(TR.nem__confirm_creation_fee, creation.fee) await require_confirm_final(common.fee) @@ -35,17 +37,19 @@ async def ask_supply_change( from ..layout import require_confirm_text supply_message = [ - ("Modify supply for", change.mosaic), - ("under namespace", change.namespace), + (TR.nem__modify_supply_for, change.mosaic), + (TR.nem__under_namespace, change.namespace), ] - await require_confirm_content("Supply change", supply_message) + await require_confirm_content(TR.nem__supply_change, supply_message) if change.type == NEMSupplyChangeType.SupplyChange_Decrease: - action = "Decrease" + action = TR.nem__decrease elif change.type == NEMSupplyChangeType.SupplyChange_Increase: - action = "Increase" + action = TR.nem__increase else: raise ValueError("Invalid supply change type") - await require_confirm_text(f"{action} supply by {change.delta} whole units?") + await require_confirm_text( + TR.nem__supply_units_template.format(action, change.delta) + ) await require_confirm_final(common.fee) @@ -59,18 +63,18 @@ async def _require_confirm_properties(definition: NEMMosaicDefinition) -> None: # description if definition.description: - append(("Description:", definition.description)) + append((TR.nem__description, definition.description)) # transferable - transferable = "Yes" if definition.transferable else "No" - append(("Transferable?", transferable)) + transferable = TR.nem__yes if definition.transferable else TR.nem__no + append((TR.nem__transferable, transferable)) # mutable_supply - imm = "mutable" if definition.mutable_supply else "immutable" + imm = TR.nem__mutable if definition.mutable_supply else TR.nem__immutable if definition.supply: - append(("Initial supply:", str(definition.supply) + "\n" + imm)) + append((TR.nem__initial_supply, str(definition.supply) + "\n" + imm)) else: - append(("Initial supply:", imm)) + append((TR.nem__initial_supply, imm)) # levy if definition.levy: @@ -79,23 +83,23 @@ async def _require_confirm_properties(definition: NEMMosaicDefinition) -> None: assert definition.levy_namespace is not None assert definition.levy_mosaic is not None - append(("Levy recipient:", definition.levy_address)) + append((TR.nem__levy_recipient, definition.levy_address)) - append(("Levy fee:", str(definition.fee))) - append(("Levy divisibility:", str(definition.divisibility))) + append((TR.nem__levy_fee, str(definition.fee))) + append((TR.nem__levy_divisibility, str(definition.divisibility))) - append(("Levy namespace:", definition.levy_namespace)) - append(("Levy mosaic:", definition.levy_mosaic)) + append((TR.nem__levy_namespace, definition.levy_namespace)) + append((TR.nem__levy_mosaic, definition.levy_mosaic)) levy_type = ( - "absolute" + TR.nem__absolute if definition.levy == NEMMosaicLevy.MosaicLevy_Absolute - else "percentile" + else TR.nem__percentile ) - append(("Levy type:", levy_type)) + append((TR.nem__levy_type, levy_type)) await confirm_properties( "confirm_properties", - "Confirm properties", + TR.nem__confirm_properties, properties, ) diff --git a/core/src/apps/nem/multisig/layout.py b/core/src/apps/nem/multisig/layout.py index ef49387bb..bd4e28ed8 100644 --- a/core/src/apps/nem/multisig/layout.py +++ b/core/src/apps/nem/multisig/layout.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from trezor.crypto import nem +from trezortranslate import TR if TYPE_CHECKING: from trezor.messages import ( @@ -17,10 +18,10 @@ async def ask_multisig(msg: NEMSignTx) -> None: assert msg.multisig.signer is not None # sign_tx address = nem.compute_address(msg.multisig.signer, msg.transaction.network) if msg.cosigning: - await _require_confirm_address("Cosign transaction for", address) + await _require_confirm_address(TR.nem__cosign_transaction_for, address) else: - await _require_confirm_address("Initiate transaction for", address) - await require_confirm_fee("Confirm multisig fee", msg.transaction.fee) + await _require_confirm_address(TR.nem__initiate_transaction_for, address) + await require_confirm_fee(TR.nem__confirm_multisig_fee, msg.transaction.fee) async def ask_aggregate_modification( @@ -33,21 +34,21 @@ async def ask_aggregate_modification( from ..layout import require_confirm_final, require_confirm_text if not multisig: - await require_confirm_text("Convert account to multisig account?") + await require_confirm_text(TR.nem__convert_account_to_multisig) for m in mod.modifications: if m.type == NEMModificationType.CosignatoryModification_Add: - action = "Add" + action = TR.nem__add else: - action = "Remove" + action = TR.nem__remove address = nem.compute_address(m.public_key, common.network) - await _require_confirm_address(action + " cosignatory", address) + await _require_confirm_address(action + TR.nem__cosignatory, address) if mod.relative_change: if multisig: - action = "Modify the number of cosignatories by " + action = TR.nem__modify_the_number_of_cosignatories_by else: - action = "Set minimum cosignatories to " + action = TR.nem__set_minimum_cosignatories_to await require_confirm_text(action + str(mod.relative_change) + "?") await require_confirm_final(common.fee) @@ -58,7 +59,7 @@ async def _require_confirm_address(action: str, address: str) -> None: from trezor.ui.layouts import confirm_address await confirm_address( - "Confirm address", + TR.nem__confirm_address, address, action, "confirm_multisig", diff --git a/core/src/apps/nem/namespace/layout.py b/core/src/apps/nem/namespace/layout.py index c48f08b1b..b4cb18547 100644 --- a/core/src/apps/nem/namespace/layout.py +++ b/core/src/apps/nem/namespace/layout.py @@ -7,6 +7,8 @@ if TYPE_CHECKING: async def ask_provision_namespace( common: NEMTransactionCommon, namespace: NEMProvisionNamespace ) -> None: + from trezortranslate import TR + from ..layout import ( require_confirm_content, require_confirm_fee, @@ -15,14 +17,14 @@ async def ask_provision_namespace( if namespace.parent: content = [ - ("Create namespace", namespace.namespace), - ("under namespace", namespace.parent), + (TR.nem__create_namespace, namespace.namespace), + (TR.nem__under_namespace, namespace.parent), ] - await require_confirm_content("Confirm namespace", content) + await require_confirm_content(TR.nem__confirm_namespace, content) else: - content = [("Create namespace", namespace.namespace)] - await require_confirm_content("Confirm namespace", content) + content = [(TR.nem__create_namespace, namespace.namespace)] + await require_confirm_content(TR.nem__confirm_namespace, content) - await require_confirm_fee("Confirm rental fee", namespace.fee) + await require_confirm_fee(TR.nem__confirm_rental_fee, namespace.fee) await require_confirm_final(common.fee) diff --git a/core/src/apps/nem/transfer/layout.py b/core/src/apps/nem/transfer/layout.py index d615c4426..20f9676fb 100644 --- a/core/src/apps/nem/transfer/layout.py +++ b/core/src/apps/nem/transfer/layout.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING from trezor.enums import ButtonRequestType from trezor.strings import format_amount +from trezortranslate import TR from ..helpers import NEM_MOSAIC_AMOUNT_DIVISOR from ..layout import require_confirm_final @@ -30,9 +31,9 @@ async def ask_transfer( # require_confirm_payload await confirm_text( "confirm_payload", - "Confirm payload", + TR.nem__confirm_payload, bytes(transfer.payload).decode(), - "Encrypted:" if encrypted else "Unencrypted:", + TR.nem__encrypted if encrypted else TR.nem__unencrypted, ButtonRequestType.ConfirmOutput, ) @@ -67,14 +68,14 @@ async def _ask_transfer_mosaic( if definition: await confirm_properties( "confirm_mosaic", - "Confirm mosaic", + TR.nem__confirm_mosaic, ( ( - "Confirm transfer of", + TR.nem__confirm_transfer_of, format_amount(mosaic_quantity, definition.divisibility) + definition.ticker, ), - ("of", definition.name), + (TR.nem__of, definition.name), ), ) levy = definition.levy # local_cache_attribute @@ -93,25 +94,28 @@ async def _ask_transfer_mosaic( await confirm_properties( "confirm_mosaic_levy", - "Confirm mosaic", - (("Confirm mosaic\nlevy fee of", levy_msg),), + TR.nem__confirm_mosaic, + ((TR.nem__levy_fee_of, levy_msg),), ) else: await confirm_action( "confirm_mosaic_unknown", - "Confirm mosaic", - "Unknown mosaic!", - "Divisibility and levy cannot be shown for unknown mosaics", + TR.nem__confirm_mosaic, + TR.nem__unknown_mosaic, + TR.nem__divisibility_and_levy_cannot_be_shown, br_code=ButtonRequestType.ConfirmOutput, ) await confirm_properties( "confirm_mosaic_transfer", - "Confirm mosaic", + TR.nem__confirm_mosaic, ( - ("Confirm transfer of", f"{mosaic_quantity} raw units"), - ("of", f"{mosaic.namespace}.{mosaic.mosaic}"), + ( + TR.nem__confirm_transfer_of, + TR.nem__raw_units_template.format(mosaic_quantity), + ), + (TR.nem__of, f"{mosaic.namespace}.{mosaic.mosaic}"), ), ) @@ -136,8 +140,8 @@ async def ask_importance_transfer( from ..layout import require_confirm_text if imp.mode == NEMImportanceTransferMode.ImportanceTransfer_Activate: - m = "Activate" + m = TR.nem__activate else: - m = "Deactivate" - await require_confirm_text(m + " remote harvesting?") + m = TR.nem__deactivate + await require_confirm_text(m + TR.nem__remote_harvesting) await require_confirm_final(common.fee) diff --git a/core/src/apps/ripple/layout.py b/core/src/apps/ripple/layout.py index f91934d2a..41f236f93 100644 --- a/core/src/apps/ripple/layout.py +++ b/core/src/apps/ripple/layout.py @@ -13,10 +13,12 @@ async def require_confirm_total(total: int, fee: int) -> None: async def require_confirm_destination_tag(tag: int) -> None: + from trezortranslate import TR + await confirm_metadata( "confirm_destination_tag", - "Confirm tag", - "Destination tag:\n{}", + TR.ripple__confirm_tag, + TR.ripple__destination_tag_template, str(tag), ButtonRequestType.ConfirmOutput, ) diff --git a/core/src/apps/stellar/layout.py b/core/src/apps/stellar/layout.py index 5ede1ff7e..c7cb3c3d0 100644 --- a/core/src/apps/stellar/layout.py +++ b/core/src/apps/stellar/layout.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING import trezor.ui.layouts as layouts from trezor import strings from trezor.enums import ButtonRequestType +from trezortranslate import TR from . import consts @@ -16,9 +17,13 @@ async def require_confirm_init( network_passphrase: str, accounts_match: bool, ) -> None: - description = "Initialize signing with" + " your account" if accounts_match else "" + description = ( + TR.stellar__initialize_signing_with + TR.stellar__your_account + if accounts_match + else "" + ) await layouts.confirm_address( - "Confirm Stellar", + TR.stellar__confirm_stellar, address, description, "confirm_init", @@ -28,15 +33,15 @@ async def require_confirm_init( if network_passphrase == consts.NETWORK_PASSPHRASE_PUBLIC: network = None elif network_passphrase == consts.NETWORK_PASSPHRASE_TESTNET: - network = "testnet network" + network = TR.stellar__testnet_network else: - network = "private network" + network = TR.stellar__private_network if network: await layouts.confirm_metadata( "confirm_init_network", - "Confirm network", - "Transaction is on {}", + TR.stellar__confirm_network, + TR.stellar__on_network_template, network, ButtonRequestType.ConfirmOutput, ) @@ -45,15 +50,19 @@ async def require_confirm_init( async def require_confirm_timebounds(start: int, end: int) -> None: await layouts.confirm_properties( "confirm_timebounds", - "Confirm timebounds", + TR.stellar__confirm_timebounds, ( ( - "Valid from (UTC)", - strings.format_timestamp(start) if start > 0 else "[no restriction]", + TR.stellar__valid_from, + strings.format_timestamp(start) + if start > 0 + else TR.stellar__no_restriction, ), ( - "Valid to (UTC)", - strings.format_timestamp(end) if end > 0 else "[no restriction]", + TR.stellar__valid_to, + strings.format_timestamp(end) + if end > 0 + else TR.stellar__no_restriction, ), ), ) @@ -73,26 +82,33 @@ async def require_confirm_memo(memo_type: StellarMemoType, memo_text: str) -> No else: return await layouts.confirm_action( "confirm_memo", - "Confirm memo", - "No memo set!", - "Important: Many exchanges require a memo when depositing", + TR.stellar__confirm_memo, + TR.stellar__no_memo_set, + TR.stellar__exchanges_require_memo, br_code=ButtonRequestType.ConfirmOutput, ) await layouts.confirm_blob( "confirm_memo", - "Confirm memo", + TR.stellar__confirm_memo, memo_text, description, ) async def require_confirm_final(fee: int, num_operations: int) -> None: - op_str = strings.format_plural("{count} {plural}", num_operations, "operation") + op_str = strings.format_plural( + "{count} {plural}", num_operations, TR.plurals__transaction_of_x_operations + ) + text = ( + TR.stellar__sign_tx_count_template.format(op_str) + + " " + + TR.stellar__sign_tx_fee_template + ) await layouts.confirm_metadata( "confirm_final", - "Final confirm", - "Sign this transaction made up of " + op_str + " and pay {}\nfor fee?", + TR.stellar__final_confirm, + text, format_amount(fee), hold=True, ) diff --git a/core/src/apps/stellar/operations/layout.py b/core/src/apps/stellar/operations/layout.py index 5289ae89e..c0e5516b0 100644 --- a/core/src/apps/stellar/operations/layout.py +++ b/core/src/apps/stellar/operations/layout.py @@ -9,6 +9,7 @@ from trezor.ui.layouts import ( confirm_properties, ) from trezor.wire import DataError, ProcessError +from trezortranslate import TR from ..layout import format_amount @@ -34,9 +35,9 @@ if TYPE_CHECKING: async def confirm_source_account(source_account: str) -> None: await confirm_address( - "Confirm operation", + TR.stellar__confirm_operation, source_account, - "Source account:", + TR.stellar__source_account, "op_source_account", ) @@ -44,19 +45,19 @@ async def confirm_source_account(source_account: str) -> None: async def confirm_allow_trust_op(op: StellarAllowTrustOp) -> None: await confirm_properties( "op_allow_trust", - "Allow trust" if op.is_authorized else "Revoke trust", + TR.stellar__allow_trust if op.is_authorized else TR.stellar__revoke_trust, ( - ("Asset", op.asset_code), - ("Trusted Account", op.trusted_account), + (TR.stellar__asset, op.asset_code), + (TR.stellar__trusted_account, op.trusted_account), ), ) async def confirm_account_merge_op(op: StellarAccountMergeOp) -> None: await confirm_address( - "Account Merge", + TR.stellar__account_merge, op.destination_account, - "All XLM will be sent to:", + TR.stellar__all_will_be_sent_to, "op_account_merge", ) @@ -64,17 +65,17 @@ async def confirm_account_merge_op(op: StellarAccountMergeOp) -> None: async def confirm_bump_sequence_op(op: StellarBumpSequenceOp) -> None: await confirm_metadata( "op_bump", - "Bump Sequence", - "Set sequence to {}?", + TR.stellar__bump_sequence, + TR.stellar__set_sequence_to_template, str(op.bump_to), ) async def confirm_change_trust_op(op: StellarChangeTrustOp) -> None: await confirm_amount( - "Delete trust" if op.limit == 0 else "Add trust", + TR.stellar__delete_trust if op.limit == 0 else TR.stellar__add_trust, format_amount(op.limit, op.asset), - "Limit:", + TR.stellar__limit, "op_change_trust", ) await confirm_asset_issuer(op.asset) @@ -83,10 +84,10 @@ async def confirm_change_trust_op(op: StellarChangeTrustOp) -> None: async def confirm_create_account_op(op: StellarCreateAccountOp) -> None: await confirm_properties( "op_create_account", - "Create Account", + TR.stellar__create_account, ( - ("Account", op.new_account), - ("Initial Balance", format_amount(op.starting_balance)), + (TR.stellar__account, op.new_account), + (TR.stellar__initial_balance, format_amount(op.starting_balance)), ), ) @@ -94,7 +95,11 @@ async def confirm_create_account_op(op: StellarCreateAccountOp) -> None: async def confirm_create_passive_sell_offer_op( op: StellarCreatePassiveSellOfferOp, ) -> None: - text = "Delete Passive Offer" if op.amount == 0 else "New Passive Offer" + text = ( + TR.stellar__delete_passive_offer + if op.amount == 0 + else TR.stellar__new_passive_offer + ) await _confirm_offer(text, op) @@ -110,9 +115,9 @@ async def _confirm_manage_offer_op_common( op: StellarManageBuyOfferOp | StellarManageSellOfferOp, ) -> None: if op.offer_id == 0: - text = "New Offer" + text = TR.stellar__new_offer else: - text = f"{'Delete' if op.amount == 0 else 'Update'} #{op.offer_id}" + text = f"{TR.stellar__delete if op.amount == 0 else TR.stellar__update} #{op.offer_id}" await _confirm_offer(text, op) @@ -130,10 +135,10 @@ async def _confirm_offer( selling_asset = op.selling_asset # local_cache_attribute if StellarManageBuyOfferOp.is_type_of(op): - buying = ("Buying:", format_amount(op.amount, buying_asset)) - selling = ("Selling:", format_asset(selling_asset)) + buying = (TR.stellar__buying, format_amount(op.amount, buying_asset)) + selling = (TR.stellar__selling, format_asset(selling_asset)) price = ( - f"Price per {format_asset(selling_asset)}:", + TR.stellar__price_per_template.format(format_asset(selling_asset)), str(op.price_n / op.price_d), ) await confirm_properties( @@ -142,10 +147,10 @@ async def _confirm_offer( (buying, selling, price), ) else: - selling = ("Selling:", format_amount(op.amount, selling_asset)) - buying = ("Buying:", format_asset(buying_asset)) + selling = (TR.stellar__selling, format_amount(op.amount, selling_asset)) + buying = (TR.stellar__buying, format_asset(buying_asset)) price = ( - f"Price per {format_asset(buying_asset)}:", + TR.stellar__price_per_template.format(format_asset(buying_asset)), str(op.price_n / op.price_d), ) await confirm_properties( @@ -165,14 +170,14 @@ async def confirm_manage_data_op(op: StellarManageDataOp) -> None: digest = sha256(op.value).digest() await confirm_properties( "op_data", - "Set data", - (("Key:", op.key), ("Value (SHA-256):", digest)), + TR.stellar__set_data, + ((TR.stellar__key, op.key), (TR.stellar__value_sha256, digest)), ) else: await confirm_metadata( "op_data", - "Clear data", - "Do you want to clear value key {}?", + TR.stellar__clear_data, + TR.stellar__wanna_clean_value_key_template, op.key, ) @@ -183,14 +188,14 @@ async def confirm_path_payment_strict_receive_op( await confirm_output( op.destination_account, format_amount(op.destination_amount, op.destination_asset), - title="Path Pay", + title=TR.stellar__path_pay, ) await confirm_asset_issuer(op.destination_asset) # confirm what the sender is using to pay await confirm_amount( - "Debited amount", + TR.stellar__debited_amount, format_amount(op.send_max, op.send_asset), - "Pay at most:", + TR.stellar__pay_at_most, "op_path_payment_strict_receive", ) await confirm_asset_issuer(op.send_asset) @@ -202,14 +207,14 @@ async def confirm_path_payment_strict_send_op( await confirm_output( op.destination_account, format_amount(op.destination_min, op.destination_asset), - title="Path Pay at least", + title=TR.stellar__path_pay_at_least, ) await confirm_asset_issuer(op.destination_asset) # confirm what the sender is using to pay await confirm_amount( - "Debited amount", + TR.stellar__debited_amount, format_amount(op.send_amount, op.send_asset), - "Pay:", + TR.stellar__pay, "op_path_payment_strict_send", ) await confirm_asset_issuer(op.send_asset) @@ -231,36 +236,38 @@ async def confirm_set_options_op(op: StellarSetOptionsOp) -> None: if op.inflation_destination_account: await confirm_address( - "Inflation", + TR.stellar__inflation, op.inflation_destination_account, - "Destination:", + TR.stellar__destination, "op_inflation", ) if op.clear_flags: t = _format_flags(op.clear_flags) - await confirm_text("op_set_options", "Clear flags", data=t) + await confirm_text("op_set_options", TR.stellar__clear_flags, data=t) if op.set_flags: t = _format_flags(op.set_flags) - await confirm_text("op_set_options", "Set flags", data=t) + await confirm_text("op_set_options", TR.stellar__set_flags, data=t) thresholds: list[tuple[str, str]] = [] append = thresholds.append # local_cache_attribute if op.master_weight is not None: - append(("Master Weight:", str(op.master_weight))) + append((TR.stellar__master_weight, str(op.master_weight))) if op.low_threshold is not None: - append(("Low:", str(op.low_threshold))) + append((TR.stellar__low, str(op.low_threshold))) if op.medium_threshold is not None: - append(("Medium:", str(op.medium_threshold))) + append((TR.stellar__medium, str(op.medium_threshold))) if op.high_threshold is not None: - append(("High:", str(op.high_threshold))) + append((TR.stellar__high, str(op.high_threshold))) if thresholds: - await confirm_properties("op_thresholds", "Account Thresholds", thresholds) + await confirm_properties( + "op_thresholds", TR.stellar__account_thresholds, thresholds + ) if op.home_domain: - await confirm_text("op_home_domain", "Home Domain", op.home_domain) + await confirm_text("op_home_domain", TR.stellar__home_domain, op.home_domain) signer_type = op.signer_type # local_cache_attribute signer_key = op.signer_key # local_cache_attribute @@ -269,18 +276,18 @@ async def confirm_set_options_op(op: StellarSetOptionsOp) -> None: raise DataError("Stellar: invalid signer option data.") if op.signer_weight > 0: - title = "Add Signer" + title = TR.stellar__add_signer else: - title = "Remove Signer" + title = TR.stellar__remove_signer data: str | bytes = "" if signer_type == StellarSignerType.ACCOUNT: - description = "Account:" + description = f"{TR.stellar__account}:" data = helpers.address_from_public_key(signer_key) elif signer_type == StellarSignerType.PRE_AUTH: - description = "Pre-auth transaction:" + description = TR.stellar__preauth_transaction data = signer_key elif signer_type == StellarSignerType.HASH: - description = "Hash:" + description = TR.stellar__hash data = signer_key else: raise ProcessError("Stellar: invalid signer type") @@ -310,7 +317,7 @@ def _format_flags(flags: int) -> str: if flags > consts.FLAGS_MAX_SIZE: raise ProcessError("Stellar: invalid flags") - flags_set = [] + flags_set: list[str] = [] if flags & consts.FLAG_AUTH_REQUIRED: flags_set.append("AUTH_REQUIRED\n") if flags & consts.FLAG_AUTH_REVOCABLE: @@ -328,8 +335,8 @@ async def confirm_asset_issuer(asset: StellarAsset) -> None: if asset.issuer is None or asset.code is None: raise DataError("Stellar: invalid asset definition") await confirm_address( - "Confirm Issuer", + TR.stellar__confirm_issuer, asset.issuer, - f"{asset.code} issuer:", + TR.stellar__issuer_template.format(asset.code), "confirm_asset_issuer", ) diff --git a/core/src/apps/tezos/layout.py b/core/src/apps/tezos/layout.py index 78f7e2aee..2193a3f31 100644 --- a/core/src/apps/tezos/layout.py +++ b/core/src/apps/tezos/layout.py @@ -1,5 +1,6 @@ from trezor.enums import ButtonRequestType from trezor.ui.layouts import confirm_address, confirm_metadata, confirm_properties +from trezortranslate import TR BR_SIGN_TX = ButtonRequestType.SignTx # global_import_cache @@ -21,15 +22,15 @@ async def require_confirm_fee(value: int, fee: int) -> None: await confirm_total( format_tezos_amount(value), format_tezos_amount(fee), - total_label="Amount:", + total_label=TR.tezos__amount, ) async def require_confirm_origination(address: str) -> None: await confirm_address( - "Confirm origination", + TR.tezos__confirm_origination, address, - "Address:", + TR.tezos__address, "confirm_origination", BR_SIGN_TX, ) @@ -38,10 +39,10 @@ async def require_confirm_origination(address: str) -> None: async def require_confirm_origination_fee(balance: int, fee: int) -> None: await confirm_properties( "confirm_origination_final", - "Confirm origination", + TR.tezos__confirm_origination, ( - ("Balance:", format_tezos_amount(balance)), - ("Fee:", format_tezos_amount(fee)), + (TR.tezos__balance, format_tezos_amount(balance)), + (TR.tezos__fee, format_tezos_amount(fee)), ), hold=True, ) @@ -49,9 +50,9 @@ async def require_confirm_origination_fee(balance: int, fee: int) -> None: async def require_confirm_delegation_baker(baker: str) -> None: await confirm_address( - "Confirm delegation", + TR.tezos__confirm_delegation, baker, - "Baker address:", + TR.tezos__baker_address, "confirm_delegation", BR_SIGN_TX, ) @@ -60,8 +61,8 @@ async def require_confirm_delegation_baker(baker: str) -> None: async def require_confirm_set_delegate(fee: int) -> None: await confirm_metadata( "confirm_delegation_final", - "Confirm delegation", - "Fee:\n{}", + TR.tezos__confirm_delegation, + TR.tezos__fee + "\n{}", format_tezos_amount(fee), BR_SIGN_TX, hold=True, @@ -71,10 +72,10 @@ async def require_confirm_set_delegate(fee: int) -> None: async def require_confirm_register_delegate(address: str, fee: int) -> None: await confirm_properties( "confirm_register_delegate", - "Register delegate", + TR.tezos__register_delegate, ( - ("Fee:", format_tezos_amount(fee)), - ("Address:", address), + (TR.tezos__fee, format_tezos_amount(fee)), + (TR.tezos__address, address), ), hold=True, br_code=BR_SIGN_TX, @@ -93,10 +94,10 @@ def format_tezos_amount(value: int) -> str: async def require_confirm_ballot(proposal: str, ballot: str) -> None: await confirm_properties( "confirm_ballot", - "Submit ballot", + TR.tezos__submit_ballot, ( - ("Ballot:", ballot), - ("Proposal:", proposal), + (TR.tezos__ballot, ballot), + (f"{TR.tezos__proposal}:", proposal), ), hold=True, br_code=BR_SIGN_TX, @@ -106,8 +107,11 @@ async def require_confirm_ballot(proposal: str, ballot: str) -> None: async def require_confirm_proposals(proposals: list[str]) -> None: await confirm_properties( "confirm_proposals", - "Submit proposals" if len(proposals) > 1 else "Submit proposal", - [("Proposal " + str(i), proposal) for i, proposal in enumerate(proposals, 1)], + TR.tezos__submit_proposals if len(proposals) > 1 else TR.tezos__submit_proposal, + [ + (f"{TR.tezos__proposal} " + str(i), proposal) + for i, proposal in enumerate(proposals, 1) + ], hold=True, br_code=BR_SIGN_TX, ) @@ -115,9 +119,9 @@ async def require_confirm_proposals(proposals: list[str]) -> None: async def require_confirm_delegation_manager_withdraw(address: str) -> None: await confirm_address( - "Remove delegation", + TR.tezos__remove_delegation, address, - "Delegator:", + TR.tezos__delegator, "confirm_undelegation", BR_SIGN_TX, ) @@ -126,8 +130,8 @@ async def require_confirm_delegation_manager_withdraw(address: str) -> None: async def require_confirm_manager_remove_delegate(fee: int) -> None: await confirm_metadata( "confirm_undelegation_final", - "Remove delegation", - "Fee:\n{}", + TR.tezos__remove_delegation, + TR.tezos__fee + "\n{}", format_tezos_amount(fee), BR_SIGN_TX, hold=True, diff --git a/core/src/apps/webauthn/add_resident_credential.py b/core/src/apps/webauthn/add_resident_credential.py index 10d15340c..1c260e79f 100644 --- a/core/src/apps/webauthn/add_resident_credential.py +++ b/core/src/apps/webauthn/add_resident_credential.py @@ -10,6 +10,7 @@ async def add_resident_credential(msg: WebAuthnAddResidentCredential) -> Success from trezor.messages import Success from trezor.ui.layouts import show_error_and_raise from trezor.ui.layouts.fido import confirm_fido + from trezortranslate import TR from .credential import Fido2Credential from .resident_credentials import store_resident_credential @@ -24,11 +25,11 @@ async def add_resident_credential(msg: WebAuthnAddResidentCredential) -> Success except Exception: await show_error_and_raise( "warning_credential", - "The credential you are trying to import does\nnot belong to this authenticator.", + TR.fido__does_not_belong, ) await confirm_fido( - "Import credential", + TR.fido__title_import_credential, cred.app_name(), cred.icon_name(), [cred.account_name()], diff --git a/core/src/apps/webauthn/fido2.py b/core/src/apps/webauthn/fido2.py index ad9cc555b..108e36f84 100644 --- a/core/src/apps/webauthn/fido2.py +++ b/core/src/apps/webauthn/fido2.py @@ -9,6 +9,7 @@ from trezor import config, io, log, loop, utils, wire, workflow from trezor.crypto import hashlib from trezor.crypto.curve import nist256p1 from trezor.ui.layouts import show_error_popup +from trezortranslate import TR from apps.base import set_homescreen from apps.common import cbor @@ -616,15 +617,15 @@ async def _confirm_bogus_app(title: str) -> None: if _last_auth_valid: await show_error_popup( title, - "This device is already registered with this application.", - "Already registered.", + TR.fido__device_already_registered, + TR.fido__already_registered, timeout_ms=_POPUP_TIMEOUT_MS, ) else: await show_error_popup( title, - "This device is not registered with this application.", - "Not registered.", + TR.fido__device_not_registered, + TR.fido__not_registered, timeout_ms=_POPUP_TIMEOUT_MS, ) @@ -684,7 +685,7 @@ class U2fConfirmRegister(U2fState): await _confirm_bogus_app("U2F") return False else: - return await _confirm_fido("U2F Register", self._cred) + return await _confirm_fido(TR.fido__title_u2f_register, self._cred) def __eq__(self, other: object) -> bool: return ( @@ -694,7 +695,7 @@ class U2fConfirmRegister(U2fState): class U2fConfirmAuthenticate(U2fState): async def confirm_dialog(self) -> bool: - return await _confirm_fido("U2F Authenticate", self._cred) + return await _confirm_fido(TR.fido__title_u2f_auth, self._cred) def __eq__(self, other: object) -> bool: return ( @@ -803,7 +804,7 @@ class Fido2ConfirmMakeCredential(Fido2State): if self._cred.rp_id == _BOGUS_RP_ID: await _confirm_bogus_app("FIDO2") return True - if not await _confirm_fido("FIDO2 Register", self._cred): + if not await _confirm_fido(TR.fido__title_register, self._cred): return False if self._user_verification: return await verify_user(KeepaliveCallback(self.cid, self.iface)) @@ -839,9 +840,9 @@ class Fido2ConfirmExcluded(Fido2ConfirmMakeCredential): self.finished = True await show_error_popup( - "FIDO2 Register", - "This device is already registered with {}.", - "Already registered.", + TR.fido__title_register, + TR.fido__device_already_registered_with_template, + TR.fido__already_registered, self._cred.rp_id, # description_param timeout_ms=_POPUP_TIMEOUT_MS, ) @@ -869,7 +870,7 @@ class Fido2ConfirmGetAssertion(Fido2State): async def confirm_dialog(self) -> bool: # There is a choice from more than one credential. try: - index = await _confirm_fido_choose("FIDO2 Authenticate", self._creds) + index = await _confirm_fido_choose(TR.fido__title_authenticate, self._creds) except wire.ActionCancelled: return False @@ -922,9 +923,9 @@ class Fido2ConfirmNoPin(State): self.finished = True await show_error_popup( - "FIDO2 Verify User", - "Please enable PIN protection.", - "Unable to verify user.", + TR.fido__title_verify_user, + TR.fido__please_enable_pin_protection, + TR.fido__unable_to_verify_user, timeout_ms=_POPUP_TIMEOUT_MS, ) return False @@ -945,9 +946,9 @@ class Fido2ConfirmNoCredentials(Fido2ConfirmGetAssertion): self.finished = True await show_error_popup( - "FIDO2 Authenticate", - "This device is not registered with\n{}.", - "Not registered.", + TR.fido__title_authenticate, + TR.fido__not_registered_with_template, + TR.fido__not_registered, self._creds[0].app_name(), # description_param timeout_ms=_POPUP_TIMEOUT_MS, ) diff --git a/core/src/apps/webauthn/list_resident_credentials.py b/core/src/apps/webauthn/list_resident_credentials.py index d39fd14a8..120aeee07 100644 --- a/core/src/apps/webauthn/list_resident_credentials.py +++ b/core/src/apps/webauthn/list_resident_credentials.py @@ -9,14 +9,15 @@ async def list_resident_credentials( ) -> WebAuthnCredentials: from trezor.messages import WebAuthnCredential, WebAuthnCredentials from trezor.ui.layouts import confirm_action + from trezortranslate import TR from . import resident_credentials await confirm_action( "credentials_list", - "List credentials", - description="Export information about the credentials stored on this device?", - verb="EXPORT", + TR.fido__title_list_credentials, + description=TR.fido__export_credentials, + verb=TR.buttons__export, ) creds = [ WebAuthnCredential( diff --git a/core/src/apps/webauthn/remove_resident_credential.py b/core/src/apps/webauthn/remove_resident_credential.py index 6c0655cbe..3a6af7dc2 100644 --- a/core/src/apps/webauthn/remove_resident_credential.py +++ b/core/src/apps/webauthn/remove_resident_credential.py @@ -10,6 +10,7 @@ async def remove_resident_credential(msg: WebAuthnRemoveResidentCredential) -> S from trezor import wire from trezor.messages import Success from trezor.ui.layouts.fido import confirm_fido + from trezortranslate import TR from .resident_credentials import get_resident_credential @@ -23,7 +24,7 @@ async def remove_resident_credential(msg: WebAuthnRemoveResidentCredential) -> S raise wire.ProcessError("Invalid credential index.") await confirm_fido( - "Remove credential", + TR.fido__title_remove_credential, cred.app_name(), cred.icon_name(), [cred.account_name()], diff --git a/core/src/apps/workflow_handlers.py b/core/src/apps/workflow_handlers.py index 8b373c4c4..d8d320098 100644 --- a/core/src/apps/workflow_handlers.py +++ b/core/src/apps/workflow_handlers.py @@ -42,6 +42,8 @@ def _find_message_handler_module(msg_type: int) -> str: return "apps.management.recovery_device" if msg_type == MessageType.ApplySettings: return "apps.management.apply_settings" + if msg_type == MessageType.ChangeLanguage: + return "apps.management.change_language" if msg_type == MessageType.ApplyFlags: return "apps.management.apply_flags" if msg_type == MessageType.ChangePin: diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index 5c210d0a1..6b67595b4 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -50,6 +50,9 @@ ShowDeviceTutorial = 95 UnlockBootloader = 96 AuthenticateDevice = 97 AuthenticityProof = 98 +ChangeLanguage = 990 +TranslationDataRequest = 991 +TranslationDataAck = 992 FirmwareErase = 6 FirmwareUpload = 7 FirmwareRequest = 8 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index 93daa4533..bf45a7540 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -67,6 +67,9 @@ if TYPE_CHECKING: UnlockBootloader = 96 AuthenticateDevice = 97 AuthenticityProof = 98 + ChangeLanguage = 990 + TranslationDataRequest = 991 + TranslationDataAck = 992 SetU2FCounter = 63 GetNextU2FCounter = 80 NextU2FCounter = 81 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index e8aabef61..55b65f770 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -2249,6 +2249,50 @@ if TYPE_CHECKING: def is_type_of(cls, msg: Any) -> TypeGuard["ApplySettings"]: return isinstance(msg, cls) + class ChangeLanguage(protobuf.MessageType): + data_length: "int" + + def __init__( + self, + *, + data_length: "int", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["ChangeLanguage"]: + return isinstance(msg, cls) + + class TranslationDataRequest(protobuf.MessageType): + data_length: "int" + data_offset: "int" + + def __init__( + self, + *, + data_length: "int", + data_offset: "int", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["TranslationDataRequest"]: + return isinstance(msg, cls) + + class TranslationDataAck(protobuf.MessageType): + data_chunk: "bytes" + + def __init__( + self, + *, + data_chunk: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["TranslationDataAck"]: + return isinstance(msg, cls) + class ApplyFlags(protobuf.MessageType): flags: "int" diff --git a/core/src/trezor/pin.py b/core/src/trezor/pin.py index 98e3e8ed7..13322e9d0 100644 --- a/core/src/trezor/pin.py +++ b/core/src/trezor/pin.py @@ -16,6 +16,7 @@ _ignore_loader_messages: tuple[str, ...] = () def ignore_nonpin_loader_messages() -> None: global _ignore_loader_messages + # TODO: handle translation of those (in C) _ignore_loader_messages = ("Processing", "Starting up") @@ -39,6 +40,7 @@ def render_empty_loader(message: str, description: str) -> None: def show_pin_timeout(seconds: int, progress: int, message: str) -> bool: from trezor.ui.layouts.progress import pin_progress + from trezortranslate import TR # Possibility to ignore certain messages - not showing loader for them if message in _ignore_loader_messages: @@ -57,11 +59,11 @@ def show_pin_timeout(seconds: int, progress: int, message: str) -> bool: if seconds != _previous_seconds: if seconds == 0: - remaining = "Done" + remaining = TR.progress__done elif seconds == 1: - remaining = "1 second left" + remaining = TR.progress__one_second_left else: - remaining = f"{seconds} seconds left" + remaining = TR.progress__x_seconds_left_template.format(seconds) _previous_remaining = remaining _previous_seconds = seconds else: diff --git a/core/src/trezor/strings.py b/core/src/trezor/strings.py index c26414c5b..5f833b46e 100644 --- a/core/src/trezor/strings.py +++ b/core/src/trezor/strings.py @@ -27,17 +27,17 @@ def format_ordinal(number: int) -> str: ) -def format_plural(string: str, count: int, plural: str) -> str: +def format_plural_english(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") + >>> format_plural_english("We need {count} more {plural}", 3, "share") 'We need 3 more shares' - >>> format_plural("We need {count} more {plural}", 1, "share") + >>> format_plural_english("We need {count} more {plural}", 1, "share") 'We need 1 more share' - >>> format_plural("{count} {plural}", 4, "candy") + >>> format_plural_english("{count} {plural}", 4, "candy") '4 candies' """ if not all(s in string for s in ("{count}", "{plural}")): @@ -56,20 +56,49 @@ def format_plural(string: str, count: int, plural: str) -> str: return string.format(count=count, plural=plural) -def format_duration_ms(milliseconds: int) -> str: +def format_plural(string: str, count: int, plurals: str) -> str: + """ + Adds plural form to a string based on `count`. + """ + if not all(s in string for s in ("{count}", "{plural}")): + # string needs to have {count} and {plural} inside + raise ValueError + + plural_options = plurals.split("|") + if len(plural_options) not in (2, 3): + # plurals need to have 2 or 3 options + raise ValueError + + # First one is for singular, last one is for MANY (or zero) + if count == 1: + plural = plural_options[0] + else: + plural = plural_options[-1] + + # In case there are three options, the middle one is for FEW (2-4) + if len(plural_options) == 3: + # TODO: this is valid for czech - are there some other languages that have it differently? + # In that case we need to add language-specific rules + if 1 < count < 5: + plural = plural_options[1] + + return string.format(count=count, plural=plural) + + +def format_duration_ms(milliseconds: int, unit_plurals: dict[str, str]) -> str: """ Returns human-friendly representation of a duration. Truncates all decimals. """ - units = ( - ("hour", 60 * 60 * 1000), - ("minute", 60 * 1000), - ("second", 1000), + units: tuple[tuple[str, int], ...] = ( + (unit_plurals["hour"], 60 * 60 * 1000), + (unit_plurals["minute"], 60 * 1000), + (unit_plurals["second"], 1000), ) for unit, divisor in units: if milliseconds >= divisor: break else: - unit = "millisecond" + unit = unit_plurals["millisecond"] divisor = 1 return format_plural("{count} {plural}", milliseconds // divisor, unit) diff --git a/core/src/trezor/translations.py b/core/src/trezor/translations.py new file mode 100644 index 000000000..fec898197 --- /dev/null +++ b/core/src/trezor/translations.py @@ -0,0 +1,29 @@ +from trezor import config + +DEFAULT_LANGUAGE = "en-US" + + +def get_language() -> str: + from trezortranslate import language_name + + translation_lang = language_name() + if translation_lang: + return translation_lang + return DEFAULT_LANGUAGE + + +def write(data: bytes | bytearray, offset: int) -> None: + from trezor import wire + + if offset + len(data) > data_max_size(): + raise wire.DataError("Language data too long") + + config.translations_set(data, offset) + + +def wipe() -> None: + config.translations_wipe() + + +def data_max_size() -> int: + return config.translations_max_bytesize() diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index fa4ccb2aa..379975534 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -5,6 +5,7 @@ from trezor import io, loop, ui from trezor.enums import ButtonRequestType from trezor.wire import ActionCancelled from trezor.wire.context import wait as ctx_wait +from trezortranslate import TR from ..common import button_request, interact @@ -263,11 +264,12 @@ async def _placeholder_confirm( data: str | None = None, description: str | None = None, *, - verb: str = "CONFIRM", + verb: str | None = None, verb_cancel: str | None = "", hold: bool = False, br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> Any: + verb = verb or TR.buttons__confirm # def_arg return await confirm_action( br_type, title.upper(), @@ -286,11 +288,12 @@ async def get_bool( title: str, data: str | None = None, description: str | None = None, - verb: str = "CONFIRM", + verb: str | None = None, verb_cancel: str | None = "", hold: bool = False, br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> bool: + verb = verb or TR.buttons__confirm # def_arg result = await interact( RustLayout( trezorui2.confirm_action( @@ -322,7 +325,7 @@ async def confirm_action( action: str | None = None, description: str | None = None, description_param: str | None = None, - verb: str = "CONFIRM", + verb: str | None = None, verb_cancel: str | None = "", hold: bool = False, hold_danger: bool = False, @@ -330,6 +333,7 @@ async def confirm_action( exc: ExceptionType = ActionCancelled, br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> None: + verb = verb or TR.buttons__confirm # def_arg if verb_cancel is not None: verb_cancel = verb_cancel.upper() @@ -369,7 +373,7 @@ async def confirm_single( br_type, title, description=begin + description_param + end, - verb=verb or "CONFIRM", + verb=verb or TR.buttons__confirm, br_code=ButtonRequestType.ProtectCall, ) @@ -379,9 +383,9 @@ async def confirm_reset_device( recovery: bool = False, ) -> None: if recovery: - button = "RECOVER WALLET" + button = TR.reset__button_recover else: - button = "CREATE WALLET" + button = TR.reset__button_create await raise_if_not_confirmed( interact( @@ -413,10 +417,10 @@ async def prompt_backup() -> bool: return await get_bool( br_type, - "SKIP BACKUP", - description="Are you sure you want to skip the backup?", - verb="BACK UP", - verb_cancel="SKIP", + TR.backup__title_skip, + description=TR.backup__want_to_skip, + verb=TR.buttons__back_up, + verb_cancel=TR.buttons__skip, br_code=br_code, ) @@ -425,10 +429,7 @@ async def confirm_path_warning( path: str, path_type: str | None = None, ) -> None: - if path_type: - title = f"Unknown {path_type}" - else: - title = "Unknown path" + title = f"{TR.words__unknown} {path_type if path_type else 'path'}" return await _placeholder_confirm( "path_warning", title.upper(), @@ -444,7 +445,7 @@ async def confirm_homescreen( interact( RustLayout( trezorui2.confirm_homescreen( - title="CHANGE HOMESCREEN?", + title=TR.homescreen__title_set, image=image, ) ), @@ -465,19 +466,18 @@ async def show_address( network: str | None = None, multisig_index: int | None = None, xpubs: Sequence[str] = (), - mismatch_title: str = "ADDRESS MISMATCH?", + mismatch_title: str | None = None, br_type: str = "show_address", br_code: ButtonRequestType = ButtonRequestType.Address, chunkify: bool = False, ) -> None: + mismatch_title = mismatch_title or TR.addr_mismatch__title # def_arg send_button_request = True if title is None: # Will be a marquee in case of multisig - title = ( - "RECEIVE ADDRESS (MULTISIG)" - if multisig_index is not None - else "RECEIVE ADDRESS" - ) + title = TR.address__title_receive_address + if multisig_index is not None: + title = f"{title} (MULTISIG)" while True: layout = RustLayout( trezorui2.confirm_address( @@ -508,7 +508,11 @@ async def show_address( def xpub_title(i: int) -> str: # Will be marquee (cannot fit one line) result = f"MULTISIG XPUB #{i + 1}" - result += " (YOURS)" if i == multisig_index else " (COSIGNER)" + result += ( + f" ({TR.address__title_yours})" + if i == multisig_index + else f" ({TR.address__title_cosigner})" + ) return result result = await ctx_wait( @@ -540,13 +544,15 @@ async def show_address( def show_pubkey( pubkey: str, - title: str = "Public key", + title: str | None = None, *, account: str | None = None, path: str | None = None, - mismatch_title: str = "KEY MISMATCH?", - br_type="show_pubkey", + mismatch_title: str | None = None, + br_type: str = "show_pubkey", ) -> Awaitable[None]: + title = title or TR.address__public_key # def_arg + mismatch_title = mismatch_title or TR.addr_mismatch__title_key_mismatch # def_arg return show_address( address=pubkey, title=title.upper(), @@ -585,9 +591,10 @@ async def show_error_and_raise( br_type: str, content: str, subheader: str | None = None, - button: str = "TRY AGAIN", + button: str | None = None, exc: ExceptionType = ActionCancelled, ) -> NoReturn: + button = button or TR.buttons__try_again # def_arg await show_warning( br_type, subheader or "", @@ -602,9 +609,10 @@ async def show_warning( br_type: str, content: str, subheader: str | None = None, - button: str = "CONTINUE", + button: str | None = None, br_code: ButtonRequestType = ButtonRequestType.Warning, ) -> None: + button = button or TR.buttons__continue # def_arg await interact( RustLayout( trezorui2.show_warning( # type: ignore [Argument missing for parameter "title"] @@ -622,9 +630,10 @@ def show_success( br_type: str, content: str, subheader: str | None = None, - button: str = "Continue", + button: str | None = None, ) -> Awaitable[None]: - title = "Success" + button = button or TR.buttons__continue # def_arg + title = TR.words__title_success # In case only subheader is supplied, showing it # in regular font, not bold. @@ -634,7 +643,7 @@ def show_success( # Special case for Shamir backup - to show everything just on one page # in regular font. - if "Continue with" in content: + if TR.words__continue_with in content: content = f"{subheader}\n\n{content}" subheader = None title = "" @@ -653,17 +662,20 @@ def show_success( async def confirm_output( address: str, amount: str, - title: str = "Confirm sending", + title: str | None = None, hold: bool = False, br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, address_label: str | None = None, output_index: int | None = None, chunkify: bool = False, ) -> None: - address_title = ( - "RECIPIENT" if output_index is None else f"RECIPIENT #{output_index + 1}" - ) - amount_title = "AMOUNT" if output_index is None else f"AMOUNT #{output_index + 1}" + title = title or TR.send__confirm_sending # def_arg + address_title = TR.send__title_recipient + if output_index is not None: + address_title += f" #{output_index + 1}" + amount_title = TR.send__title_amount + if output_index is not None: + amount_title += f" #{output_index + 1}" while True: result = await interact( @@ -716,7 +728,7 @@ async def confirm_payment_request( memos_str = "\n".join(memos) return await _placeholder_confirm( "confirm_payment_request", - "CONFIRM SENDING", + TR.send__title_confirm_sending, description=f"{amount} to\n{recipient_name}\n{memos_str}", br_code=ButtonRequestType.ConfirmOutput, ) @@ -725,7 +737,7 @@ async def confirm_payment_request( async def should_show_more( title: str, para: Iterable[tuple[int, str]], - button_text: str = "Show all", + button_text: str | None = None, br_type: str = "should_show_more", br_code: ButtonRequestType = BR_TYPE_OTHER, confirm: str | bytes | None = None, @@ -736,8 +748,9 @@ async def should_show_more( Raises ActionCancelled if the user cancels. """ + button_text = button_text or TR.buttons__show_all # def_arg if confirm is None or not isinstance(confirm, str): - confirm = "CONFIRM" + confirm = TR.buttons__confirm result = await interact( RustLayout( @@ -766,14 +779,15 @@ async def confirm_blob( br_type: str, title: str, data: bytes | str, - description: str = "", - verb: str = "CONFIRM", + description: str | None = None, + verb: str | None = None, verb_cancel: str | None = "", # icon hold: bool = False, br_code: ButtonRequestType = BR_TYPE_OTHER, ask_pagination: bool = False, chunkify: bool = False, ) -> None: + verb = verb or TR.buttons__confirm # def_arg title = title.upper() layout = RustLayout( trezorui2.confirm_blob( @@ -791,7 +805,7 @@ async def confirm_blob( if ask_pagination and layout.page_count() > 1: assert not hold await _confirm_ask_pagination( - br_type, title, data, description, verb_cancel, br_code + br_type, title, data, description or "", verb_cancel, br_code ) else: @@ -832,7 +846,7 @@ async def _confirm_ask_pagination( paginated = RustLayout( trezorui2.confirm_more( title=title, - button="GO BACK", + button=TR.buttons__go_back, items=[(ui.BOLD, f"Size: {len(data)} bytes"), (ui.MONO, data)], ) ) @@ -848,10 +862,11 @@ async def _confirm_ask_pagination( def confirm_address( title: str, address: str, - description: str = "Address:", + description: str | None = None, br_type: str = "confirm_address", br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> Awaitable[None]: + description = description or TR.address__address # def_arg return confirm_blob( br_type, title.upper(), @@ -880,10 +895,11 @@ async def confirm_text( def confirm_amount( title: str, amount: str, - description: str = "Amount:", + description: str | None = None, br_type: str = "confirm_amount", br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> Awaitable[None]: + description = description or TR.send__amount # def_arg return confirm_blob( br_type, title.upper(), @@ -953,7 +969,7 @@ async def confirm_value( title=title.upper(), description=description, value=value, - verb=verb or "HOLD TO CONFIRM", + verb=verb or TR.buttons__hold_to_confirm, hold=hold, ) ), @@ -1013,13 +1029,15 @@ async def confirm_total( total_amount: str, fee_amount: str, fee_rate_amount: str | None = None, - title: str = "SENDING", - total_label: str = "Total amount:", - fee_label: str = "Including fee:", + title: str | None = None, + total_label: str | None = None, + fee_label: str | None = None, account_label: str | None = None, br_type: str = "confirm_total", br_code: ButtonRequestType = ButtonRequestType.SignTx, ) -> None: + total_label = total_label or TR.send__total_amount # def_arg + fee_label = fee_label or TR.send__including_fee # def_arg await raise_if_not_confirmed( interact( RustLayout( @@ -1145,10 +1163,10 @@ async def confirm_replacement(description: str, txid: str) -> None: await confirm_value( description.upper(), txid, - "Transaction ID:", + TR.send__transaction_id, "confirm_replacement", ButtonRequestType.SignTx, - verb="CONTINUE", + verb=TR.buttons__continue, ) @@ -1251,7 +1269,7 @@ async def confirm_sign_identity( await _placeholder_confirm( "confirm_sign_identity", - f"Sign {proto}".upper(), + f"{TR.words__sign} {proto}".upper(), text, br_code=BR_TYPE_OTHER, ) @@ -1271,16 +1289,16 @@ async def confirm_signverify( while True: await confirm_blob( br_type, - "SIGNING ADDRESS", + TR.sign_message__confirm_address, address, - verb="CONTINUE", + verb=TR.buttons__continue, br_code=BR_TYPE_OTHER, ) try: await confirm_blob( br_type, - "CONFIRM MESSAGE", + TR.sign_message__confirm_message, message, verb_cancel="^", br_code=BR_TYPE_OTHER, @@ -1323,7 +1341,7 @@ async def request_passphrase_on_device(max_len: int) -> str: result = await interact( RustLayout( trezorui2.request_passphrase( - prompt="ENTER PASSPHRASE", + prompt=TR.passphrase__title_enter, max_len=max_len, ) ), @@ -1350,9 +1368,9 @@ async def request_pin_on_device( if attempts_remaining is None or attempts_remaining == 16: subprompt = "" elif attempts_remaining == 1: - subprompt = "Last attempt" + subprompt = TR.pin__last_attempt else: - subprompt = f"{attempts_remaining} tries left" + subprompt = f"{attempts_remaining} {TR.pin__tries_left}" result = await interact( RustLayout( @@ -1377,19 +1395,21 @@ async def confirm_reenter_pin( is_wipe_code: bool = False, ) -> None: br_type = "reenter_wipe_code" if is_wipe_code else "reenter_pin" - title = "CHECK WIPE CODE" if is_wipe_code else "CHECK PIN" - description = "wipe code" if is_wipe_code else "PIN" + title = TR.wipe_code__title_check if is_wipe_code else TR.pin__title_check_pin + description = ( + TR.wipe_code__reenter_to_confirm if is_wipe_code else TR.pin__reenter_to_confirm + ) return await confirm_action( br_type, title, - description=f"Please re-enter {description} to confirm.", - verb="CONTINUE", + description=description, + verb=TR.buttons__continue, verb_cancel=None, br_code=BR_TYPE_OTHER, ) -async def confirm_multiple_pages_texts( +async def _confirm_multiple_pages_texts( br_type: str, title: str, items: list[str], @@ -1414,13 +1434,13 @@ async def confirm_multiple_pages_texts( async def pin_mismatch_popup( is_wipe_code: bool = False, ) -> None: - description = "wipe codes" if is_wipe_code else "PINs" + description = TR.wipe_code__mismatch if is_wipe_code else TR.pin__mismatch br_code = "wipe_code_mismatch" if is_wipe_code else "pin_mismatch" return await show_warning( br_code, - f"Entered {description} do not match!", - "Please check again.", - "CHECK AGAIN", + description, + TR.pin__please_check_again, + TR.buttons__check_again, BR_TYPE_OTHER, ) @@ -1428,9 +1448,9 @@ async def pin_mismatch_popup( async def wipe_code_same_as_pin_popup() -> None: return await confirm_action( "wipe_code_same_as_pin", - "INVALID WIPE CODE", - description="The wipe code must be different from your PIN.\nPlease try again.", - verb="TRY AGAIN", + TR.wipe_code__title_invalid, + description=TR.wipe_code__diff_from_pin, + verb=TR.buttons__try_again, verb_cancel=None, br_code=BR_TYPE_OTHER, ) @@ -1443,12 +1463,11 @@ async def confirm_set_new_pin( information: str, br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> None: - question = f"Turn on {description} protection?" - await confirm_multiple_pages_texts( + await _confirm_multiple_pages_texts( br_type, title.upper(), - [question, information], - "TURN ON", + [description, information], + TR.buttons__turn_on, br_code, ) @@ -1458,14 +1477,14 @@ async def confirm_set_new_pin( # Additional information for the user to know about PIN next_info = [ - "PIN should be 4-50 digits long.", - "Position of the cursor will change between entries for enhanced security.", + TR.pin__should_be_long, + TR.pin__cursor_will_change, ] - await confirm_multiple_pages_texts( + await _confirm_multiple_pages_texts( br_type, title.upper(), next_info, - "CONTINUE", + TR.buttons__continue, br_code, ) diff --git a/core/src/trezor/ui/layouts/tr/fido.py b/core/src/trezor/ui/layouts/tr/fido.py index 17b66032b..a290c7f52 100644 --- a/core/src/trezor/ui/layouts/tr/fido.py +++ b/core/src/trezor/ui/layouts/tr/fido.py @@ -38,13 +38,15 @@ async def confirm_fido( async def confirm_fido_reset() -> bool: + from trezortranslate import TR + confirm = RustLayout( trezorui2.confirm_action( - title="FIDO2 RESET", - description="Do you really want to erase all credentials?", + title=TR.fido__title_reset, + description=TR.fido__wanna_erase_credentials, action=None, verb_cancel="", - verb="CONFIRM", + verb=TR.buttons__confirm, ) ) return (await confirm) is trezorui2.CONFIRMED diff --git a/core/src/trezor/ui/layouts/tr/homescreen.py b/core/src/trezor/ui/layouts/tr/homescreen.py index a58966430..5192d46e8 100644 --- a/core/src/trezor/ui/layouts/tr/homescreen.py +++ b/core/src/trezor/ui/layouts/tr/homescreen.py @@ -43,6 +43,7 @@ class Homescreen(HomescreenBase): level = 1 if notification is not None: notification = notification.rstrip("!") + # TODO: handle translation of EXPERIMENTAL if "EXPERIMENTAL" in notification: level = 2 elif notification_is_error: @@ -106,10 +107,12 @@ class Busyscreen(HomescreenBase): RENDER_INDICATOR = storage_cache.BUSYSCREEN_ON def __init__(self, delay_ms: int) -> None: + from trezortranslate import TR + skip = storage_cache.homescreen_shown is self.RENDER_INDICATOR super().__init__( layout=trezorui2.show_progress_coinjoin( - title="Waiting for others", + title=TR.coinjoin__waiting_for_others, indeterminate=True, time_ms=delay_ms, skip_first_paint=skip, diff --git a/core/src/trezor/ui/layouts/tr/progress.py b/core/src/trezor/ui/layouts/tr/progress.py index 5cc4331e8..6644ad70a 100644 --- a/core/src/trezor/ui/layouts/tr/progress.py +++ b/core/src/trezor/ui/layouts/tr/progress.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING import trezorui2 from trezor import ui +from trezortranslate import TR if TYPE_CHECKING: from typing import Any @@ -29,10 +30,11 @@ class RustProgress: def progress( - message: str = "PLEASE WAIT", + message: str | None = None, description: str | None = None, indeterminate: bool = False, ) -> ProgressLayout: + message = message or TR.progress__please_wait # def_arg return RustProgress( layout=trezorui2.show_progress( title=message.upper(), @@ -57,12 +59,12 @@ def pin_progress(message: str, description: str) -> ProgressLayout: def monero_keyimage_sync_progress() -> ProgressLayout: - return progress("", "Syncing...") + return progress("", TR.progress__syncing) def monero_live_refresh_progress() -> ProgressLayout: - return progress("", "Refreshing...", indeterminate=True) + return progress("", TR.progress__refreshing, indeterminate=True) def monero_transaction_progress_inner() -> ProgressLayout: - return progress("", "Signing transaction...") + return progress("", TR.progress__signing_transaction) diff --git a/core/src/trezor/ui/layouts/tr/recovery.py b/core/src/trezor/ui/layouts/tr/recovery.py index a42280765..53b0b2b8c 100644 --- a/core/src/trezor/ui/layouts/tr/recovery.py +++ b/core/src/trezor/ui/layouts/tr/recovery.py @@ -2,6 +2,7 @@ from typing import Callable, Iterable import trezorui2 from trezor.enums import ButtonRequestType +from trezortranslate import TR from ..common import interact from . import RustLayout, raise_if_not_confirmed, show_warning @@ -20,7 +21,7 @@ async def request_word_count(dry_run: bool) -> int: async def request_word(word_index: int, word_count: int, is_slip39: bool) -> str: from trezor.wire.context import wait - prompt = f"WORD {word_index + 1} OF {word_count}" + prompt = TR.recovery__word_x_of_y_template.format(word_index + 1, word_count) if is_slip39: word_choice = RustLayout(trezorui2.request_slip39(prompt=prompt)) @@ -45,10 +46,10 @@ async def show_group_share_success(share_index: int, group_index: int) -> None: RustLayout( trezorui2.show_group_share_success( lines=[ - "You have entered", - f"Share {share_index + 1}", - "from", - f"Group {group_index + 1}", + TR.recovery__you_have_entered, + TR.recovery__share_num_template.format(share_index + 1), + TR.words__from, + TR.recovery__group_num_template.format(group_index + 1), ], ) ), @@ -99,7 +100,8 @@ async def show_recovery_warning( br_type: str, content: str, subheader: str | None = None, - button: str = "TRY AGAIN", + button: str | None = None, br_code: ButtonRequestType = ButtonRequestType.Warning, ) -> None: + button = button or TR.buttons__try_again # def_arg await show_warning(br_type, content, subheader, button, br_code) diff --git a/core/src/trezor/ui/layouts/tr/reset.py b/core/src/trezor/ui/layouts/tr/reset.py index 9835c5ce1..78b421740 100644 --- a/core/src/trezor/ui/layouts/tr/reset.py +++ b/core/src/trezor/ui/layouts/tr/reset.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING import trezorui2 from trezor.enums import ButtonRequestType from trezor.wire import ActionCancelled +from trezortranslate import TR from ..common import interact from . import RustLayout, confirm_action, show_warning @@ -25,14 +26,16 @@ async def show_share_words( br_code = ButtonRequestType.ResetDevice if share_index is None: - title = "STANDARD BACKUP" - check_title = "CHECK BACKUP" + title = TR.reset__share_words_title + check_title = TR.reset__check_backup_title elif group_index is None: - title = f"SHARE #{share_index + 1}" - check_title = f"CHECK SHARE #{share_index + 1}" + title = f"{TR.words__title_share} #{share_index + 1}" + check_title = ( + f"{TR.words__title_check} {TR.words__title_share} #{share_index + 1}" + ) else: - title = f"GROUP {group_index + 1} - SHARE {share_index + 1}" - check_title = f"GROUP {group_index + 1} - SHARE {share_index + 1}" + title = f"{TR.words__title_group} {group_index + 1} - {TR.words__title_share} {share_index + 1}" + check_title = f"{TR.words__title_group} {group_index + 1} - {TR.words__title_share} {share_index + 1}" # We want the option to go back from words to the previous screen # (by sending CANCELLED) @@ -40,8 +43,8 @@ async def show_share_words( await confirm_action( br_type, title, - description=f"Write down all {len(share_words)} words in order.", - verb="SHOW WORDS", + description=TR.reset__write_down_words_template.format(len(share_words)), + verb=TR.buttons__show_words, verb_cancel=None, br_code=br_code, ) @@ -61,8 +64,8 @@ async def show_share_words( await confirm_action( br_type, check_title, - description="Select the correct word for each position.", - verb="CONTINUE", + description=TR.reset__select_correct_word, + verb=TR.buttons__continue, verb_cancel=None, br_code=br_code, ) @@ -85,11 +88,12 @@ async def select_word( while len(words) < 3: words.append(words[-1]) + word_ordinal = format_ordinal(checked_index + 1).upper() result = await wait( RustLayout( trezorui2.select_word( title="", - description=f"SELECT {format_ordinal(checked_index + 1).upper()} WORD", + description=TR.reset__select_word_template.format(word_ordinal), words=(words[0].lower(), words[1].lower(), words[2].lower()), ) ) @@ -107,23 +111,23 @@ async def slip39_show_checklist(step: int, backup_type: BackupType) -> None: items = ( ( - "Number of shares", - "Set threshold", - "Write down and check all shares", + TR.reset__slip39_checklist_num_shares, + TR.reset__slip39_checklist_set_threshold, + TR.reset__slip39_checklist_write_down, ) if backup_type == BackupType.Slip39_Basic else ( - "Number of groups", - "Number of shares", - "Set sizes and thresholds", + TR.reset__slip39_checklist_num_groups, + TR.reset__slip39_checklist_num_shares, + TR.reset__slip39_checklist_set_sizes, ) ) result = await interact( RustLayout( trezorui2.show_checklist( - title="BACKUP CHECKLIST", - button="CONTINUE", + title=TR.reset__slip39_checklist_title, + button=TR.buttons__continue, active=step, items=items, ) @@ -165,9 +169,9 @@ async def slip39_prompt_threshold( ) -> int: await confirm_action( "slip39_prompt_threshold", - "Threshold", - description="= minimum number of unique word lists used for recovery.", - verb="CONTINUE", + TR.words__title_threshold, + description=TR.reset__threshold_info, + verb=TR.buttons__continue, verb_cancel=None, ) @@ -178,9 +182,9 @@ async def slip39_prompt_threshold( max_count = num_of_shares if group_id is not None: - title = f"THRESHOLD - GROUP {group_id + 1}" + title = f"{TR.words__title_threshold} - {TR.words__title_group} {group_id + 1}" else: - title = "THRESHOLD" + title = TR.words__title_threshold return await _prompt_number( title, @@ -194,9 +198,9 @@ async def slip39_prompt_threshold( async def slip39_prompt_number_of_shares(group_id: int | None = None) -> int: await confirm_action( "slip39_shares", - "Number of shares", - description="= total number of unique word lists used for wallet backup.", - verb="CONTINUE", + TR.reset__title_number_of_shares, + description=TR.reset__number_of_shares_info, + verb=TR.buttons__continue, verb_cancel=None, ) @@ -205,9 +209,9 @@ async def slip39_prompt_number_of_shares(group_id: int | None = None) -> int: max_count = 16 if group_id is not None: - title = f"# SHARES - GROUP {group_id + 1}" + title = f"# {TR.words__title_shares} - {TR.words__title_group} {group_id + 1}" else: - title = "NUMBER OF SHARES" + title = TR.reset__title_number_of_shares return await _prompt_number( title, @@ -224,7 +228,7 @@ async def slip39_advanced_prompt_number_of_groups() -> int: max_count = 16 return await _prompt_number( - "NUMBER OF GROUPS", + TR.reset__title_number_of_groups, count, min_count, max_count, @@ -238,7 +242,7 @@ async def slip39_advanced_prompt_group_threshold(num_of_groups: int) -> int: max_count = num_of_groups return await _prompt_number( - "GROUP THRESHOLD", + TR.reset__title_group_threshold, count, min_count, max_count, @@ -249,9 +253,9 @@ async def slip39_advanced_prompt_group_threshold(num_of_groups: int) -> int: async def show_warning_backup(slip39: bool) -> None: await show_warning( "backup_warning", - "REMEMBER", - "Never make a digital copy of your backup or upload it online!", - "OK, I UNDERSTAND", + TR.words__title_remember, + TR.reset__never_make_digital_copy, + TR.buttons__ok_i_understand, ButtonRequestType.ResetDevice, ) @@ -259,9 +263,9 @@ async def show_warning_backup(slip39: bool) -> None: async def show_success_backup() -> None: await confirm_action( "success_backup", - "BACKUP IS DONE", - description="Keep it safe!", - verb="CONTINUE", + TR.reset__title_backup_is_done, + description=TR.words__keep_it_safe, + verb=TR.buttons__continue, verb_cancel=None, br_code=ButtonRequestType.Success, ) @@ -271,9 +275,11 @@ async def show_reset_warning( br_type: str, content: str, subheader: str | None = None, - button: str = "TRY AGAIN", + button: str | None = None, br_code: ButtonRequestType = ButtonRequestType.Warning, ) -> None: + button = button or TR.buttons__try_again # def_arg + await show_warning( br_type, subheader or "", diff --git a/core/src/trezor/ui/layouts/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index 4f5f0d6fc..0c60d4500 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -5,6 +5,7 @@ from trezor import io, loop, ui from trezor.enums import ButtonRequestType from trezor.wire import ActionCancelled from trezor.wire.context import wait as ctx_wait +from trezortranslate import TR from ..common import button_request, interact @@ -306,9 +307,9 @@ async def confirm_single( async def confirm_reset_device(title: str, recovery: bool = False) -> None: if recovery: - button = "RECOVER WALLET" + button = TR.reset__button_recover else: - button = "CREATE WALLET" + button = TR.reset__button_create await raise_if_not_confirmed( interact( @@ -331,11 +332,11 @@ async def prompt_backup() -> bool: result = await interact( RustLayout( trezorui2.confirm_action( - title="SUCCESS", - action="New wallet created successfully.", - description="You should back up your new wallet right now.", - verb="BACK UP", - verb_cancel="SKIP", + title=TR.words__title_success, + action=TR.backup__new_wallet_successfully_created, + description=TR.backup__it_should_be_backed_up, + verb=TR.buttons__back_up, + verb_cancel=TR.buttons__skip, ) ), "backup_device", @@ -347,11 +348,11 @@ async def prompt_backup() -> bool: result = await interact( RustLayout( trezorui2.confirm_action( - title="WARNING", - action="Are you sure you want to skip the backup?", - description="You can back up your Trezor once, at any time.", - verb="BACK UP", - verb_cancel="SKIP", + title=TR.words__warning.upper(), + action=TR.backup__want_to_skip, + description=TR.backup__can_back_up_anytime, + verb=TR.buttons__back_up, + verb_cancel=TR.buttons__skip, ) ), "backup_device", @@ -365,9 +366,9 @@ async def confirm_path_warning( path_type: str | None = None, ) -> None: title = ( - "Wrong derivation path for selected account." + TR.addr_mismatch__wrong_derication_path if not path_type - else f"Unknown {path_type.lower()}." + else f"{TR.words__unknown} {path_type.lower()}." ) await raise_if_not_confirmed( interact( @@ -375,8 +376,8 @@ async def confirm_path_warning( trezorui2.show_warning( title=title, value=path, - description="Continue anyway?", - button="CONTINUE", + description=TR.words__continue_anyway, + button=TR.buttons__continue, ) ), "path_warning", @@ -392,7 +393,7 @@ async def confirm_homescreen( interact( RustLayout( trezorui2.confirm_homescreen( - title="CHANGE HOMESCREEN", + title=TR.homescreen__title_set, image=image, ) ), @@ -413,20 +414,20 @@ async def show_address( network: str | None = None, multisig_index: int | None = None, xpubs: Sequence[str] = (), - mismatch_title: str = "Address mismatch?", + mismatch_title: str | None = None, details_title: str | None = None, br_type: str = "show_address", br_code: ButtonRequestType = ButtonRequestType.Address, chunkify: bool = False, ) -> None: + mismatch_title = mismatch_title or TR.addr_mismatch__title # def_arg send_button_request = True + if title is None: - title = ( - "RECEIVE ADDRESS\n(MULTISIG)" - if multisig_index is not None - else "RECEIVE ADDRESS" - ) - details_title = "RECEIVING TO" + title = TR.address__title_receive_address + if multisig_index is not None: + title = f"{title}\n(MULTISIG)" + details_title = TR.send__title_receiving_to elif details_title is None: details_title = title @@ -460,7 +461,11 @@ async def show_address( def xpub_title(i: int) -> str: result = f"MULTISIG XPUB #{i + 1}\n" - result += "(YOURS)" if i == multisig_index else "(COSIGNER)" + result += ( + f"({TR.address__title_yours})" + if i == multisig_index + else f"({TR.address__title_cosigner})" + ) return result result = await ctx_wait( @@ -490,13 +495,15 @@ async def show_address( def show_pubkey( pubkey: str, - title: str = "Public key", + title: str | None = None, *, account: str | None = None, path: str | None = None, - mismatch_title: str = "Key mismatch?", - br_type="show_pubkey", + mismatch_title: str | None = None, + br_type: str = "show_pubkey", ) -> Awaitable[None]: + title = title or TR.address__public_key # def_arg + mismatch_title = mismatch_title or TR.addr_mismatch__title_key_mismatch # def_arg return show_address( address=pubkey, title=title.upper(), @@ -513,9 +520,10 @@ async def show_error_and_raise( br_type: str, content: str, subheader: str | None = None, - button: str = "TRY AGAIN", + button: str | None = None, exc: ExceptionType = ActionCancelled, ) -> NoReturn: + button = button or TR.buttons__try_again # def_arg await interact( RustLayout( trezorui2.show_error( @@ -535,9 +543,10 @@ async def show_warning( br_type: str, content: str, subheader: str | None = None, - button: str = "CONTINUE", + button: str | None = None, br_code: ButtonRequestType = ButtonRequestType.Warning, ) -> None: + button = button or TR.buttons__continue # def_arg await raise_if_not_confirmed( interact( RustLayout( @@ -557,8 +566,9 @@ async def show_success( br_type: str, content: str, subheader: str | None = None, - button: str = "CONTINUE", + button: str | None = None, ) -> None: + button = button or TR.buttons__continue # def_arg await raise_if_not_confirmed( interact( RustLayout( @@ -586,16 +596,17 @@ async def confirm_output( chunkify: bool = False, ) -> None: if title is not None: + # TODO: handle translation if title.upper().startswith("CONFIRM "): title = title[len("CONFIRM ") :] amount_title = title.upper() recipient_title = title.upper() elif output_index is not None: - amount_title = f"AMOUNT #{output_index + 1}" - recipient_title = f"RECIPIENT #{output_index + 1}" + amount_title = f"{TR.send__title_amount } #{output_index + 1}" + recipient_title = f"{TR.send__title_recipient} #{output_index + 1}" else: - amount_title = "SENDING AMOUNT" - recipient_title = "SENDING TO" + amount_title = TR.send__confirm_sending + recipient_title = TR.send__title_sending_to while True: result = await interact( @@ -605,7 +616,7 @@ async def confirm_output( subtitle=address_label, description=None, value=address, - verb="CONTINUE", + verb=TR.buttons__continue, hold=False, info_button=False, chunkify=chunkify, @@ -624,7 +635,7 @@ async def confirm_output( subtitle=None, description=None, value=amount, - verb=None if hold else "CONFIRM", + verb=None if hold else TR.buttons__confirm, verb_cancel="^", hold=hold, info_button=False, @@ -645,11 +656,11 @@ async def confirm_payment_request( result = await interact( RustLayout( trezorui2.confirm_with_info( - title="SENDING", + title=TR.send__title_sending, items=[(ui.NORMAL, f"{amount} to\n{recipient_name}")] + [(ui.NORMAL, memo) for memo in memos], - button="CONFIRM", - info_button="DETAILS", + button=TR.buttons__confirm, + info_button=TR.buttons__details, ) ), "confirm_payment_request", @@ -669,7 +680,7 @@ async def confirm_payment_request( async def should_show_more( title: str, para: Iterable[tuple[int, str]], - button_text: str = "Show all", + button_text: str | None = None, br_type: str = "should_show_more", br_code: ButtonRequestType = BR_TYPE_OTHER, confirm: str | bytes | None = None, @@ -679,8 +690,9 @@ async def should_show_more( Raises ActionCancelled if the user cancels. """ + button_text = button_text or TR.buttons__show_all # def_arg if confirm is None or not isinstance(confirm, str): - confirm = "CONFIRM" + confirm = TR.buttons__confirm result = await interact( RustLayout( @@ -730,7 +742,7 @@ async def _confirm_ask_pagination( paginated = RustLayout( trezorui2.confirm_more( title=title, - button="CLOSE", + button=TR.buttons__close, items=[(ui.MONO, data)], ) ) @@ -747,14 +759,15 @@ async def confirm_blob( br_type: str, title: str, data: bytes | str, - description: str = "", - verb: str = "CONFIRM", + description: str | None = None, + verb: str | None = None, verb_cancel: str | None = None, hold: bool = False, br_code: ButtonRequestType = BR_TYPE_OTHER, ask_pagination: bool = False, chunkify: bool = False, ) -> None: + verb = verb or TR.buttons__confirm # def_arg title = title.upper() layout = RustLayout( trezorui2.confirm_blob( @@ -771,7 +784,7 @@ async def confirm_blob( if ask_pagination and layout.page_count() > 1: assert not hold - await _confirm_ask_pagination(br_type, title, data, description, br_code) + await _confirm_ask_pagination(br_type, title, data, description or "", br_code) else: await raise_if_not_confirmed( @@ -786,7 +799,7 @@ async def confirm_blob( async def confirm_address( title: str, address: str, - description: str | None = "Address:", + description: str | None = TR.address__address, br_type: str = "confirm_address", br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> None: @@ -796,7 +809,7 @@ async def confirm_address( description or "", br_type, br_code, - verb="CONFIRM", + verb=TR.buttons__confirm, ) @@ -813,24 +826,25 @@ async def confirm_text( description or "", br_type, br_code, - verb="CONFIRM", + verb=TR.buttons__confirm, ) def confirm_amount( title: str, amount: str, - description: str = "Amount:", + description: str | None = None, br_type: str = "confirm_amount", br_code: ButtonRequestType = BR_TYPE_OTHER, ) -> Awaitable[None]: + description = description or TR.send__amount # def_arg return confirm_value( title, amount, description, br_type, br_code, - verb="CONFIRM", + verb=TR.buttons__confirm, ) @@ -857,7 +871,7 @@ def confirm_value( info_items = info_items or [] info_layout = RustLayout( trezorui2.show_info_with_cancel( - title="INFORMATION", + title=TR.words__title_information, items=info_items, ) ) @@ -910,36 +924,42 @@ async def confirm_properties( async def confirm_total( total_amount: str, fee_amount: str, - title: str = "SUMMARY", - total_label: str = "Total amount:", - fee_label: str = "Including fee:", + title: str | None = None, + total_label: str | None = None, + fee_label: str | None = None, account_label: str | None = None, fee_rate_amount: str | None = None, br_type: str = "confirm_total", br_code: ButtonRequestType = ButtonRequestType.SignTx, ) -> None: + title = title or TR.words__title_summary # def_arg + total_label = total_label or TR.send__total_amount # def_arg + fee_label = fee_label or TR.send__including_fee # def_arg + items = [ (total_label, total_amount), (fee_label, fee_amount), ] info_items = [] if account_label: - info_items.append(("Sending from account:", account_label)) + info_items.append((TR.confirm_total__sending_from_account, account_label)) if fee_rate_amount: - info_items.append(("Fee rate:", fee_rate_amount)) + info_items.append((TR.confirm_total__fee_rate, fee_rate_amount)) await confirm_summary( - items, "SUMMARY", info_items, br_type=br_type, br_code=br_code + items, TR.words__title_summary, info_items, br_type=br_type, br_code=br_code ) async def confirm_summary( items: Iterable[tuple[str, str]], - title: str = "SUMMARY", + title: str | None = None, info_items: Iterable[tuple[str, str]] | None = None, br_type: str = "confirm_total", br_code: ButtonRequestType = ButtonRequestType.SignTx, ) -> None: + title = title or TR.words__title_summary # def_arg + total_layout = RustLayout( trezorui2.confirm_total( title=title.upper(), @@ -950,7 +970,7 @@ async def confirm_summary( info_items = info_items or [] info_layout = RustLayout( trezorui2.show_info_with_cancel( - title="INFORMATION", + title=TR.words__title_information, items=info_items, ) ) @@ -968,10 +988,10 @@ async def confirm_ethereum_tx( ) -> None: total_layout = RustLayout( trezorui2.confirm_total( - title="SUMMARY", + title=TR.words__title_summary, items=[ - ("Amount:", total_amount), - ("Maximum fee:", maximum_fee), + (TR.send__amount, total_amount), + (TR.send__maximum_fee, maximum_fee), ], info_button=True, cancel_arrow=True, @@ -979,7 +999,7 @@ async def confirm_ethereum_tx( ) info_layout = RustLayout( trezorui2.show_info_with_cancel( - title="FEE INFORMATION", + title=TR.confirm_total__title_fee, items=items, ) ) @@ -988,9 +1008,9 @@ async def confirm_ethereum_tx( # Allowing going back and forth between recipient and summary/details await confirm_blob( br_type, - "RECIPIENT", + TR.send__title_recipient, recipient, - verb="CONTINUE", + verb=TR.buttons__continue, chunkify=chunkify, ) @@ -1026,10 +1046,10 @@ async def confirm_joint_total(spending_amount: str, total_amount: str) -> None: interact( RustLayout( trezorui2.confirm_total( - title="JOINT TRANSACTION", + title=TR.send__title_joint_transaction, items=[ - ("You are contributing:", spending_amount), - ("To the total amount:", total_amount), + (TR.send__you_are_contributing, spending_amount), + (TR.send__to_the_total_amount, total_amount), ], ) ), @@ -1046,8 +1066,9 @@ async def confirm_metadata( param: str | None = None, br_code: ButtonRequestType = ButtonRequestType.SignTx, hold: bool = False, - verb: str = "CONTINUE", + verb: str | None = None, ) -> None: + verb = verb or TR.buttons__continue # def_arg await confirm_action( br_type, title=title.upper(), @@ -1065,8 +1086,8 @@ async def confirm_replacement(title: str, txid: str) -> None: "confirm_replacement", title.upper(), txid, - "Transaction ID:", - "CONTINUE", + TR.send__transaction_id, + TR.buttons__continue, br_code=ButtonRequestType.SignTx, ) @@ -1079,11 +1100,11 @@ async def confirm_modify_output( ) -> None: address_layout = RustLayout( trezorui2.confirm_blob( - title="MODIFY AMOUNT", + title=TR.modify_amount__title, data=address, - verb="CONTINUE", + verb=TR.buttons__continue, verb_cancel=None, - description="Address:", + description=TR.modify_amount__address, extra=None, ) ) @@ -1159,10 +1180,10 @@ async def confirm_modify_fee( ) items: list[tuple[str, str]] = [] if fee_rate_amount: - items.append(("New fee rate:", fee_rate_amount)) + items.append((TR.bitcoin__new_fee_rate, fee_rate_amount)) info_layout = RustLayout( trezorui2.show_info_with_cancel( - title="FEE INFORMATION", + title=TR.confirm_total__title_fee, items=items, ) ) @@ -1192,7 +1213,7 @@ async def confirm_sign_identity( ) -> None: await confirm_blob( "sign_identity", - f"Sign {proto}", + f"{TR.words__sign} {proto}", identity, challenge_visual + "\n" if challenge_visual else "", br_code=BR_TYPE_OTHER, @@ -1208,10 +1229,10 @@ async def confirm_signverify( chunkify: bool = False, ) -> None: if verify: - address_title = "VERIFY ADDRESS" + address_title = TR.sign_message__verify_address br_type = "verify_message" else: - address_title = "SIGNING ADDRESS" + address_title = TR.sign_message__confirm_address br_type = "sign_message" address_layout = RustLayout( @@ -1219,7 +1240,7 @@ async def confirm_signverify( title=address_title, data=address, description="", - verb="CONTINUE", + verb=TR.buttons__continue, extra=None, chunkify=chunkify, ) @@ -1227,14 +1248,19 @@ async def confirm_signverify( items: list[tuple[str, str]] = [] if account is not None: - items.append(("Account:", account)) + items.append((TR.address_details__account, account)) if path is not None: - items.append(("Derivation path:", path)) - items.append(("Message size:", f"{len(message)} Bytes")) + items.append((TR.address_details__derivation_path, path)) + items.append( + ( + TR.sign_message__message_size, + TR.sign_message__bytes_template.format(len(message)), + ) + ) info_layout = RustLayout( trezorui2.show_info_with_cancel( - title="INFORMATION", + title=TR.words__title_information, items=items, horizontal=True, ) @@ -1242,12 +1268,12 @@ async def confirm_signverify( message_layout = RustLayout( trezorui2.confirm_blob( - title="CONFIRM MESSAGE", + title=TR.sign_message__confirm_message, description=None, data=message, extra=None, hold=not verify, - verb="CONFIRM" if verify else None, + verb=TR.buttons__confirm if verify else None, ) ) @@ -1257,7 +1283,7 @@ async def confirm_signverify( ) if result is not CONFIRMED: result = await ctx_wait( - RustLayout(trezorui2.show_mismatch(title="Address mismatch?")) + RustLayout(trezorui2.show_mismatch(title=TR.addr_mismatch__mismatch)) ) assert result in (CONFIRMED, CANCELLED) # Right button aborts action, left goes back to showing address. @@ -1304,7 +1330,7 @@ def request_passphrase_on_host() -> None: draw_simple( trezorui2.show_simple( title=None, - description="Please enter your passphrase.", + description=TR.passphrase__please_enter, ) ) @@ -1312,7 +1338,9 @@ def request_passphrase_on_host() -> None: async def request_passphrase_on_device(max_len: int) -> str: result = await interact( RustLayout( - trezorui2.request_passphrase(prompt="Enter passphrase", max_len=max_len) + trezorui2.request_passphrase( + prompt=TR.passphrase__title_enter, max_len=max_len + ) ), "passphrase_device", ButtonRequestType.PassphraseEntry, @@ -1335,9 +1363,9 @@ async def request_pin_on_device( if attempts_remaining is None: subprompt = "" elif attempts_remaining == 1: - subprompt = "Last attempt" + subprompt = TR.pin__last_attempt else: - subprompt = f"{attempts_remaining} tries left" + subprompt = f"{attempts_remaining} {TR.pin__tries_left}" result = await interact( RustLayout( @@ -1368,21 +1396,21 @@ async def pin_mismatch_popup( is_wipe_code: bool = False, ) -> None: await button_request("pin_mismatch", code=BR_TYPE_OTHER) - title = "Wipe code mismatch" if is_wipe_code else "PIN mismatch" - description = "wipe codes" if is_wipe_code else "PINs" + title = TR.wipe_code__wipe_code_mismatch if is_wipe_code else TR.pin__pin_mismatch + description = TR.wipe_code__mismatch if is_wipe_code else TR.pin__mismatch return await show_error_popup( title, - f"The {description} you entered do not match.", - button="TRY AGAIN", + description, + button=TR.buttons__try_again, ) async def wipe_code_same_as_pin_popup() -> None: await button_request("wipe_code_same_as_pin", code=BR_TYPE_OTHER) return await show_error_popup( - "Invalid wipe code", - "The wipe code must be different from your PIN.", - button="TRY AGAIN", + TR.wipe_code__invalid, + TR.wipe_code__diff_from_pin, + button=TR.buttons__try_again, ) @@ -1399,12 +1427,10 @@ async def confirm_set_new_pin( trezorui2.confirm_emphasized( title=title.upper(), items=( - "Turn on ", - (True, description), - " protection?\n\n", + (True, description + "\n\n"), information, ), - verb="TURN ON", + verb=TR.buttons__turn_on, ) ), br_type, diff --git a/core/src/trezor/ui/layouts/tt/fido.py b/core/src/trezor/ui/layouts/tt/fido.py index 5330a3cd7..44e8bdcba 100644 --- a/core/src/trezor/ui/layouts/tt/fido.py +++ b/core/src/trezor/ui/layouts/tt/fido.py @@ -76,11 +76,13 @@ async def confirm_fido( async def confirm_fido_reset() -> bool: + from trezortranslate import TR + confirm = RustLayout( trezorui2.confirm_action( - title="FIDO2 RESET", - action="erase all credentials?", - description="Do you really want to", + title=TR.fido__title_reset, + action=TR.fido__erase_credentials, + description=TR.words__really_wanna, reverse=True, ) ) diff --git a/core/src/trezor/ui/layouts/tt/homescreen.py b/core/src/trezor/ui/layouts/tt/homescreen.py index 9bcb7ea6d..8b66a4c54 100644 --- a/core/src/trezor/ui/layouts/tt/homescreen.py +++ b/core/src/trezor/ui/layouts/tt/homescreen.py @@ -54,6 +54,7 @@ class Homescreen(HomescreenBase): notification = notification.rstrip("!") if "COINJOIN" in notification.upper(): level = 3 + # TODO: handle translation of EXPERIMENTAL elif "EXPERIMENTAL" in notification.upper(): level = 2 elif notification_is_error: @@ -121,10 +122,12 @@ class Busyscreen(HomescreenBase): RENDER_INDICATOR = storage_cache.BUSYSCREEN_ON def __init__(self, delay_ms: int) -> None: + from trezortranslate import TR + skip = storage_cache.homescreen_shown is self.RENDER_INDICATOR super().__init__( layout=trezorui2.show_progress_coinjoin( - title="Waiting for others", + title=TR.coinjoin__waiting_for_others, indeterminate=True, time_ms=delay_ms, skip_first_paint=skip, diff --git a/core/src/trezor/ui/layouts/tt/progress.py b/core/src/trezor/ui/layouts/tt/progress.py index 400510717..031732f83 100644 --- a/core/src/trezor/ui/layouts/tt/progress.py +++ b/core/src/trezor/ui/layouts/tt/progress.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING import trezorui2 from trezor import ui +from trezortranslate import TR if TYPE_CHECKING: from typing import Any @@ -31,10 +32,11 @@ class RustProgress: def progress( - message: str = "PLEASE WAIT", + message: str | None = None, description: str | None = None, indeterminate: bool = False, ) -> ProgressLayout: + message = message or TR.progress__please_wait # def_arg return RustProgress( layout=trezorui2.show_progress( title=message.upper(), @@ -59,12 +61,12 @@ def pin_progress(message: str, description: str) -> ProgressLayout: def monero_keyimage_sync_progress() -> ProgressLayout: - return progress("SYNCING") + return progress("", TR.progress__syncing) def monero_live_refresh_progress() -> ProgressLayout: - return progress("REFRESHING", indeterminate=True) + return progress("", TR.progress__refreshing, indeterminate=True) def monero_transaction_progress_inner() -> ProgressLayout: - return progress("SIGNING TRANSACTION") + return progress("", TR.progress__signing_transaction) diff --git a/core/src/trezor/ui/layouts/tt/recovery.py b/core/src/trezor/ui/layouts/tt/recovery.py index cbe33fdee..769c2cd04 100644 --- a/core/src/trezor/ui/layouts/tt/recovery.py +++ b/core/src/trezor/ui/layouts/tt/recovery.py @@ -3,6 +3,7 @@ from typing import Callable, Iterable import trezorui2 from trezor.enums import ButtonRequestType from trezor.wire.context import wait as ctx_wait +from trezortranslate import TR from ..common import interact from . import RustLayout, raise_if_not_confirmed @@ -32,7 +33,7 @@ async def request_word_count(dry_run: bool) -> int: async def request_word(word_index: int, word_count: int, is_slip39: bool) -> str: - prompt = f"Type word {word_index + 1} of {word_count}" + prompt = TR.recovery__type_word_x_of_y_template.format(word_index + 1, word_count) if is_slip39: keyboard = RustLayout(trezorui2.request_slip39(prompt=prompt)) else: @@ -54,7 +55,9 @@ async def show_remaining_shares( for remaining, group in groups: if 0 < remaining < MAX_SHARE_COUNT: title = strings.format_plural( - "{count} more {plural} starting", remaining, "share" + TR.recovery__x_more_items_starting_template_plural, + remaining, + TR.plurals__x_shares_needed, ) words = "\n".join(group) pages.append((title, words)) @@ -63,7 +66,9 @@ async def show_remaining_shares( ): groups_remaining = group_threshold - shares_remaining.count(0) title = strings.format_plural( - "{count} more {plural} starting", groups_remaining, "group" + TR.recovery__x_more_items_starting_template_plural, + groups_remaining, + TR.plurals__x_groups_needed, ) words = "\n".join(group) pages.append((title, words)) @@ -83,10 +88,10 @@ async def show_group_share_success(share_index: int, group_index: int) -> None: RustLayout( trezorui2.show_group_share_success( lines=[ - "You have entered", - f"Share {share_index + 1}", - "from", - f"Group {group_index + 1}", + TR.recovery__you_have_entered, + TR.recovery__share_num_template.format(share_index + 1), + TR.words__from, + TR.recovery__group_num_template.format(group_index + 1), ], ) ), @@ -108,7 +113,7 @@ async def continue_recovery( if show_info: # Show this just one-time - description = "You'll only have to select the first 2-4 letters of each word." + description = TR.recovery__only_first_n_letters else: description = subtext or "" @@ -135,9 +140,10 @@ async def show_recovery_warning( br_type: str, content: str, subheader: str | None = None, - button: str = "TRY AGAIN", + button: str | None = None, br_code: ButtonRequestType = ButtonRequestType.Warning, ) -> None: + button = button or TR.buttons__try_again # def_arg await raise_if_not_confirmed( interact( RustLayout( diff --git a/core/src/trezor/ui/layouts/tt/reset.py b/core/src/trezor/ui/layouts/tt/reset.py index 62b84526d..109460b60 100644 --- a/core/src/trezor/ui/layouts/tt/reset.py +++ b/core/src/trezor/ui/layouts/tt/reset.py @@ -4,6 +4,7 @@ import trezorui2 from trezor.enums import ButtonRequestType from trezor.wire import ActionCancelled from trezor.wire.context import wait as ctx_wait +from trezortranslate import TR from ..common import interact from . import RustLayout, raise_if_not_confirmed @@ -47,11 +48,13 @@ async def show_share_words( group_index: int | None = None, ) -> None: if share_index is None: - title = "RECOVERY SEED" + title = TR.reset__recovery_seed_title elif group_index is None: - title = f"RECOVERY SHARE #{share_index + 1}" + title = TR.reset__recovery_share_title_template.format(share_index + 1) else: - title = f"GROUP {group_index + 1} - SHARE {share_index + 1}" + title = TR.reset__group_share_title_template.format( + group_index + 1, share_index + 1 + ) pages = _split_share_into_pages(share_words) @@ -77,11 +80,13 @@ async def select_word( group_index: int | None = None, ) -> str: if share_index is None: - title: str = "CHECK SEED" + title: str = TR.reset__check_seed_title elif group_index is None: - title = f"CHECK SHARE #{share_index + 1}" + title = TR.reset__check_share_title_template.format(share_index + 1) else: - title = f"CHECK G{group_index + 1} - SHARE {share_index + 1}" + title = TR.reset__check_group_share_title_template.format( + group_index + 1, share_index + 1 + ) # It may happen (with a very low probability) # that there will be less than three unique words to choose from. @@ -94,7 +99,9 @@ async def select_word( RustLayout( trezorui2.select_word( title=title, - description=f"Select word {checked_index + 1} of {count}:", + description=TR.reset__select_word_x_of_y_template.format( + checked_index + 1, count + ), words=(words[0], words[1], words[2]), ) ) @@ -112,23 +119,23 @@ async def slip39_show_checklist(step: int, backup_type: BackupType) -> None: items = ( ( - "Set number of shares", - "Set threshold", - "Write down and check all recovery shares", + TR.reset__slip39_checklist_set_num_shares, + TR.reset__slip39_checklist_set_threshold, + TR.reset__slip39_checklist_write_down_recovery, ) if backup_type == BackupType.Slip39_Basic else ( - "Set number of groups", - "Set number of shares", - "Set size and threshold for each group", + TR.reset__slip39_checklist_set_num_groups, + TR.reset__slip39_checklist_set_num_shares, + TR.reset__slip39_checklist_set_sizes_longer, ) ) result = await interact( RustLayout( trezorui2.show_checklist( - title="BACKUP CHECKLIST", - button="CONTINUE", + title=TR.reset__slip39_checklist_title, + button=TR.buttons__continue, active=step, items=items, ) @@ -179,7 +186,9 @@ async def _prompt_number( await ctx_wait( RustLayout( trezorui2.show_simple( - title=None, description=info(value), button="OK, I UNDERSTAND" + title=None, + description=info(value), + button=TR.buttons__ok_i_understand, ) ) ) @@ -198,39 +207,41 @@ async def slip39_prompt_threshold( def description(count: int) -> str: if group_id is None: if count == 1: - return "For recovery you need 1 share." + return TR.reset__you_need_one_share elif count == max_count: - return f"For recovery you need all {count} of the shares." + return TR.reset__need_all_share_template.format(count) else: - return f"For recovery you need any {count} of the shares." + return TR.reset__need_any_share_template.format(count) else: - return f"The required number of shares to form Group {group_id + 1}." + return TR.reset__num_shares_for_group_template.format(group_id + 1) def info(count: int) -> str: - text = "The threshold sets the number of shares " + # TODO: this is madness... + text = TR.reset__the_threshold_sets_the_number_of_shares if group_id is None: - text += "needed to recover your wallet. " - text += f"Set it to {count} and you will need " + text += TR.reset__needed_to_recover_your_wallet + text += TR.reset__set_it_to_count_template.format(count) if num_of_shares == 1: - text += "1 share." + text += TR.reset__one_share elif num_of_shares == count: - text += f"all {count} of your {num_of_shares} shares." + text += TR.reset__all_x_of_y_template.format(count, num_of_shares) else: - text += f"any {count} of your {num_of_shares} shares." + text += TR.reset__any_x_of_y_template.format(count, num_of_shares) + text += "." else: - text += "needed to form a group. " - text += f"Set it to {count} and you will " + text += TR.reset__needed_to_form_a_group + text += TR.reset__set_it_to_count_template.format(count) if num_of_shares == 1: - text += "need 1 share " + text += TR.reset__one_share + " " elif num_of_shares == count: - text += f"need all {count} of {num_of_shares} shares " + text += TR.reset__all_x_of_y_template.format(count, num_of_shares) else: - text += f"need any {count} of {num_of_shares} shares " - text += f"to form Group {group_id + 1}." + text += TR.reset__any_x_of_y_template.format(count, num_of_shares) + text += " " + TR.reset__to_form_group_template.format(group_id + 1) return text return await _prompt_number( - "SET THRESHOLD", + TR.reset__title_set_threshold, description, info, count, @@ -248,19 +259,21 @@ async def slip39_prompt_number_of_shares(group_id: int | None = None) -> int: def description(i: int): if group_id is None: if i == 1: - return "Only one share will be created." + return TR.reset__only_one_share_will_be_created else: - return f"{i} people or locations will each hold one share." + return TR.reset__num_of_share_holders_template.format(i) else: - return f"Set the total number of shares in Group {group_id + 1}." + return TR.reset__total_number_of_shares_in_group_template.format( + group_id + 1 + ) if group_id is None: - info = "Each recovery share is a sequence of 20 words. Next you will choose how many shares you need to recover your wallet." + info = TR.reset__num_of_shares_basic_info else: - info = f"Each recovery share is a sequence of 20 words. Next you will choose the threshold number of shares needed to form Group {group_id + 1}." + info = TR.reset__num_of_shares_advanced_info_template.format(group_id + 1) return await _prompt_number( - "SET NUMBER OF SHARES", + TR.reset__title_set_number_of_shares, description, lambda i: info, count, @@ -274,11 +287,11 @@ async def slip39_advanced_prompt_number_of_groups() -> int: count = 5 min_count = 2 max_count = 16 - description = "A group is made up of recovery shares." - info = "Each group has a set number of shares and its own threshold. In the next steps you will set the numbers of shares and the thresholds." + description = TR.reset__group_description + info = TR.reset__group_info return await _prompt_number( - "SET NUMBER OF GROUPS", + TR.reset__title_set_number_of_groups, lambda i: description, lambda i: info, count, @@ -292,11 +305,11 @@ async def slip39_advanced_prompt_group_threshold(num_of_groups: int) -> int: count = num_of_groups // 2 + 1 min_count = 1 max_count = num_of_groups - description = "The required number of groups for recovery." - info = "The group threshold specifies the number of groups required to recover your wallet." + description = TR.reset__required_number_of_groups + info = TR.reset__advanced_group_threshold_info return await _prompt_number( - "SET GROUP THRESHOLD", + TR.reset__title_set_group_threshold, lambda i: description, lambda i: info, count, @@ -307,19 +320,11 @@ async def slip39_advanced_prompt_group_threshold(num_of_groups: int) -> int: async def show_warning_backup(slip39: bool) -> None: - if slip39: - description = ( - "Never make a digital copy of your shares and never upload them online." - ) - else: - description = ( - "Never make a digital copy of your seed and never upload it online." - ) result = await interact( RustLayout( trezorui2.show_info( - title=description, - button="OK, I UNDERSTAND", + title=TR.reset__never_make_digital_copy, + button=TR.buttons__ok_i_understand, allow_cancel=False, ) ), @@ -333,17 +338,21 @@ async def show_warning_backup(slip39: bool) -> None: async def show_success_backup() -> None: from . import show_success - text = "Use your backup when you need to recover your wallet." - await show_success("success_backup", text, "Your backup is done.") + await show_success( + "success_backup", + TR.reset__use_your_backup, + TR.reset__your_backup_is_done, + ) async def show_reset_warning( br_type: str, content: str, subheader: str | None = None, - button: str = "TRY AGAIN", + button: str | None = None, br_code: ButtonRequestType = ButtonRequestType.Warning, ) -> None: + button = button or TR.buttons__try_again # def_arg await raise_if_not_confirmed( interact( RustLayout( diff --git a/core/tests/test_trezor.strings.py b/core/tests/test_trezor.strings.py index 3346098f9..8448cb2c6 100644 --- a/core/tests/test_trezor.strings.py +++ b/core/tests/test_trezor.strings.py @@ -22,7 +22,7 @@ class TestStrings(unittest.TestCase): for v in VECTORS: self.assertEqual(strings.format_amount(v[0], v[1]), v[2]) - def test_format_plural(self): + def test_format_plural_english(self): VECTORS = [ ("We need {count} more {plural}", 1, "share", "We need 1 more share"), ("We need {count} more {plural}", 3, "share", "We need 3 more shares"), @@ -35,6 +35,23 @@ class TestStrings(unittest.TestCase): ("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_english(v[0], v[1], v[2]), v[3]) + + with self.assertRaises(ValueError): + strings.format_plural_english("Hello", 1, "share") + + def test_format_plural(self): + VECTORS = [ + ("You are about to sign {count} {plural}", 0, "action|actions", "You are about to sign 0 actions"), + ("You are about to sign {count} {plural}", 1, "action|actions", "You are about to sign 1 action"), + ("You are about to sign {count} {plural}", 3, "action|actions", "You are about to sign 3 actions"), + ("You are about to sign {count} {plural}", 15, "action|actions", "You are about to sign 15 actions"), + ("Chystáte se podepsat {count} {plural}", 0, "akci|akce|akcí", "Chystáte se podepsat 0 akcí"), + ("Chystáte se podepsat {count} {plural}", 1, "akci|akce|akcí", "Chystáte se podepsat 1 akci"), + ("Chystáte se podepsat {count} {plural}", 3, "akci|akce|akcí", "Chystáte se podepsat 3 akce"), + ("Chystáte se podepsat {count} {plural}", 15, "akci|akce|akcí", "Chystáte se podepsat 15 akcí"), + ] for v in VECTORS: self.assertEqual(strings.format_plural(v[0], v[1], v[2]), v[3]) @@ -42,6 +59,13 @@ class TestStrings(unittest.TestCase): strings.format_plural("Hello", 1, "share") def test_format_duration_ms(self): + unit_plurals = { + "millisecond": "millisecond|milliseconds", + "second": "second|seconds", + "minute": "minute|minutes", + "hour": "hour|hours", + } + VECTORS = [ (0, "0 milliseconds"), (1, "1 millisecond"), @@ -60,7 +84,7 @@ class TestStrings(unittest.TestCase): ] for v in VECTORS: - self.assertEqual(strings.format_duration_ms(v[0]), v[1]) + self.assertEqual(strings.format_duration_ms(v[0], unit_plurals), v[1]) def test_format_timestamp(self): VECTORS = [ diff --git a/core/tools/codegen/foreign_chars.py b/core/tools/codegen/foreign_chars.py new file mode 100644 index 000000000..d3cd4fc95 --- /dev/null +++ b/core/tools/codegen/foreign_chars.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from typing import Any + +czech_chars: list[tuple[str, str]] = [ + ("Á", "C381"), + ("Č", "C48C"), + ("Ď", "C48E"), + ("É", "C389"), + ("Ě", "C49A"), + ("Í", "C38D"), + ("Ň", "C587"), + ("Ó", "C393"), + ("Ř", "C598"), + ("Š", "C5A0"), + ("Ť", "C5A4"), + ("Ú", "C39A"), + ("Ů", "C5AE"), + ("Ý", "C39D"), + ("Ž", "C5BD"), + ("á", "C3A1"), + ("č", "C48D"), + ("ď", "C48F"), + ("é", "C3A9"), + ("ě", "C49B"), + ("í", "C3AD"), + ("ň", "C588"), + ("ó", "C3B3"), + ("ř", "C599"), + ("š", "C5A1"), + ("ť", "C5A5"), + ("ú", "C3BA"), + ("ů", "C5AF"), + ("ý", "C3BD"), + ("ž", "C5BE"), +] + +french_chars: list[tuple[str, str]] = [ + ("À", "C380"), + ("Â", "C382"), + ("Æ", "C386"), + ("Ç", "C387"), + ("È", "C388"), + ("É", "C389"), + ("Ê", "C38A"), + ("Ë", "C38B"), + ("Î", "C38E"), + ("Ï", "C38F"), + ("Ô", "C394"), + ("Ù", "C399"), + ("Û", "C39B"), + ("Ü", "C39C"), + ("Ÿ", "C5B8"), + ("Œ", "C592"), + ("à", "C3A0"), + ("â", "C3A2"), + ("æ", "C3A6"), + ("ç", "C3A7"), + ("è", "C3A8"), + ("é", "C3A9"), + ("ê", "C3AA"), + ("ë", "C3AB"), + ("î", "C3AE"), + ("ï", "C3AF"), + ("ô", "C3B4"), + ("ù", "C3B9"), + ("û", "C3BB"), + ("ü", "C3BC"), + ("ÿ", "C3BF"), + ("œ", "C593"), +] + +all_languages: list[dict[str, Any]] = [ + {"name": "cs", "data": czech_chars}, + {"name": "fr", "data": french_chars}, +] diff --git a/core/tools/codegen/gen_font.py b/core/tools/codegen/gen_font.py index 230d86ed5..5f045a970 100755 --- a/core/tools/codegen/gen_font.py +++ b/core/tools/codegen/gen_font.py @@ -4,10 +4,16 @@ from __future__ import annotations +from dataclasses import dataclass from pathlib import Path +from typing import TextIO, Any +import json +# pip install freetype-py import freetype +from foreign_chars import all_languages + HERE = Path(__file__).parent FONTS_DIR = HERE / "fonts" @@ -66,167 +72,321 @@ def drop_left_columns(buf: list[int], width: int, drop: int) -> list[int]: return res -def process_face( - name: str, - style: str, - size: int, - bpp: int = 4, - shaveX: int = 0, - ext: str = "ttf", -) -> None: - print("Processing ... %s %s %s" % (name, style, size)) - file_name = FONTS_DIR / f"{name}-{style}.{ext}" - face = freetype.Face(str(file_name)) - face.set_pixel_sizes(0, size) - fontname = "%s_%s_%d" % (name.lower(), style.lower(), size) - font_ymin = 0 - font_ymax = 0 - - with open("font_%s.c" % fontname, "wt") as f: - f.write("#include \n\n") - f.write("// clang-format off\n\n") - f.write("// - the first two bytes are width and height of the glyph\n") - f.write( - "// - the third, fourth and fifth bytes are advance, bearingX and bearingY of the horizontal metrics of the glyph\n" - ) - f.write("// - the rest is packed %d-bit glyph data\n\n" % bpp) - for i in range(MIN_GLYPH, MAX_GLYPH + 1): - c = chr(i) - face.load_char(c, freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_NORMAL) - bitmap = face.glyph.bitmap - metrics = face.glyph.metrics - assert metrics.width // 64 == bitmap.width - assert metrics.height // 64 == bitmap.rows - assert metrics.width % 64 == 0 - assert metrics.height % 64 == 0 - assert metrics.horiAdvance % 64 == 0 - assert metrics.horiBearingX % 64 == 0 - assert metrics.horiBearingY % 64 == 0 - assert bitmap.width == bitmap.pitch - assert len(bitmap.buffer) == bitmap.pitch * bitmap.rows - width = bitmap.width - rows = bitmap.rows - advance = metrics.horiAdvance // 64 - bearingX = metrics.horiBearingX // 64 - - remove_left = shaveX - # discard space on the left side - if shaveX > 0: - diff = min(advance, bearingX, shaveX) - advance -= diff - bearingX -= diff - remove_left -= diff - # the following code is here just for some letters (listed below) - # not using negative bearingX makes life so much easier; add it to advance instead - if bearingX < 0: - if c in "AXYjxy}),/_": - advance += -bearingX - bearingX = 0 - else: - raise ValueError("Negative bearingX for character '%s'" % c) - bearingY = metrics.horiBearingY // 64 - assert advance >= 0 and advance <= 255 - assert bearingX >= 0 and bearingX <= 255 - if bearingY < 0: # HACK - print("normalizing bearingY %d for '%s'" % (bearingY, c)) - bearingY = 0 - assert bearingY >= 0 and bearingY <= 255 - buf = list(bitmap.buffer) - # discard non-space pixels on the left side - if remove_left > 0 and width > 0: - assert bearingX == 0 - buf = drop_left_columns(buf, width, remove_left) - assert width > remove_left - width -= remove_left - assert advance > remove_left - advance -= remove_left - print( - 'Glyph "%c": removed %d pixel columns from the left' - % (c, remove_left) - ) +@dataclass +class Glyph: + char: str + width: int + rows: int + advance: int + bearingX: int + bearingY: int + buf: list[int] + num_grays: int + inverse_colors: bool = False + + @classmethod + def from_face( + cls, face: freetype.Face, c: str, shaveX: int, inverse_colors: bool = False + ) -> Glyph: + bitmap = face.glyph.bitmap + metrics = face.glyph.metrics + assert metrics.width // 64 == bitmap.width + assert metrics.height // 64 == bitmap.rows + assert metrics.width % 64 == 0 + assert metrics.height % 64 == 0 + assert metrics.horiAdvance % 64 == 0 + assert metrics.horiBearingX % 64 == 0 + assert metrics.horiBearingY % 64 == 0 + assert bitmap.width == bitmap.pitch + assert len(bitmap.buffer) == bitmap.pitch * bitmap.rows + width = bitmap.width + rows = bitmap.rows + advance = metrics.horiAdvance // 64 + bearingX = metrics.horiBearingX // 64 + + remove_left = shaveX + # discard space on the left side + if shaveX > 0: + diff = min(advance, bearingX, shaveX) + advance -= diff + bearingX -= diff + remove_left -= diff + # the following code is here just for some letters (listed below) + # not using negative bearingX makes life so much easier; add it to advance instead + if bearingX < 0: + if c in "ÀÂÆÎÏîïÿÝŸÁýAXYjxy}),/_": + advance += -bearingX + bearingX = 0 + else: + raise ValueError("Negative bearingX for character '%s'" % c) + bearingY = metrics.horiBearingY // 64 + assert advance >= 0 and advance <= 255 + assert bearingX >= 0 and bearingX <= 255 + if bearingY < 0: # HACK + print("normalizing bearingY %d for '%s'" % (bearingY, c)) + bearingY = 0 + assert bearingY >= 0 and bearingY <= 255 + buf = list(bitmap.buffer) + # discard non-space pixels on the left side + if remove_left > 0 and width > 0: + assert bearingX == 0 + buf = drop_left_columns(buf, width, remove_left) + assert width > remove_left + width -= remove_left + assert advance > remove_left + advance -= remove_left print( - 'Loaded glyph "%c" ... %d x %d @ %d grays (%d bytes, metrics: %d, %d, %d)' - % ( - c, - bitmap.width, - bitmap.rows, - bitmap.num_grays, - len(bitmap.buffer), - advance, - bearingX, - bearingY, - ) + 'Glyph "%c": removed %d pixel columns from the left' % (c, remove_left) ) - f.write( - "/* %c */ static const uint8_t Font_%s_%s_%d_glyph_%d[] = { %d, %d, %d, %d, %d" - % (c, name, style, size, i, width, rows, advance, bearingX, bearingY) + + return Glyph( + char=c, + width=width, + rows=rows, + advance=advance, + bearingX=bearingX, + bearingY=bearingY, + buf=buf, + num_grays=bitmap.num_grays, + inverse_colors=inverse_colors, + ) + + def print_metrics(self) -> None: + print( + 'Loaded glyph "%c" ... %d x %d @ %d grays (%d bytes, metrics: %d, %d, %d)' + % ( + self.char, + self.width, + self.rows, + self.num_grays, + len(self.buf), + self.advance, + self.bearingX, + self.bearingY, ) - if len(buf) > 0: - f.write( - ", " - + ", ".join(["%d" % x for x in process_bitmap_buffer(buf, bpp)]) - ) - f.write(" };\n") + ) - if i == ord("?"): - nonprintable = ( - "\nconst uint8_t Font_%s_%s_%d_glyph_nonprintable[] = { %d, %d, %d, %d, %d" - % (name, style, size, width, rows, advance, bearingX, bearingY) - ) - nonprintable += ", " + ", ".join( - ["%d" % (x ^ 0xFF) for x in process_bitmap_buffer(buf, bpp)] + def process_byte(self, b: int) -> int: + if self.inverse_colors: + return b ^ 0xFF + else: + return b + + def get_definition_line( + self, name_style_size: str, bpp: int, i: int | str, static: bool = True + ) -> str: + line = ( + "/* %c */ static const uint8_t Font_%s_glyph_%s[] = { %d, %d, %d, %d, %d" + % ( + self.char, + name_style_size, + i, + self.width, + self.rows, + self.advance, + self.bearingX, + self.bearingY, + ) + ) + + if len(self.buf) > 0: + line = line + ( + ", " + + ", ".join( + [ + "%d" % self.process_byte(x) + for x in process_bitmap_buffer(self.buf, bpp) + ] ) - nonprintable += " };\n" + ) + + line = line + " };\n" + + if not static: + line = line.replace("static const", "const") + + return line + + def get_json_list(self, bpp: int) -> list[int]: + infos = [ + self.width, + self.rows, + self.advance, + self.bearingX, + self.bearingY, + ] + data = [self.process_byte(x) for x in process_bitmap_buffer(self.buf, bpp)] + + return infos + data + + +class FaceProcessor: + def __init__( + self, + name: str, + style: str, + size: int, + bpp: int = 4, + shaveX: int = 0, + ext: str = "ttf", + ): + print("Processing ... %s %s %s" % (name, style, size)) + self.name = name + self.style = style + self.size = size + self.bpp = bpp + self.shaveX = shaveX + self.ext = ext + self.face = freetype.Face(str(FONTS_DIR / f"{name}-{style}.{ext}")) + self.face.set_pixel_sizes(0, size) # type: ignore + self.fontname = "%s_%s_%d" % (name.lower(), style.lower(), size) + self.font_ymin = 0 + self.font_ymax = 0 - yMin = bearingY - rows - yMax = yMin + rows - font_ymin = min(font_ymin, yMin) - font_ymax = max(font_ymax, yMax) + @property + def _name_style_size(self) -> str: + return f"{self.name}_{self.style}_{self.size}" + @property + def _c_file_name(self) -> str: + return f"font_{self.fontname}.c" + + @property + def _h_file_name(self) -> str: + return f"font_{self.fontname}.h" + + def write_files(self) -> None: + self.write_c_files() + self.write_foreign_json() + + def write_c_files(self) -> None: + self._write_c_file() + self._write_h_file() + + def write_foreign_json(self) -> None: + def int_list_to_hex(int_list: list[int]) -> str: + return "".join(f"{x:02x}" for x in int_list) + + for language in all_languages: + all_objects: list[dict[str, Any]] = [] + for item in language["data"]: + c = item[0] + self._load_char(c) + glyph = Glyph.from_face(self.face, c, self.shaveX) + glyph.print_metrics() + json_list = glyph.get_json_list(self.bpp) + obj = { + "char": c, + "utf8": item[1], + "data": int_list_to_hex(json_list), + } + print("obj", obj) + all_objects.append(obj) + filename = f"font_{self.fontname}_{language['name']}.json" + with open(filename, "w", encoding="utf-8") as f: + json_content = json.dumps(all_objects, indent=2, ensure_ascii=False) + f.write(json_content + "\n") + + def _write_c_file(self) -> None: + with open(self._c_file_name, "wt") as f: + self._write_c_file_header(f) + self._write_c_file_content(f) + + def _write_c_file_content(self, f: TextIO) -> None: + # Write "normal" ASCII characters + for i in range(MIN_GLYPH, MAX_GLYPH + 1): + c = chr(i) + self._write_char_definition(f, c, i) + + # Write non-printable character + f.write("\n") + nonprintable = self._get_nonprintable_definition_line() f.write(nonprintable) + # Write array of all glyphs + f.write("\n") f.write( - "\nconst uint8_t * const Font_%s_%s_%d[%d + 1 - %d] = {\n" - % (name, style, size, MAX_GLYPH, MIN_GLYPH) + "const uint8_t * const Font_%s[%d + 1 - %d] = {\n" + % (self._name_style_size, MAX_GLYPH, MIN_GLYPH) ) for i in range(MIN_GLYPH, MAX_GLYPH + 1): - f.write(" Font_%s_%s_%d_glyph_%d,\n" % (name, style, size, i)) + f.write(" Font_%s_glyph_%d,\n" % (self._name_style_size, i)) f.write("};\n") - with open("font_%s.h" % fontname, "wt") as f: - f.write("#include \n\n") - f.write("#if TREZOR_FONT_BPP != %d\n" % bpp) - f.write("#error Wrong TREZOR_FONT_BPP (expected %d)\n" % bpp) - f.write("#endif\n") + def _write_char_definition(self, f: TextIO, c: str, i: int) -> None: + self._load_char(c) + glyph = Glyph.from_face(self.face, c, self.shaveX) + glyph.print_metrics() + definition_line = glyph.get_definition_line(self._name_style_size, self.bpp, i) + f.write(definition_line) - f.write("#define Font_%s_%s_%d_HEIGHT %d\n" % (name, style, size, size)) - f.write( - "#define Font_%s_%s_%d_MAX_HEIGHT %d\n" - % (name, style, size, font_ymax - font_ymin) - ) - f.write("#define Font_%s_%s_%d_BASELINE %d\n" % (name, style, size, -font_ymin)) + # Update mix/max metrics + yMin = glyph.bearingY - glyph.rows + yMax = yMin + glyph.rows + self.font_ymin = min(self.font_ymin, yMin) + self.font_ymax = max(self.font_ymax, yMax) + + def _write_c_file_header(self, f: TextIO) -> None: + f.write("#include \n\n") + f.write("// clang-format off\n\n") + f.write("// - the first two bytes are width and height of the glyph\n") f.write( - "extern const uint8_t* const Font_%s_%s_%d[%d + 1 - %d];\n" - % (name, style, size, MAX_GLYPH, MIN_GLYPH) + "// - the third, fourth and fifth bytes are advance, bearingX and bearingY of the horizontal metrics of the glyph\n" ) - f.write( - "extern const uint8_t Font_%s_%s_%d_glyph_nonprintable[];\n" - % (name, style, size) + f.write("// - the rest is packed %d-bit glyph data\n\n" % self.bpp) + + def _get_nonprintable_definition_line(self) -> str: + c = "?" + self._load_char(c) + glyph = Glyph.from_face(self.face, c, self.shaveX, inverse_colors=True) + return glyph.get_definition_line( + self._name_style_size, self.bpp, "nonprintable", static=False ) + def _load_char(self, c: str) -> None: + self.face.load_char(c, freetype.FT_LOAD_RENDER | freetype.FT_LOAD_TARGET_NORMAL) # type: ignore + + def _write_h_file(self) -> None: + with open(self._h_file_name, "wt") as f: + f.write("#include \n\n") + f.write("#if TREZOR_FONT_BPP != %d\n" % self.bpp) + f.write("#error Wrong TREZOR_FONT_BPP (expected %d)\n" % self.bpp) + f.write("#endif\n") + + f.write("#define Font_%s_HEIGHT %d\n" % (self._name_style_size, self.size)) + f.write( + "#define Font_%s_MAX_HEIGHT %d\n" + % (self._name_style_size, self.font_ymax - self.font_ymin) + ) + f.write( + "#define Font_%s_BASELINE %d\n" + % (self._name_style_size, -self.font_ymin) + ) + f.write( + "extern const uint8_t* const Font_%s[%d + 1 - %d];\n" + % (self._name_style_size, MAX_GLYPH, MIN_GLYPH) + ) + f.write( + "extern const uint8_t Font_%s_glyph_nonprintable[];\n" + % (self._name_style_size) + ) + + +if __name__ == "__main__": + FaceProcessor("Roboto", "Regular", 20).write_files() + FaceProcessor("Roboto", "Bold", 20).write_files() -process_face("Roboto", "Regular", 20) -process_face("Roboto", "Bold", 20) + FaceProcessor("TTHoves", "Regular", 21, ext="otf").write_files() + FaceProcessor("TTHoves", "DemiBold", 21, ext="otf").write_files() + FaceProcessor("TTHoves", "Bold", 17, ext="otf").write_files() + FaceProcessor("RobotoMono", "Medium", 20).write_files() -process_face("TTHoves", "Regular", 21, ext="otf") -process_face("TTHoves", "DemiBold", 21, ext="otf") -process_face("TTHoves", "Bold", 17, ext="otf") -process_face("RobotoMono", "Medium", 20) + FaceProcessor("PixelOperator", "Regular", 8, bpp=1, shaveX=1).write_files() -process_face("PixelOperator", "Regular", 8, bpp=1, shaveX=1) -process_face("PixelOperator", "Bold", 8, bpp=1, shaveX=1) -process_face("PixelOperatorMono", "Regular", 8, bpp=1, shaveX=1) + FaceProcessor("PixelOperator", "Bold", 8, bpp=1, shaveX=1).write_files() + FaceProcessor("PixelOperatorMono", "Regular", 8, bpp=1, shaveX=1).write_files() -# For model R -process_face("Unifont", "Regular", 16, bpp=1, shaveX=1, ext="otf") -process_face("Unifont", "Bold", 16, bpp=1, shaveX=1, ext="otf") + # For model R + FaceProcessor("Unifont", "Regular", 16, bpp=1, shaveX=1, ext="otf").write_files() + # NOTE: Unifont Bold does not seem to have czech characters + FaceProcessor("Unifont", "Bold", 16, bpp=1, shaveX=1, ext="otf").write_files() diff --git a/core/tools/translations/.gitignore b/core/tools/translations/.gitignore new file mode 100644 index 000000000..a6c57f5fb --- /dev/null +++ b/core/tools/translations/.gitignore @@ -0,0 +1 @@ +*.json diff --git a/core/tools/translations/check_missing_rust.py b/core/tools/translations/check_missing_rust.py new file mode 100644 index 000000000..bab5bb773 --- /dev/null +++ b/core/tools/translations/check_missing_rust.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from pathlib import Path +import re +import json + + +HERE = Path(__file__).parent +CORE = HERE.parent.parent + +RUST_DIR = CORE / "embed" / "rust" / "src" / "ui" +MAPPING_FILE = HERE / "mapping_rust.json" + +IGNORE_FILES = ["cs.rs", "en.rs", "fr.rs", "general.rs", "fido_icons.rs"] +IGNORE_DIRS = ["bootloader"] + + +def extract_strings(file_path: Path) -> list[str]: + with open(file_path, "r") as file: + strings: list[str] = [] + line_starts_break = ["mod tests"] + line_starts_pause = ["fn trace"] + line_starts_continue = ["#[cfg", "//"] + in_line_continue = [ + "Err(", + "assert_if_debugging_ui", + "panic!(", + "value_error!(", + "unwrap!(", + ] + is_paused = False + for line in file: + if is_paused: + if line.startswith("}"): + is_paused = False + continue + if any([line.strip().startswith(start) for start in line_starts_break]): + break + if any([line.strip().startswith(start) for start in line_starts_pause]): + is_paused = True + continue + if any([line.strip().startswith(start) for start in line_starts_continue]): + continue + if any([substr in line for substr in in_line_continue]): + continue + new = re.findall(r'"(.*?)"', line) + strings.extend(new) + return strings + + +def ignore_string(string: str) -> bool: + if string.endswith(".toif"): + return True + if string.startswith("model_"): + return True + if string.startswith("ui_"): + return True + if len(string) < 4: + return True + if "__" in string: + return True + return False + + +if __name__ == "__main__": + all_rust_files = list(RUST_DIR.glob("**/*.rs")) + all_strings: dict[str, list[str]] = {} + for file_path in all_rust_files: + if file_path.name in IGNORE_FILES: + continue + if any([ignore_dir in str(file_path) for ignore_dir in IGNORE_DIRS]): + continue + strings = extract_strings(file_path) + strings = list(set(strings)) + strings = [s for s in strings if s] + strings = [string for string in strings if not ignore_string(string)] + if strings: + all_strings[str(file_path)] = strings + MAPPING_FILE.write_text(json.dumps(all_strings, indent=4)) diff --git a/core/tools/translations/check_missing_upy.py b/core/tools/translations/check_missing_upy.py new file mode 100644 index 000000000..ce1c1a57e --- /dev/null +++ b/core/tools/translations/check_missing_upy.py @@ -0,0 +1,274 @@ +from __future__ import annotations + +from pathlib import Path +import ast +import json +from typing import Any + + +HERE = Path(__file__).parent +CORE = HERE.parent.parent +CORE_SRC = CORE / "src" +KEY_PREFIX = "" + +MAPPING_FILE = HERE / "mapping_upy.json" +IGNORE_FILE = HERE / "ignore_upy.json" + +if IGNORE_FILE.exists(): + content = json.loads(IGNORE_FILE.read_text()) + IGNORE_SET: set[str] = set(content.keys()) +else: + IGNORE_SET = set() # type: ignore + + +def find_all_strings(filename: str | Path) -> list[str]: + with open(filename, "r") as file: + file_content = file.read() + + tree = ast.parse(file_content) + strings: list[str] = [] + + class StringVisitor(ast.NodeVisitor): + def visit_Str(self, node: ast.Str): + strings.append(node.s) + + def visit_JoinedStr(self, node: ast.JoinedStr): + for value in node.values: + if isinstance(value, ast.Str): + strings.append(value.s) + + visitor = StringVisitor() + visitor.visit(tree) + + return strings + + +def find_strings_to_ignore(filename: str | Path) -> list[str]: + with open(filename, "r") as file: + file_content = file.read() + + tree = ast.parse(file_content) + strings: list[str] = [] + + def ignore_func(func_name: str) -> bool: + if not func_name: + return True + substrs = ["Error", "Exception", "wire.", "log.", "ensure", "mem_trace"] + if any(substr in func_name for substr in substrs): + return True + if func_name in ( + "_log", + "info", + "warning", + "debug", + "Success", + "SdCardUnavailable", + "NotInitialized", + "ActionCancelled", + "UnexpectedMessage", + "NotEnoughFunds", + "Failure", + "PinCancelled", + "TypeVar", + "getattr", + "_validate_public_key", + "check_mem", + "halt", + "pack", + "mem_trace", + ): + return True + return False + + def get_final_attribute_name(node: ast.AST) -> str: + """Recursively extracts the final attribute name from a nested attribute expression.""" + if isinstance(node, ast.Name): + return node.id + elif isinstance(node, ast.Attribute): + return get_final_attribute_name(node.value) + "." + node.attr + return "" + + def include_all_strings(arg: ast.expr) -> None: + if isinstance(arg, ast.Str): + strings.append(arg.s) + elif isinstance(arg, ast.JoinedStr): + for value in arg.values: + if isinstance(value, ast.Str): + strings.append(value.s) + elif isinstance(value, ast.FormattedValue): + # This part is an expression inside an f-string + expr_as_str = ast.dump(value.value, annotate_fields=False) + strings.append(expr_as_str) + + class IgnoreStringVisitor(ast.NodeVisitor): + def visit_Call(self, node: ast.Call): + func_name = get_final_attribute_name(node.func) + if ignore_func(func_name): + for arg in node.args + [kw.value for kw in node.keywords]: + include_all_strings(arg) + # Continue visiting the children of this node (!!!Necessary!!!) + self.generic_visit(node) + + def visit_Assert(self, node: ast.Assert): + error_message = node.msg + if error_message: + include_all_strings(error_message) + self.generic_visit(node) + + def visit_Assign(self, node: ast.Assign): + ignore_variables = [ + "msg_wire", + "msg_type", + ] + for target in node.targets: + if isinstance(target, ast.Name) and target.id in ignore_variables: + value = node.value + include_all_strings(value) + self.generic_visit(node) + + def visit_FunctionDef(self, node: ast.FunctionDef): + for arg in node.args.args: + annotation = arg.annotation + if annotation: + include_all_strings(annotation) + return_annotation = node.returns + if return_annotation: + include_all_strings(return_annotation) + self.generic_visit(node) + + def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> Any: + for arg in node.args.args: + annotation = arg.annotation + if annotation: + include_all_strings(annotation) + return_annotation = node.returns + if return_annotation: + include_all_strings(return_annotation) + self.generic_visit(node) + + def visit_AnnAssign(self, node: ast.AnnAssign): + annotation = node.annotation + include_all_strings(annotation) + self.generic_visit(node) + + visitor = IgnoreStringVisitor() + visitor.visit(tree) + + # get all the top-level string comments + for node in tree.body: + if isinstance(node, ast.Expr) and isinstance( + node.value, (ast.Str, ast.JoinedStr) + ): + strings.append(node.value.s) # type: ignore + + return strings + + +def find_docstrings(filename: str | Path) -> list[str]: + with open(filename, "r") as file: + file_content = file.read() + + tree = ast.parse(file_content) + + functions = [ + f + for f in ast.walk(tree) + if isinstance(f, (ast.FunctionDef, ast.AsyncFunctionDef)) + ] + function_docs = [ast.get_docstring(f) for f in functions] + + classes = [c for c in ast.walk(tree) if isinstance(c, ast.ClassDef)] + class_docs = [ast.get_docstring(c) for c in classes] + + all_docstrings = function_docs + class_docs + + module_docstring = ast.get_docstring(tree) + if module_docstring: + all_docstrings.append(module_docstring) + + return [doc for doc in all_docstrings if doc] + + +def check_file(file: str | Path) -> list[str]: + all_strings = find_all_strings(file) + + def is_docstring(string: str) -> bool: + return "\n " in string or (string.startswith("\n") and string.endswith("\n")) + + all_strings = [string for string in all_strings if not is_docstring(string)] + ignore_strings = find_strings_to_ignore(file) + docstrings = find_docstrings(file) + + # Remove duplicates + all_strings = list(set(all_strings)) + ignore_strings = set(ignore_strings) + docstrings = set(docstrings) + + to_ignore = ignore_strings | docstrings + + # Remove strings that are passed to error and other non-translatable functions + return [s for s in all_strings if s not in to_ignore] + + +def check_file_report(file: str | Path) -> None: + all_files = {str(file): check_file(file)} + report_all_files(all_files) + + +def check_folder_resursive_report( + folder: str | Path, ignore_files: list[str] | None = None +) -> None: + if ignore_files is None: + ignore_files = [] + all_files: dict[str, list[str]] = {} + for file in Path(folder).rglob("*.py"): + if file.name in ignore_files: + continue + file_strings = check_file(file) + all_files[str(file)] = file_strings + report_all_files(all_files) + + +def report_all_files(all_files: dict[str, list[str]]) -> None: + str_mapping: dict[str, str] = {} + for _file, strings in all_files.items(): + for string in strings: + if "_" in string: + continue + str_id = ( + string.lower() + .strip() + .replace(" ", "_") + .replace("-", "_") + .replace(":", "") + .replace("?", "") + ) + if KEY_PREFIX: + str_id = f"{KEY_PREFIX}__{str_id}" + if str_id in IGNORE_SET: + continue + str_mapping[str_id] = string + MAPPING_FILE.write_text(json.dumps(str_mapping, indent=4)) + + +if __name__ == "__main__": + ignore_files = [ + "coininfo.py", + "nem_mosaics.py", + "knownapps.py", + "networks.py", + "tokens.py", + "all_modules.py", + "workflow_handlers.py", + "messages.py", + "errors.py", + ] + + # folder = CORE_SRC / "apps" + # folder = CORE_SRC / "trezor" + folder = CORE_SRC + check_folder_resursive_report(folder, ignore_files=ignore_files) + + # file = CORE_SRC / "trezor/ui/layouts/tt_v2/reset.py" + # KEY_PREFIX = "TR.reset" # type: ignore + # check_file_report(file) diff --git a/core/tools/translations/sort_keys.py b/core/tools/translations/sort_keys.py new file mode 100644 index 000000000..844597976 --- /dev/null +++ b/core/tools/translations/sort_keys.py @@ -0,0 +1,16 @@ +import json + +from validate_same_keys import DIR + +for lang in ["en", "cs", "fr"]: + lang_file = DIR / f"{lang}.json" + lang_data = json.loads(lang_file.read_text()) + + for section_name, section in lang_data["translations"].items(): + for key in section: + if "title" in key: + lang_data["translations"][section_name][key] = lang_data["translations"][section_name][key].upper() + + lang_file.write_text( + json.dumps(lang_data, indent=2, sort_keys=True, ensure_ascii=False) + "\n" + ) diff --git a/core/tools/translations/translate_missing.py b/core/tools/translations/translate_missing.py new file mode 100644 index 000000000..7e1f34b75 --- /dev/null +++ b/core/tools/translations/translate_missing.py @@ -0,0 +1,68 @@ +from googletrans import Translator +from typing import Any, Dict +import json + +from validate_same_keys import get_missing_lang_dict, DIR, MISSING_VALUE + + +def translate_dict( + d: Dict[str, Any], translator: Translator, from_lang: str, to_lang: str +) -> Dict[str, Any]: + new_dict: dict[str, Any] = {} + for key, value in d.items(): + if isinstance(value, dict): + new_dict[key] = translate_dict(value, translator, from_lang, to_lang) + else: + try: + translated_text = translator.translate( + value, src=from_lang, dest=to_lang + ).text + new_dict[key] = translated_text + except Exception as e: + print(f"Error translating {value}: {e}") + new_dict[key] = MISSING_VALUE + return new_dict + + +def update_nested_dict(target: dict, source: dict) -> None: + for key, value in target.items(): + if key in source: + if isinstance(value, dict): + update_nested_dict(value, source[key]) + else: + target[key] = source[key] + + +def extend_nested_dict(bigger: dict, smaller: dict) -> None: + for key, value in smaller.items(): + if key in bigger: + if isinstance(value, dict) and isinstance(bigger[key], dict): + extend_nested_dict(bigger[key], value) + else: + bigger[key] = value + else: + bigger[key] = value + + +if __name__ == "__main__": + translator = Translator() + TRANSLATE = True + + with open(DIR / "en.json", "r") as f: + en_dict = json.load(f)["translations"] + + for language in ["cs", "fr"]: + print(f"Translating to {language}") + missing = get_missing_lang_dict(language) + if TRANSLATE: + update_nested_dict(missing, en_dict) + translated_dict = translate_dict(missing, translator, "en", language) + else: + translated_dict = missing + print("translated_dict", translated_dict) + lang_file = DIR / f"{language}.json" + lang_data = json.loads(lang_file.read_text()) + extend_nested_dict(lang_data["translations"], translated_dict) + lang_file.write_text( + json.dumps(lang_data, indent=2, sort_keys=True, ensure_ascii=False) + "\n" + ) diff --git a/core/tools/translations/validate_same_keys.py b/core/tools/translations/validate_same_keys.py new file mode 100644 index 000000000..dd7f17737 --- /dev/null +++ b/core/tools/translations/validate_same_keys.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import json +from pathlib import Path +from typing import TYPE_CHECKING, Dict + +HERE = Path(__file__).parent +CORE = HERE.parent.parent + +DIR = CORE / "embed" / "rust" / "src" / "ui" / "translations" + +MISSING_VALUE = "TODO:missing" + +if TYPE_CHECKING: + DoubleDict = Dict[str, Dict[str, str]] + + +def get_all_json_keys(data: "DoubleDict") -> set[str]: + keys: set[str] = set() + for section_name, section in data.items(): + for k, _v in section.items(): + keys.add(f"{section_name}__{k}") + return keys + + +def get_missing_dict(missing_set: set[str]) -> "DoubleDict": + missing_dict: "DoubleDict" = {} + for missing in sorted(missing_set): + section_name, key = missing.split("__") + if section_name not in missing_dict: + missing_dict[section_name] = {} + missing_dict[section_name][key] = MISSING_VALUE + return missing_dict + + +def get_lang_keys(lang: str) -> set[str]: + lang_file = DIR / f"{lang}.json" + lang_data = json.loads(lang_file.read_text())["translations"] + return get_all_json_keys(lang_data) + + +def get_missing_lang_dict(lang: str) -> "DoubleDict": + lang_keys = get_lang_keys(lang) + en_keys = get_lang_keys("en") + return get_missing_dict(en_keys - lang_keys) + + +def do_check(lang: str, missing_file: Path) -> bool: + lang_keys = get_lang_keys(lang) + en_keys = get_lang_keys("en") + + if lang_keys == en_keys: + print(f"SUCCESS: {lang} and en files have the same keys") + return True + else: + print(f"{lang} and en files have different keys") + print(f"{lang} - en:", len(lang_keys - en_keys)) + print(f"en - {lang}:", len(en_keys - lang_keys)) + missing_lang = get_missing_dict(en_keys - lang_keys) + missing_en = get_missing_dict(lang_keys - en_keys) + missing_file.write_text(json.dumps(missing_lang, indent=2)) + print(f"Diff written into {missing_file}") + if missing_en: + print(f"Extra keys: {missing_en}") + return False + + +if __name__ == "__main__": + is_ok = True + is_ok &= do_check("cs", HERE / "missing_cs.json") + is_ok &= do_check("fr", HERE / "missing_fr.json") + if not is_ok: + print("ERROR: there were some inconsistencies") + exit(1) diff --git a/docs/ci/jobs.md b/docs/ci/jobs.md index e654ae7c2..790ed16cc 100644 --- a/docs/ci/jobs.md +++ b/docs/ci/jobs.md @@ -142,7 +142,7 @@ Bitcoin-only version. ## TEST stage - [test.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml) All the tests run test cases on the freshly built emulators from the previous `BUILD` stage. -Consists of **38 jobs** below: +Consists of **46 jobs** below: ### [core unit python test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L15) Python unit tests, checking core functionality. @@ -158,92 +158,108 @@ with the expected UI result. See artifacts for a comprehensive report of UI. See [docs/tests/ui-tests](../tests/ui-tests.md) for more info. -### [core device R test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L85) +### [core device test czech](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L85) -### [core device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L117) +### [core device test french](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L117) -### [core btconly device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L136) +### [core device R test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L149) + +### [core device R test czech](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L181) + +### [core device R test french](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L214) + +### [core device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L247) + +### [core btconly device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L266) Device tests excluding altcoins, only for BTC. -### [core btconly device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L156) +### [core btconly device asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L286) -### [core monero test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L177) +### [core monero test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L307) Monero tests. -### [core monero asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L197) +### [core monero asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L327) -### [core u2f test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L220) +### [core u2f test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L350) Tests for U2F and HID. -### [core u2f asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L239) +### [core u2f asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L369) -### [core fido2 test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L257) +### [core fido2 test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L387) FIDO2 device tests. -### [core fido2 asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L280) +### [core fido2 asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L410) -### [core click test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L300) +### [core click test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L430) Click tests - UI. See [docs/tests/click-tests](../tests/click-tests.md) for more info. -### [core click R test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L332) +### [core click test czech](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L460) + +### [core click test french](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L491) + +### [core click R test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L524) Click tests. See [docs/tests/click-tests](../tests/click-tests.md) for more info. -### [core click asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L361) +### [core click R test czech](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L553) + +### [core click R test french](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L583) + +### [core click asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L613) -### [core upgrade test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L382) +### [core upgrade test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L634) Upgrade tests. See [docs/tests/upgrade-tests](../tests/upgrade-tests.md) for more info. -### [core upgrade asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L401) +### [core upgrade asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L653) -### [core persistence test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L423) +### [core persistence test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L675) Persistence tests - UI. -### [core persistence asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L453) +### [core persistence asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L705) -### [core hwi test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L471) +### [core hwi test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L723) -### [crypto test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L490) +### [crypto test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L742) -### [legacy device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L522) +### [legacy device test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L774) Legacy device test - UI. -### [legacy asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L550) +### [legacy asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L802) -### [legacy btconly test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L562) +### [legacy btconly test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L814) -### [legacy btconly asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L582) +### [legacy btconly asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L834) -### [legacy upgrade test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L597) +### [legacy upgrade test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L849) -### [legacy upgrade asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L616) +### [legacy upgrade asan test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L868) -### [legacy hwi test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L637) +### [legacy hwi test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L889) -### [python test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L657) +### [python test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L909) -### [python support test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L676) +### [python support test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L928) -### [rust test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L685) +### [rust test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L937) -### [storage test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L695) +### [storage test](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L947) -### [core unix memory profiler](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L719) +### [core unix memory profiler](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L971) -### [core firmware flash size checker](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L745) +### [core firmware flash size checker](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L997) Finds out how much flash space we have left in the firmware build Fails if the free space is less than certain threshold -### [core firmware flash size compare master](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L758) +### [core firmware flash size compare master](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L1010) Compares the current flash space with the situation in the current master Fails if the new binary is significantly larger than the master one (the threshold is defined in the script, currently 5kb). Allowing fir failure, not to prevent the merge. Also generates a report with the current situation -### [connect test core](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L773) +### [connect test core](https://github.com/trezor/trezor-firmware/blob/master/ci/test.yml#L1025) --- ## TEST-HW stage - [test-hw.yml](https://github.com/trezor/trezor-firmware/blob/master/ci/test-hw.yml) diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index 06129b1d1..1822c369e 100644 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -9,7 +9,8 @@ SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdPro EthereumSignTypedData EthereumTypedDataStructRequest EthereumTypedDataStructAck \ EthereumTypedDataValueRequest EthereumTypedDataValueAck ShowDeviceTutorial \ UnlockBootloader AuthenticateDevice AuthenticityProof \ - Solana StellarClaimClaimableBalanceOp + Solana StellarClaimClaimableBalanceOp \ + ChangeLanguage TranslationDataRequest TranslationDataAck ifeq ($(BITCOIN_ONLY), 1) SKIPPED_MESSAGES += Ethereum NEM Stellar diff --git a/python/src/trezorlib/cli/settings.py b/python/src/trezorlib/cli/settings.py index 1cd5118ab..ee5aaf0cc 100644 --- a/python/src/trezorlib/cli/settings.py +++ b/python/src/trezorlib/cli/settings.py @@ -20,7 +20,7 @@ from typing import TYPE_CHECKING, Optional, cast import click -from .. import device, messages, toif +from .. import device, messages, toif, translations from . import AliasedGroup, ChoiceType, with_client if TYPE_CHECKING: @@ -203,6 +203,30 @@ def label(client: "TrezorClient", label: str) -> str: return device.apply_settings(client, label=label) +@cli.command() +@click.option("-f", "--file", type=str, help="Language JSON file with translations.") +@click.option("-u", "--url", help="Link to already created and signed blob.") +@click.option("-r", "--remove", is_flag=True, help="Switch back to english.") +@with_client +def language(client: "TrezorClient", file: str, url: str, remove: bool) -> str: + """Set new language with translations.""" + if file and url: + raise click.ClickException("Please provide only one of -f or -u") + + if remove: + language_data = b"" + else: + if file: + model = client.features.model + assert model is not None + language_data = translations.blob_from_file(Path(file), model) + elif url: + language_data = translations.blob_from_url(url) + else: + raise click.ClickException("Please provide either -f or -u") + return device.change_language(client, language_data=language_data) + + @cli.command() @click.argument("rotation", type=ChoiceType(ROTATION)) @with_client diff --git a/python/src/trezorlib/cli/trezorctl.py b/python/src/trezorlib/cli/trezorctl.py index e00c965ae..814690def 100755 --- a/python/src/trezorlib/cli/trezorctl.py +++ b/python/src/trezorlib/cli/trezorctl.py @@ -49,6 +49,7 @@ from . import ( solana, stellar, tezos, + utils, with_client, ) @@ -415,6 +416,7 @@ cli.add_command(settings.cli) cli.add_command(solana.cli) cli.add_command(stellar.cli) cli.add_command(tezos.cli) +cli.add_command(utils.cli) cli.add_command(firmware.cli) cli.add_command(debug.cli) diff --git a/python/src/trezorlib/cli/utils.py b/python/src/trezorlib/cli/utils.py new file mode 100644 index 000000000..ea9dc4e42 --- /dev/null +++ b/python/src/trezorlib/cli/utils.py @@ -0,0 +1,81 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2022 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +from pathlib import Path + +import click + +from .. import translations + + +@click.group(name="utils") +def cli() -> None: + """Utils commands.""" + + +@cli.command() +@click.option( + "-f", + "--file", + type=str, + help="Language JSON file with translations.", + required=True, +) +@click.option("-m", "--model", required=True) +def sign_translations(file: str, model: str) -> None: + """Sign translations blob.""" + file_path = Path(file) + if not file_path.exists(): + raise click.ClickException(f"File {file_path} does not exist.") + + file_info = translations.get_file_info(file_path) + language = file_info["language"] + version = file_info["version"] + supported_models = file_info["supported_models"] + if model not in supported_models: + raise click.ClickException( + "Fonts for model {} not found in file. Available models: {}".format( + model, supported_models + ) + ) + click.echo(f"Creating blob for language {language} version {version}") + + unsigned_blob = translations.blob_from_file(file_path, model, sign_dev=False) + signing = translations.Signing(unsigned_blob) + hash_to_sign = signing.hash_to_sign() + click.echo("Hash to sign: {}".format(hash_to_sign.hex())) + click.echo("Please sign this hash and paste the signature below.") + signature: str = click.prompt("Signature", type=str) + signature_bytes = bytes.fromhex(signature) + signed_blob = signing.apply_signature(signature_bytes) + + # TODO: this is a pain point of model name "Safe 3" with a space + model = model.replace(" ", "") + + output_file_name = f"translations_signed_{model}_{version}.dat" + output_file_path = file_path.parent / output_file_name + if output_file_path.exists(): + overwrite = click.confirm( + f"WARNING: File {output_file_path} already exists. Overwrite?" + ) + if overwrite: + click.echo("Overwriting file.") + else: + click.echo("Aborting and not overwriting file.") + return + with output_file_path.open("wb") as f: + f.write(signed_blob) + click.echo(f"Signed blob saved to {output_file_path}") diff --git a/python/src/trezorlib/device.py b/python/src/trezorlib/device.py index 1fa33dea6..309d88834 100644 --- a/python/src/trezorlib/device.py +++ b/python/src/trezorlib/device.py @@ -63,6 +63,27 @@ def apply_settings( return out +@expect(messages.Success, field="message", ret_type=str) +@session +def change_language( + client: "TrezorClient", + language_data: bytes, +) -> "MessageType": + msg = messages.ChangeLanguage(data_length=len(language_data)) + + response = client.call(msg) + while not isinstance(response, messages.Success): + assert isinstance(response, messages.TranslationDataRequest) + data_length = response.data_length + data_offset = response.data_offset + chunk = language_data[data_offset : data_offset + data_length] + response = client.call(messages.TranslationDataAck(data_chunk=chunk)) + + assert isinstance(response, messages.Success) + client.refresh_features() # changing the language in features + return response + + @expect(messages.Success, field="message", ret_type=str) @session def apply_flags(client: "TrezorClient", flags: int) -> "MessageType": diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 23940e5ff..296554d82 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -75,6 +75,9 @@ class MessageType(IntEnum): UnlockBootloader = 96 AuthenticateDevice = 97 AuthenticityProof = 98 + ChangeLanguage = 990 + TranslationDataRequest = 991 + TranslationDataAck = 992 SetU2FCounter = 63 GetNextU2FCounter = 80 NextU2FCounter = 81 @@ -3378,6 +3381,51 @@ class ApplySettings(protobuf.MessageType): self.hide_passphrase_from_host = hide_passphrase_from_host +class ChangeLanguage(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 990 + FIELDS = { + 1: protobuf.Field("data_length", "uint32", repeated=False, required=True), + } + + def __init__( + self, + *, + data_length: "int", + ) -> None: + self.data_length = data_length + + +class TranslationDataRequest(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 991 + FIELDS = { + 1: protobuf.Field("data_length", "uint32", repeated=False, required=True), + 2: protobuf.Field("data_offset", "uint32", repeated=False, required=True), + } + + def __init__( + self, + *, + data_length: "int", + data_offset: "int", + ) -> None: + self.data_length = data_length + self.data_offset = data_offset + + +class TranslationDataAck(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 992 + FIELDS = { + 1: protobuf.Field("data_chunk", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + data_chunk: "bytes", + ) -> None: + self.data_chunk = data_chunk + + class ApplyFlags(protobuf.MessageType): MESSAGE_WIRE_TYPE = 28 FIELDS = { diff --git a/python/src/trezorlib/translations.py b/python/src/trezorlib/translations.py new file mode 100644 index 000000000..ad0fa5eec --- /dev/null +++ b/python/src/trezorlib/translations.py @@ -0,0 +1,362 @@ +import json +import struct +from hashlib import sha256 +from pathlib import Path +from typing import Any, Dict, List, Tuple + +import requests +from typing_extensions import TypedDict + +from .translations_dev_sign import sign_with_dev_keys + +MAGIC = b"TRTR" +# All sections need to be aligned to 2 bytes for the offset tables using u16 to work properly +ALIGNMENT_BYTE = b"\x00" +HEADER_LEN = 256 +SIG_LEN = 65 +# TODO: why is the sigmask used/useful? +SIGMASK = (0b111).to_bytes(1, "little") + +TranslationData = Dict[str, Dict[str, str]] +HeaderData = Dict[str, str] +FontData = Dict[str, str] +OrderData = Dict[int, str] + + +class FileInfo(TypedDict): + language: str + version: str + supported_models: List[str] + + +# TODO: might create some tests for reading the resulting blob +# TODO: try to apply some compression of the blob + + +class Signing: + def __init__(self, data_without_sig: bytes) -> None: + self.data_without_sig = data_without_sig + + def perform_dev_signing(self) -> bytes: + """Signs the appropriate data and returns the blob with the signature""" + signature = self._get_dev_signature() + return self.apply_signature(signature) + + def _get_dev_signature(self) -> bytes: + """Returns the development signature of the data.""" + to_sign = self.hash_to_sign() + return sign_with_dev_keys(to_sign) + + def hash_to_sign(self) -> bytes: + """Returns the data that should be signed - hash of header with empty signature.""" + data_to_sign = self.data_without_sig[:HEADER_LEN] + for byte in data_to_sign[-SIG_LEN:]: + assert byte == 0, "Signature should be empty" + return sha256(data_to_sign).digest() + + def apply_signature(self, signature: bytes) -> bytes: + """Put signature data at the right location into the header.""" + assert len(signature) == SIG_LEN - 1, "Signature should be 64 bytes long" + to_write = SIGMASK + signature + assert len(to_write) == SIG_LEN, "Signature and sigmask should be 65 bytes long" + return ( + self.data_without_sig[: HEADER_LEN - SIG_LEN] + + to_write + + self.data_without_sig[HEADER_LEN:] + ) + + +def get_file_info(json_file: Path) -> FileInfo: + with open(json_file, "r") as f: + data = json.load(f) + header: HeaderData = data["header"] + font = data["font"] + + supported_models = list(font.keys()) + return { + "language": header["language"], + "version": header["version"], + "supported_models": supported_models, + } + + +def blob_from_file(json_file: Path, model: str, sign_dev: bool = True) -> bytes: + with open(json_file, "r") as f: + data = json.load(f) + file_dir = json_file.parent + font_dir = file_dir / "fonts" + order_json_file = file_dir / "order.json" + return blob_from_dict(data, font_dir, order_json_file, model, sign_dev) + + +def blob_from_url(url: str) -> bytes: + r = requests.get(url) + r.raise_for_status() + return r.content + + +def blob_from_dict( + data: Dict[str, Any], + font_dir: Path, + order_json_file: Path, + model: str, + sign_dev: bool = True, +) -> bytes: + header: HeaderData = data["header"] + translations: TranslationData = data["translations"] + font = data["font"] + if model not in font: + raise ValueError( + f"Font for model {model} not found --- use one of {list(font.keys())}" + ) + model_font: FontData = font[model] + order_raw: Dict[str, str] = json.loads(order_json_file.read_text()) + order: OrderData = {int(k): v for k, v in order_raw.items()} + blob = _blob_from_data(header, translations, model_font, font_dir, order) + if sign_dev: + blob = Signing(blob).perform_dev_signing() + return blob + + +def _blob_from_data( + header: HeaderData, + translations: TranslationData, + font: FontData, + font_dir: Path, + order: OrderData, +) -> bytes: + translations_blob, translations_num = _create_translations_blob(translations, order) + assert ( + len(translations_blob) % 2 == 0 + ), "Translations data should be aligned to 2 bytes" + + font_blob = _create_font_blob(font, font_dir) + assert len(font_blob) % 2 == 0, "Font data should be aligned to 2 bytes" + + data_blob = translations_blob + font_blob + assert len(data_blob) % 2 == 0, "Data should be aligned to 2 bytes" + + header_blob = _create_header_blob( + magic=MAGIC, + lang=header["language"], + version=header["version"], + data_length=len(data_blob), + translations_length=len(translations_blob), + translations_num=translations_num, + data_hash=sha256(data_blob).digest(), + change_language_title=header["change_language_title"], + change_language_prompt=header["change_language_prompt"], + ) + assert len(header_blob) == HEADER_LEN, "Header should be 256 bytes long" + + final_blob = header_blob + data_blob + assert len(final_blob) % 2 == 0, "Final blob should be aligned to 2 bytes" + + return final_blob + + +def _create_font_blob(font: FontData, font_dir: Path) -> bytearray: + """Example structure of the font dict: + (The beginning number corresponds to the C representation of each font) + { + "1_FONT_NORMAL": "font_tthoves_regular_21_cs.json", + "2_FONT_BOLD": "font_tthoves_bold_17_cs.json", + "3_FONT_MONO": "font_robotomono_medium_20_cs.json", + "4_FONT_BIG": null, + "5_FONT_DEMIBOLD": "font_tthoves_demibold_21_cs.json" + } + """ + num_fonts: list[tuple[int, Path]] = [] + for font_name, file_name in font.items(): + if not file_name: + continue + file_path = font_dir / file_name + font_num = int(font_name.split("_")[0]) + num_fonts.append((font_num, file_path)) + + data_length = len(num_fonts) + + blob = bytearray() + + # Data length (2 bytes) + blob += struct.pack("H", data_length) + + # Initialize Index Table + # Each item has 2 bytes for font_num + 2 bytes for offset + index_table_pos = len(blob) + index_table_item_size = 2 + 2 + blob.extend(bytearray(index_table_item_size * data_length)) + + # Append specific fonts and fill Index Table + offset = len(blob) + for font_num, file_path in sorted(num_fonts): + # Looks like pyright bug below + specific_font_data = _font_blob_from_file(file_path) # type: ignore [Argument of type "int" cannot be assigned to parameter "json_file"] + + assert ( + len(specific_font_data) % 2 == 0 + ), "Specific font data should be aligned to 2 bytes" + + # Update index table + struct.pack_into("HH", blob, index_table_pos, font_num, offset) + + # Append character data + blob.extend(specific_font_data) + + # Update offset and index_table_pos + offset += len(specific_font_data) + index_table_pos += index_table_item_size + + return blob + + +def _font_blob_from_file(json_file: Path) -> bytearray: + json_content = json.loads(json_file.read_text()) + data_length = len(json_content) + + blob = bytearray() + + # Data length (2 bytes) + blob += struct.pack("H", data_length) + + # Initialize Index Table + # Each item has 2 bytes for char_code + 2 bytes for offset + index_table_pos = len(blob) + index_table_item_size = 2 + 2 + blob.extend(bytearray(index_table_item_size * data_length)) + + # Append Character Data and fill Index Table + offset = len(blob) + for obj in json_content: + utf8_char_str = obj["utf8"] + assert len(utf8_char_str) == 4 + char_code = int(utf8_char_str, 16) + data = bytes.fromhex(obj["data"]) + + # Update index table + struct.pack_into("HH", blob, index_table_pos, char_code, offset) + + # Append character data + blob.extend(data) + + # Update offset and index_table_pos + offset += len(data) + index_table_pos += index_table_item_size + + if len(blob) % 2 == 1: + blob += ALIGNMENT_BYTE + + return blob + + +def _create_translations_blob( + translations: TranslationData, order: OrderData +) -> Tuple[bytearray, int]: + items_to_write: Dict[str, str] = {} + for section_name, section in translations.items(): + for k, v in section.items(): + name = f"{section_name}__{k}" + items_to_write[name] = v + + data_length = len(order) + + blob = bytearray() + + # Initialize Index Table + # Each item has 2 bytes for offset + index_table_pos = len(blob) + index_table_item_size = 2 + blob.extend(bytearray(index_table_item_size * data_length)) + + sorted_order = sorted(order.items(), key=lambda x: x[0]) + + # Append Translation Data and fill Index Table + offset = len(blob) + for _, name in sorted_order: + # Update index table + struct.pack_into("H", blob, index_table_pos, offset) + + # Append translation data + # Value might not be there, as the string may have been deleted in the past + value = items_to_write.get(name) + if value: + data = value.encode() + blob.extend(data) + offset += len(data) + + index_table_pos += index_table_item_size + + if len(blob) % 2 == 1: + blob += ALIGNMENT_BYTE + + return blob, data_length + + +def _create_header_blob( + magic: bytes, + lang: str, + version: str, + data_length: int, + translations_length: int, + translations_num: int, + data_hash: bytes, + change_language_title: str, + change_language_prompt: str, +) -> bytes: + header = b"" + + # Magic (4 bytes) + assert len(magic) == 4, "Magic should be 4 bytes long" + header += struct.pack("4s", magic) + + # Version (16 bytes) + assert len(version.encode()) <= 16, "Version string is too long" + header += struct.pack("16s", version.encode()) + + # Language name (32 bytes) + assert len(lang.encode()) <= 32, "Language name is too long" + header += struct.pack("32s", lang.encode()) + + # Data length (2 bytes) + assert 0 <= data_length <= 0xFFFF, "Data length should fit in two bytes" + header += struct.pack("H", data_length) + + # Translations length (2 bytes) + assert ( + 0 <= translations_length <= 0xFFFF + ), "Translations length should fit in two bytes" + header += struct.pack("H", translations_length) + + # Translations amount (2 bytes) + assert 0 <= translations_num <= 0xFFFF, "Translation num should fit in two bytes" + header += struct.pack("H", translations_num) + + # Data hash (32 bytes) + assert len(data_hash) == 32, "Data hash should be 32 bytes long" + header += struct.pack("32s", data_hash) + + # Change language title (20 bytes) + # Needs to be ASCII because it will be shown with english ASCII-only fonts + assert change_language_title.isascii(), "Change language title should be ascii" + assert ( + len(change_language_title.encode()) <= 20 + ), "Change language title is too long" + header += struct.pack("20s", change_language_title.encode()) + + # Change language prompt (40 bytes) + # Needs to be ASCII because it will be shown with english ASCII-only fonts + assert change_language_prompt.isascii(), "Change language prompt should be ascii" + assert ( + len(change_language_prompt.encode()) <= 40 + ), "Change language prompt is too long" + header += struct.pack("40s", change_language_prompt.encode()) + + assert ( + len(header) <= HEADER_LEN - SIG_LEN + ), "Not enough space for signature in header" + + # Fill rest with zeros + while not len(header) == HEADER_LEN: + header += struct.pack("B", 0) + + return header diff --git a/python/src/trezorlib/translations_dev_sign.py b/python/src/trezorlib/translations_dev_sign.py new file mode 100644 index 000000000..06b1a3940 --- /dev/null +++ b/python/src/trezorlib/translations_dev_sign.py @@ -0,0 +1,31 @@ +from pathlib import Path +from typing import Sequence + +from . import cosi + +# NOTE: the whole file taken from definitions repository + +HERE = Path(__file__).parent + +PRIVATE_KEYS_DEV = [byte * 32 for byte in (b"\xdd", b"\xde", b"\xdf")] + + +def sign_with_dev_keys(hash: bytes) -> bytes: + """Sign the hash with the development private key.""" + return _sign_with_privkeys(hash, PRIVATE_KEYS_DEV) + + +def _sign_with_privkeys(digest: bytes, privkeys: Sequence[bytes]) -> bytes: + """Locally produce a CoSi signature.""" + pubkeys = [cosi.pubkey_from_privkey(sk) for sk in privkeys] + nonces = [cosi.get_nonce(sk, digest, i) for i, sk in enumerate(privkeys)] + + global_pk = cosi.combine_keys(pubkeys) + global_R = cosi.combine_keys(R for _, R in nonces) + + sigs = [ + cosi.sign_with_privkey(digest, sk, global_pk, r, global_R) + for sk, (r, _) in zip(privkeys, nonces) + ] + + return cosi.combine_sig(global_R, sigs) diff --git a/rust/trezor-client/src/messages/generated.rs b/rust/trezor-client/src/messages/generated.rs index 6fd14de9f..db5e13217 100644 --- a/rust/trezor-client/src/messages/generated.rs +++ b/rust/trezor-client/src/messages/generated.rs @@ -45,6 +45,9 @@ trezor_message_impl! { UnlockBootloader => MessageType_UnlockBootloader, AuthenticateDevice => MessageType_AuthenticateDevice, AuthenticityProof => MessageType_AuthenticityProof, + ChangeLanguage => MessageType_ChangeLanguage, + TranslationDataRequest => MessageType_TranslationDataRequest, + TranslationDataAck => MessageType_TranslationDataAck, SetU2FCounter => MessageType_SetU2FCounter, GetNextU2FCounter => MessageType_GetNextU2FCounter, NextU2FCounter => MessageType_NextU2FCounter, diff --git a/rust/trezor-client/src/protos/generated/messages.rs b/rust/trezor-client/src/protos/generated/messages.rs index 962a88de5..6983b8a65 100644 --- a/rust/trezor-client/src/protos/generated/messages.rs +++ b/rust/trezor-client/src/protos/generated/messages.rs @@ -120,6 +120,12 @@ pub enum MessageType { MessageType_AuthenticateDevice = 97, // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_AuthenticityProof) MessageType_AuthenticityProof = 98, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_ChangeLanguage) + MessageType_ChangeLanguage = 990, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_TranslationDataRequest) + MessageType_TranslationDataRequest = 991, + // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_TranslationDataAck) + MessageType_TranslationDataAck = 992, // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_SetU2FCounter) MessageType_SetU2FCounter = 63, // @@protoc_insertion_point(enum_value:hw.trezor.messages.MessageType.MessageType_GetNextU2FCounter) @@ -565,6 +571,9 @@ impl ::protobuf::Enum for MessageType { 96 => ::std::option::Option::Some(MessageType::MessageType_UnlockBootloader), 97 => ::std::option::Option::Some(MessageType::MessageType_AuthenticateDevice), 98 => ::std::option::Option::Some(MessageType::MessageType_AuthenticityProof), + 990 => ::std::option::Option::Some(MessageType::MessageType_ChangeLanguage), + 991 => ::std::option::Option::Some(MessageType::MessageType_TranslationDataRequest), + 992 => ::std::option::Option::Some(MessageType::MessageType_TranslationDataAck), 63 => ::std::option::Option::Some(MessageType::MessageType_SetU2FCounter), 80 => ::std::option::Option::Some(MessageType::MessageType_GetNextU2FCounter), 81 => ::std::option::Option::Some(MessageType::MessageType_NextU2FCounter), @@ -811,6 +820,9 @@ impl ::protobuf::Enum for MessageType { "MessageType_UnlockBootloader" => ::std::option::Option::Some(MessageType::MessageType_UnlockBootloader), "MessageType_AuthenticateDevice" => ::std::option::Option::Some(MessageType::MessageType_AuthenticateDevice), "MessageType_AuthenticityProof" => ::std::option::Option::Some(MessageType::MessageType_AuthenticityProof), + "MessageType_ChangeLanguage" => ::std::option::Option::Some(MessageType::MessageType_ChangeLanguage), + "MessageType_TranslationDataRequest" => ::std::option::Option::Some(MessageType::MessageType_TranslationDataRequest), + "MessageType_TranslationDataAck" => ::std::option::Option::Some(MessageType::MessageType_TranslationDataAck), "MessageType_SetU2FCounter" => ::std::option::Option::Some(MessageType::MessageType_SetU2FCounter), "MessageType_GetNextU2FCounter" => ::std::option::Option::Some(MessageType::MessageType_GetNextU2FCounter), "MessageType_NextU2FCounter" => ::std::option::Option::Some(MessageType::MessageType_NextU2FCounter), @@ -1056,6 +1068,9 @@ impl ::protobuf::Enum for MessageType { MessageType::MessageType_UnlockBootloader, MessageType::MessageType_AuthenticateDevice, MessageType::MessageType_AuthenticityProof, + MessageType::MessageType_ChangeLanguage, + MessageType::MessageType_TranslationDataRequest, + MessageType::MessageType_TranslationDataAck, MessageType::MessageType_SetU2FCounter, MessageType::MessageType_GetNextU2FCounter, MessageType::MessageType_NextU2FCounter, @@ -1307,200 +1322,203 @@ impl ::protobuf::EnumFull for MessageType { MessageType::MessageType_UnlockBootloader => 43, MessageType::MessageType_AuthenticateDevice => 44, MessageType::MessageType_AuthenticityProof => 45, - MessageType::MessageType_SetU2FCounter => 46, - MessageType::MessageType_GetNextU2FCounter => 47, - MessageType::MessageType_NextU2FCounter => 48, - MessageType::MessageType_Deprecated_PassphraseStateRequest => 49, - MessageType::MessageType_Deprecated_PassphraseStateAck => 50, - MessageType::MessageType_FirmwareErase => 51, - MessageType::MessageType_FirmwareUpload => 52, - MessageType::MessageType_FirmwareRequest => 53, - MessageType::MessageType_SelfTest => 54, - MessageType::MessageType_GetPublicKey => 55, - MessageType::MessageType_PublicKey => 56, - MessageType::MessageType_SignTx => 57, - MessageType::MessageType_TxRequest => 58, - MessageType::MessageType_TxAck => 59, - MessageType::MessageType_GetAddress => 60, - MessageType::MessageType_Address => 61, - MessageType::MessageType_TxAckPaymentRequest => 62, - MessageType::MessageType_SignMessage => 63, - MessageType::MessageType_VerifyMessage => 64, - MessageType::MessageType_MessageSignature => 65, - MessageType::MessageType_GetOwnershipId => 66, - MessageType::MessageType_OwnershipId => 67, - MessageType::MessageType_GetOwnershipProof => 68, - MessageType::MessageType_OwnershipProof => 69, - MessageType::MessageType_AuthorizeCoinJoin => 70, - MessageType::MessageType_CipherKeyValue => 71, - MessageType::MessageType_CipheredKeyValue => 72, - MessageType::MessageType_SignIdentity => 73, - MessageType::MessageType_SignedIdentity => 74, - MessageType::MessageType_GetECDHSessionKey => 75, - MessageType::MessageType_ECDHSessionKey => 76, - MessageType::MessageType_CosiCommit => 77, - MessageType::MessageType_CosiCommitment => 78, - MessageType::MessageType_CosiSign => 79, - MessageType::MessageType_CosiSignature => 80, - MessageType::MessageType_DebugLinkDecision => 81, - MessageType::MessageType_DebugLinkGetState => 82, - MessageType::MessageType_DebugLinkState => 83, - MessageType::MessageType_DebugLinkStop => 84, - MessageType::MessageType_DebugLinkLog => 85, - MessageType::MessageType_DebugLinkMemoryRead => 86, - MessageType::MessageType_DebugLinkMemory => 87, - MessageType::MessageType_DebugLinkMemoryWrite => 88, - MessageType::MessageType_DebugLinkFlashErase => 89, - MessageType::MessageType_DebugLinkLayout => 90, - MessageType::MessageType_DebugLinkReseedRandom => 91, - MessageType::MessageType_DebugLinkRecordScreen => 92, - MessageType::MessageType_DebugLinkEraseSdCard => 93, - MessageType::MessageType_DebugLinkWatchLayout => 94, - MessageType::MessageType_DebugLinkResetDebugEvents => 95, - MessageType::MessageType_EthereumGetPublicKey => 96, - MessageType::MessageType_EthereumPublicKey => 97, - MessageType::MessageType_EthereumGetAddress => 98, - MessageType::MessageType_EthereumAddress => 99, - MessageType::MessageType_EthereumSignTx => 100, - MessageType::MessageType_EthereumSignTxEIP1559 => 101, - MessageType::MessageType_EthereumTxRequest => 102, - MessageType::MessageType_EthereumTxAck => 103, - MessageType::MessageType_EthereumSignMessage => 104, - MessageType::MessageType_EthereumVerifyMessage => 105, - MessageType::MessageType_EthereumMessageSignature => 106, - MessageType::MessageType_EthereumSignTypedData => 107, - MessageType::MessageType_EthereumTypedDataStructRequest => 108, - MessageType::MessageType_EthereumTypedDataStructAck => 109, - MessageType::MessageType_EthereumTypedDataValueRequest => 110, - MessageType::MessageType_EthereumTypedDataValueAck => 111, - MessageType::MessageType_EthereumTypedDataSignature => 112, - MessageType::MessageType_EthereumSignTypedHash => 113, - MessageType::MessageType_NEMGetAddress => 114, - MessageType::MessageType_NEMAddress => 115, - MessageType::MessageType_NEMSignTx => 116, - MessageType::MessageType_NEMSignedTx => 117, - MessageType::MessageType_NEMDecryptMessage => 118, - MessageType::MessageType_NEMDecryptedMessage => 119, - MessageType::MessageType_TezosGetAddress => 120, - MessageType::MessageType_TezosAddress => 121, - MessageType::MessageType_TezosSignTx => 122, - MessageType::MessageType_TezosSignedTx => 123, - MessageType::MessageType_TezosGetPublicKey => 124, - MessageType::MessageType_TezosPublicKey => 125, - MessageType::MessageType_StellarSignTx => 126, - MessageType::MessageType_StellarTxOpRequest => 127, - MessageType::MessageType_StellarGetAddress => 128, - MessageType::MessageType_StellarAddress => 129, - MessageType::MessageType_StellarCreateAccountOp => 130, - MessageType::MessageType_StellarPaymentOp => 131, - MessageType::MessageType_StellarPathPaymentStrictReceiveOp => 132, - MessageType::MessageType_StellarManageSellOfferOp => 133, - MessageType::MessageType_StellarCreatePassiveSellOfferOp => 134, - MessageType::MessageType_StellarSetOptionsOp => 135, - MessageType::MessageType_StellarChangeTrustOp => 136, - MessageType::MessageType_StellarAllowTrustOp => 137, - MessageType::MessageType_StellarAccountMergeOp => 138, - MessageType::MessageType_StellarManageDataOp => 139, - MessageType::MessageType_StellarBumpSequenceOp => 140, - MessageType::MessageType_StellarManageBuyOfferOp => 141, - MessageType::MessageType_StellarPathPaymentStrictSendOp => 142, - MessageType::MessageType_StellarClaimClaimableBalanceOp => 143, - MessageType::MessageType_StellarSignedTx => 144, - MessageType::MessageType_CardanoGetPublicKey => 145, - MessageType::MessageType_CardanoPublicKey => 146, - MessageType::MessageType_CardanoGetAddress => 147, - MessageType::MessageType_CardanoAddress => 148, - MessageType::MessageType_CardanoTxItemAck => 149, - MessageType::MessageType_CardanoTxAuxiliaryDataSupplement => 150, - MessageType::MessageType_CardanoTxWitnessRequest => 151, - MessageType::MessageType_CardanoTxWitnessResponse => 152, - MessageType::MessageType_CardanoTxHostAck => 153, - MessageType::MessageType_CardanoTxBodyHash => 154, - MessageType::MessageType_CardanoSignTxFinished => 155, - MessageType::MessageType_CardanoSignTxInit => 156, - MessageType::MessageType_CardanoTxInput => 157, - MessageType::MessageType_CardanoTxOutput => 158, - MessageType::MessageType_CardanoAssetGroup => 159, - MessageType::MessageType_CardanoToken => 160, - MessageType::MessageType_CardanoTxCertificate => 161, - MessageType::MessageType_CardanoTxWithdrawal => 162, - MessageType::MessageType_CardanoTxAuxiliaryData => 163, - MessageType::MessageType_CardanoPoolOwner => 164, - MessageType::MessageType_CardanoPoolRelayParameters => 165, - MessageType::MessageType_CardanoGetNativeScriptHash => 166, - MessageType::MessageType_CardanoNativeScriptHash => 167, - MessageType::MessageType_CardanoTxMint => 168, - MessageType::MessageType_CardanoTxCollateralInput => 169, - MessageType::MessageType_CardanoTxRequiredSigner => 170, - MessageType::MessageType_CardanoTxInlineDatumChunk => 171, - MessageType::MessageType_CardanoTxReferenceScriptChunk => 172, - MessageType::MessageType_CardanoTxReferenceInput => 173, - MessageType::MessageType_RippleGetAddress => 174, - MessageType::MessageType_RippleAddress => 175, - MessageType::MessageType_RippleSignTx => 176, - MessageType::MessageType_RippleSignedTx => 177, - MessageType::MessageType_MoneroTransactionInitRequest => 178, - MessageType::MessageType_MoneroTransactionInitAck => 179, - MessageType::MessageType_MoneroTransactionSetInputRequest => 180, - MessageType::MessageType_MoneroTransactionSetInputAck => 181, - MessageType::MessageType_MoneroTransactionInputViniRequest => 182, - MessageType::MessageType_MoneroTransactionInputViniAck => 183, - MessageType::MessageType_MoneroTransactionAllInputsSetRequest => 184, - MessageType::MessageType_MoneroTransactionAllInputsSetAck => 185, - MessageType::MessageType_MoneroTransactionSetOutputRequest => 186, - MessageType::MessageType_MoneroTransactionSetOutputAck => 187, - MessageType::MessageType_MoneroTransactionAllOutSetRequest => 188, - MessageType::MessageType_MoneroTransactionAllOutSetAck => 189, - MessageType::MessageType_MoneroTransactionSignInputRequest => 190, - MessageType::MessageType_MoneroTransactionSignInputAck => 191, - MessageType::MessageType_MoneroTransactionFinalRequest => 192, - MessageType::MessageType_MoneroTransactionFinalAck => 193, - MessageType::MessageType_MoneroKeyImageExportInitRequest => 194, - MessageType::MessageType_MoneroKeyImageExportInitAck => 195, - MessageType::MessageType_MoneroKeyImageSyncStepRequest => 196, - MessageType::MessageType_MoneroKeyImageSyncStepAck => 197, - MessageType::MessageType_MoneroKeyImageSyncFinalRequest => 198, - MessageType::MessageType_MoneroKeyImageSyncFinalAck => 199, - MessageType::MessageType_MoneroGetAddress => 200, - MessageType::MessageType_MoneroAddress => 201, - MessageType::MessageType_MoneroGetWatchKey => 202, - MessageType::MessageType_MoneroWatchKey => 203, - MessageType::MessageType_DebugMoneroDiagRequest => 204, - MessageType::MessageType_DebugMoneroDiagAck => 205, - MessageType::MessageType_MoneroGetTxKeyRequest => 206, - MessageType::MessageType_MoneroGetTxKeyAck => 207, - MessageType::MessageType_MoneroLiveRefreshStartRequest => 208, - MessageType::MessageType_MoneroLiveRefreshStartAck => 209, - MessageType::MessageType_MoneroLiveRefreshStepRequest => 210, - MessageType::MessageType_MoneroLiveRefreshStepAck => 211, - MessageType::MessageType_MoneroLiveRefreshFinalRequest => 212, - MessageType::MessageType_MoneroLiveRefreshFinalAck => 213, - MessageType::MessageType_EosGetPublicKey => 214, - MessageType::MessageType_EosPublicKey => 215, - MessageType::MessageType_EosSignTx => 216, - MessageType::MessageType_EosTxActionRequest => 217, - MessageType::MessageType_EosTxActionAck => 218, - MessageType::MessageType_EosSignedTx => 219, - MessageType::MessageType_BinanceGetAddress => 220, - MessageType::MessageType_BinanceAddress => 221, - MessageType::MessageType_BinanceGetPublicKey => 222, - MessageType::MessageType_BinancePublicKey => 223, - MessageType::MessageType_BinanceSignTx => 224, - MessageType::MessageType_BinanceTxRequest => 225, - MessageType::MessageType_BinanceTransferMsg => 226, - MessageType::MessageType_BinanceOrderMsg => 227, - MessageType::MessageType_BinanceCancelMsg => 228, - MessageType::MessageType_BinanceSignedTx => 229, - MessageType::MessageType_WebAuthnListResidentCredentials => 230, - MessageType::MessageType_WebAuthnCredentials => 231, - MessageType::MessageType_WebAuthnAddResidentCredential => 232, - MessageType::MessageType_WebAuthnRemoveResidentCredential => 233, - MessageType::MessageType_SolanaGetPublicKey => 234, - MessageType::MessageType_SolanaPublicKey => 235, - MessageType::MessageType_SolanaGetAddress => 236, - MessageType::MessageType_SolanaAddress => 237, - MessageType::MessageType_SolanaSignTx => 238, - MessageType::MessageType_SolanaTxSignature => 239, + MessageType::MessageType_ChangeLanguage => 46, + MessageType::MessageType_TranslationDataRequest => 47, + MessageType::MessageType_TranslationDataAck => 48, + MessageType::MessageType_SetU2FCounter => 49, + MessageType::MessageType_GetNextU2FCounter => 50, + MessageType::MessageType_NextU2FCounter => 51, + MessageType::MessageType_Deprecated_PassphraseStateRequest => 52, + MessageType::MessageType_Deprecated_PassphraseStateAck => 53, + MessageType::MessageType_FirmwareErase => 54, + MessageType::MessageType_FirmwareUpload => 55, + MessageType::MessageType_FirmwareRequest => 56, + MessageType::MessageType_SelfTest => 57, + MessageType::MessageType_GetPublicKey => 58, + MessageType::MessageType_PublicKey => 59, + MessageType::MessageType_SignTx => 60, + MessageType::MessageType_TxRequest => 61, + MessageType::MessageType_TxAck => 62, + MessageType::MessageType_GetAddress => 63, + MessageType::MessageType_Address => 64, + MessageType::MessageType_TxAckPaymentRequest => 65, + MessageType::MessageType_SignMessage => 66, + MessageType::MessageType_VerifyMessage => 67, + MessageType::MessageType_MessageSignature => 68, + MessageType::MessageType_GetOwnershipId => 69, + MessageType::MessageType_OwnershipId => 70, + MessageType::MessageType_GetOwnershipProof => 71, + MessageType::MessageType_OwnershipProof => 72, + MessageType::MessageType_AuthorizeCoinJoin => 73, + MessageType::MessageType_CipherKeyValue => 74, + MessageType::MessageType_CipheredKeyValue => 75, + MessageType::MessageType_SignIdentity => 76, + MessageType::MessageType_SignedIdentity => 77, + MessageType::MessageType_GetECDHSessionKey => 78, + MessageType::MessageType_ECDHSessionKey => 79, + MessageType::MessageType_CosiCommit => 80, + MessageType::MessageType_CosiCommitment => 81, + MessageType::MessageType_CosiSign => 82, + MessageType::MessageType_CosiSignature => 83, + MessageType::MessageType_DebugLinkDecision => 84, + MessageType::MessageType_DebugLinkGetState => 85, + MessageType::MessageType_DebugLinkState => 86, + MessageType::MessageType_DebugLinkStop => 87, + MessageType::MessageType_DebugLinkLog => 88, + MessageType::MessageType_DebugLinkMemoryRead => 89, + MessageType::MessageType_DebugLinkMemory => 90, + MessageType::MessageType_DebugLinkMemoryWrite => 91, + MessageType::MessageType_DebugLinkFlashErase => 92, + MessageType::MessageType_DebugLinkLayout => 93, + MessageType::MessageType_DebugLinkReseedRandom => 94, + MessageType::MessageType_DebugLinkRecordScreen => 95, + MessageType::MessageType_DebugLinkEraseSdCard => 96, + MessageType::MessageType_DebugLinkWatchLayout => 97, + MessageType::MessageType_DebugLinkResetDebugEvents => 98, + MessageType::MessageType_EthereumGetPublicKey => 99, + MessageType::MessageType_EthereumPublicKey => 100, + MessageType::MessageType_EthereumGetAddress => 101, + MessageType::MessageType_EthereumAddress => 102, + MessageType::MessageType_EthereumSignTx => 103, + MessageType::MessageType_EthereumSignTxEIP1559 => 104, + MessageType::MessageType_EthereumTxRequest => 105, + MessageType::MessageType_EthereumTxAck => 106, + MessageType::MessageType_EthereumSignMessage => 107, + MessageType::MessageType_EthereumVerifyMessage => 108, + MessageType::MessageType_EthereumMessageSignature => 109, + MessageType::MessageType_EthereumSignTypedData => 110, + MessageType::MessageType_EthereumTypedDataStructRequest => 111, + MessageType::MessageType_EthereumTypedDataStructAck => 112, + MessageType::MessageType_EthereumTypedDataValueRequest => 113, + MessageType::MessageType_EthereumTypedDataValueAck => 114, + MessageType::MessageType_EthereumTypedDataSignature => 115, + MessageType::MessageType_EthereumSignTypedHash => 116, + MessageType::MessageType_NEMGetAddress => 117, + MessageType::MessageType_NEMAddress => 118, + MessageType::MessageType_NEMSignTx => 119, + MessageType::MessageType_NEMSignedTx => 120, + MessageType::MessageType_NEMDecryptMessage => 121, + MessageType::MessageType_NEMDecryptedMessage => 122, + MessageType::MessageType_TezosGetAddress => 123, + MessageType::MessageType_TezosAddress => 124, + MessageType::MessageType_TezosSignTx => 125, + MessageType::MessageType_TezosSignedTx => 126, + MessageType::MessageType_TezosGetPublicKey => 127, + MessageType::MessageType_TezosPublicKey => 128, + MessageType::MessageType_StellarSignTx => 129, + MessageType::MessageType_StellarTxOpRequest => 130, + MessageType::MessageType_StellarGetAddress => 131, + MessageType::MessageType_StellarAddress => 132, + MessageType::MessageType_StellarCreateAccountOp => 133, + MessageType::MessageType_StellarPaymentOp => 134, + MessageType::MessageType_StellarPathPaymentStrictReceiveOp => 135, + MessageType::MessageType_StellarManageSellOfferOp => 136, + MessageType::MessageType_StellarCreatePassiveSellOfferOp => 137, + MessageType::MessageType_StellarSetOptionsOp => 138, + MessageType::MessageType_StellarChangeTrustOp => 139, + MessageType::MessageType_StellarAllowTrustOp => 140, + MessageType::MessageType_StellarAccountMergeOp => 141, + MessageType::MessageType_StellarManageDataOp => 142, + MessageType::MessageType_StellarBumpSequenceOp => 143, + MessageType::MessageType_StellarManageBuyOfferOp => 144, + MessageType::MessageType_StellarPathPaymentStrictSendOp => 145, + MessageType::MessageType_StellarClaimClaimableBalanceOp => 146, + MessageType::MessageType_StellarSignedTx => 147, + MessageType::MessageType_CardanoGetPublicKey => 148, + MessageType::MessageType_CardanoPublicKey => 149, + MessageType::MessageType_CardanoGetAddress => 150, + MessageType::MessageType_CardanoAddress => 151, + MessageType::MessageType_CardanoTxItemAck => 152, + MessageType::MessageType_CardanoTxAuxiliaryDataSupplement => 153, + MessageType::MessageType_CardanoTxWitnessRequest => 154, + MessageType::MessageType_CardanoTxWitnessResponse => 155, + MessageType::MessageType_CardanoTxHostAck => 156, + MessageType::MessageType_CardanoTxBodyHash => 157, + MessageType::MessageType_CardanoSignTxFinished => 158, + MessageType::MessageType_CardanoSignTxInit => 159, + MessageType::MessageType_CardanoTxInput => 160, + MessageType::MessageType_CardanoTxOutput => 161, + MessageType::MessageType_CardanoAssetGroup => 162, + MessageType::MessageType_CardanoToken => 163, + MessageType::MessageType_CardanoTxCertificate => 164, + MessageType::MessageType_CardanoTxWithdrawal => 165, + MessageType::MessageType_CardanoTxAuxiliaryData => 166, + MessageType::MessageType_CardanoPoolOwner => 167, + MessageType::MessageType_CardanoPoolRelayParameters => 168, + MessageType::MessageType_CardanoGetNativeScriptHash => 169, + MessageType::MessageType_CardanoNativeScriptHash => 170, + MessageType::MessageType_CardanoTxMint => 171, + MessageType::MessageType_CardanoTxCollateralInput => 172, + MessageType::MessageType_CardanoTxRequiredSigner => 173, + MessageType::MessageType_CardanoTxInlineDatumChunk => 174, + MessageType::MessageType_CardanoTxReferenceScriptChunk => 175, + MessageType::MessageType_CardanoTxReferenceInput => 176, + MessageType::MessageType_RippleGetAddress => 177, + MessageType::MessageType_RippleAddress => 178, + MessageType::MessageType_RippleSignTx => 179, + MessageType::MessageType_RippleSignedTx => 180, + MessageType::MessageType_MoneroTransactionInitRequest => 181, + MessageType::MessageType_MoneroTransactionInitAck => 182, + MessageType::MessageType_MoneroTransactionSetInputRequest => 183, + MessageType::MessageType_MoneroTransactionSetInputAck => 184, + MessageType::MessageType_MoneroTransactionInputViniRequest => 185, + MessageType::MessageType_MoneroTransactionInputViniAck => 186, + MessageType::MessageType_MoneroTransactionAllInputsSetRequest => 187, + MessageType::MessageType_MoneroTransactionAllInputsSetAck => 188, + MessageType::MessageType_MoneroTransactionSetOutputRequest => 189, + MessageType::MessageType_MoneroTransactionSetOutputAck => 190, + MessageType::MessageType_MoneroTransactionAllOutSetRequest => 191, + MessageType::MessageType_MoneroTransactionAllOutSetAck => 192, + MessageType::MessageType_MoneroTransactionSignInputRequest => 193, + MessageType::MessageType_MoneroTransactionSignInputAck => 194, + MessageType::MessageType_MoneroTransactionFinalRequest => 195, + MessageType::MessageType_MoneroTransactionFinalAck => 196, + MessageType::MessageType_MoneroKeyImageExportInitRequest => 197, + MessageType::MessageType_MoneroKeyImageExportInitAck => 198, + MessageType::MessageType_MoneroKeyImageSyncStepRequest => 199, + MessageType::MessageType_MoneroKeyImageSyncStepAck => 200, + MessageType::MessageType_MoneroKeyImageSyncFinalRequest => 201, + MessageType::MessageType_MoneroKeyImageSyncFinalAck => 202, + MessageType::MessageType_MoneroGetAddress => 203, + MessageType::MessageType_MoneroAddress => 204, + MessageType::MessageType_MoneroGetWatchKey => 205, + MessageType::MessageType_MoneroWatchKey => 206, + MessageType::MessageType_DebugMoneroDiagRequest => 207, + MessageType::MessageType_DebugMoneroDiagAck => 208, + MessageType::MessageType_MoneroGetTxKeyRequest => 209, + MessageType::MessageType_MoneroGetTxKeyAck => 210, + MessageType::MessageType_MoneroLiveRefreshStartRequest => 211, + MessageType::MessageType_MoneroLiveRefreshStartAck => 212, + MessageType::MessageType_MoneroLiveRefreshStepRequest => 213, + MessageType::MessageType_MoneroLiveRefreshStepAck => 214, + MessageType::MessageType_MoneroLiveRefreshFinalRequest => 215, + MessageType::MessageType_MoneroLiveRefreshFinalAck => 216, + MessageType::MessageType_EosGetPublicKey => 217, + MessageType::MessageType_EosPublicKey => 218, + MessageType::MessageType_EosSignTx => 219, + MessageType::MessageType_EosTxActionRequest => 220, + MessageType::MessageType_EosTxActionAck => 221, + MessageType::MessageType_EosSignedTx => 222, + MessageType::MessageType_BinanceGetAddress => 223, + MessageType::MessageType_BinanceAddress => 224, + MessageType::MessageType_BinanceGetPublicKey => 225, + MessageType::MessageType_BinancePublicKey => 226, + MessageType::MessageType_BinanceSignTx => 227, + MessageType::MessageType_BinanceTxRequest => 228, + MessageType::MessageType_BinanceTransferMsg => 229, + MessageType::MessageType_BinanceOrderMsg => 230, + MessageType::MessageType_BinanceCancelMsg => 231, + MessageType::MessageType_BinanceSignedTx => 232, + MessageType::MessageType_WebAuthnListResidentCredentials => 233, + MessageType::MessageType_WebAuthnCredentials => 234, + MessageType::MessageType_WebAuthnAddResidentCredential => 235, + MessageType::MessageType_WebAuthnRemoveResidentCredential => 236, + MessageType::MessageType_SolanaGetPublicKey => 237, + MessageType::MessageType_SolanaPublicKey => 238, + MessageType::MessageType_SolanaGetAddress => 239, + MessageType::MessageType_SolanaAddress => 240, + MessageType::MessageType_SolanaSignTx => 241, + MessageType::MessageType_SolanaTxSignature => 242, }; Self::enum_descriptor().value_by_index(index) } @@ -1550,7 +1568,7 @@ pub mod exts { static file_descriptor_proto_data: &'static [u8] = b"\ \n\x0emessages.proto\x12\x12hw.trezor.messages\x1a\x20google/protobuf/de\ - scriptor.proto*\x8bS\n\x0bMessageType\x12(\n\x16MessageType_Initialize\ + scriptor.proto*\x98T\n\x0bMessageType\x12(\n\x16MessageType_Initialize\ \x10\0\x1a\x0c\x80\xa6\x1d\x01\xb0\xb5\x18\x01\x90\xb5\x18\x01\x12\x1e\n\ \x10MessageType_Ping\x10\x01\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12\ %\n\x13MessageType_Success\x10\x02\x1a\x0c\x80\xa6\x1d\x01\xa8\xb5\x18\ @@ -1605,115 +1623,119 @@ static file_descriptor_proto_data: &'static [u8] = b"\ ockBootloader\x10`\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12,\n\x1eMes\ sageType_AuthenticateDevice\x10a\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\ \x12+\n\x1dMessageType_AuthenticityProof\x10b\x1a\x08\x80\xa6\x1d\x01\ - \x90\xb5\x18\x01\x12#\n\x19MessageType_SetU2FCounter\x10?\x1a\x04\x90\ - \xb5\x18\x01\x12'\n\x1dMessageType_GetNextU2FCounter\x10P\x1a\x04\x90\ - \xb5\x18\x01\x12$\n\x1aMessageType_NextU2FCounter\x10Q\x1a\x04\x98\xb5\ - \x18\x01\x125\n-MessageType_Deprecated_PassphraseStateRequest\x10M\x1a\ - \x02\x08\x01\x121\n)MessageType_Deprecated_PassphraseStateAck\x10N\x1a\ - \x02\x08\x01\x12+\n\x19MessageType_FirmwareErase\x10\x06\x1a\x0c\xb8\xb5\ - \x18\x01\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12,\n\x1aMessageType_FirmwareU\ - pload\x10\x07\x1a\x0c\xb8\xb5\x18\x01\x80\xa6\x1d\x01\x90\xb5\x18\x01\ - \x12-\n\x1bMessageType_FirmwareRequest\x10\x08\x1a\x0c\xb8\xb5\x18\x01\ - \x80\xa6\x1d\x01\x98\xb5\x18\x01\x12&\n\x14MessageType_SelfTest\x10\x20\ - \x1a\x0c\xb8\xb5\x18\x01\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12&\n\x18Messa\ - geType_GetPublicKey\x10\x0b\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12#\ - \n\x15MessageType_PublicKey\x10\x0c\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\ - \x01\x12\x20\n\x12MessageType_SignTx\x10\x0f\x1a\x08\x80\xa6\x1d\x01\x90\ - \xb5\x18\x01\x12#\n\x15MessageType_TxRequest\x10\x15\x1a\x08\x80\xa6\x1d\ - \x01\x98\xb5\x18\x01\x12\x1f\n\x11MessageType_TxAck\x10\x16\x1a\x08\x80\ - \xa6\x1d\x01\x90\xb5\x18\x01\x12$\n\x16MessageType_GetAddress\x10\x1d\ - \x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12!\n\x13MessageType_Address\ - \x10\x1e\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12)\n\x1fMessageType_T\ - xAckPaymentRequest\x10%\x1a\x04\x90\xb5\x18\x01\x12%\n\x17MessageType_Si\ - gnMessage\x10&\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12'\n\x19Message\ - Type_VerifyMessage\x10'\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12*\n\ - \x1cMessageType_MessageSignature\x10(\x1a\x08\x80\xa6\x1d\x01\x98\xb5\ - \x18\x01\x12(\n\x1aMessageType_GetOwnershipId\x10+\x1a\x08\x80\xa6\x1d\ - \x01\x90\xb5\x18\x01\x12%\n\x17MessageType_OwnershipId\x10,\x1a\x08\x80\ - \xa6\x1d\x01\x98\xb5\x18\x01\x12+\n\x1dMessageType_GetOwnershipProof\x10\ - 1\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12(\n\x1aMessageType_Ownershi\ - pProof\x102\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12+\n\x1dMessageTyp\ - e_AuthorizeCoinJoin\x103\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12(\n\ - \x1aMessageType_CipherKeyValue\x10\x17\x1a\x08\x80\xa6\x1d\x01\x90\xb5\ - \x18\x01\x12*\n\x1cMessageType_CipheredKeyValue\x100\x1a\x08\x80\xa6\x1d\ - \x01\x98\xb5\x18\x01\x12&\n\x18MessageType_SignIdentity\x105\x1a\x08\x80\ - \xa6\x1d\x01\x90\xb5\x18\x01\x12(\n\x1aMessageType_SignedIdentity\x106\ - \x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12+\n\x1dMessageType_GetECDHSe\ - ssionKey\x10=\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12(\n\x1aMessageT\ - ype_ECDHSessionKey\x10>\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12$\n\ - \x16MessageType_CosiCommit\x10G\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\ - \x12(\n\x1aMessageType_CosiCommitment\x10H\x1a\x08\x80\xa6\x1d\x01\x98\ - \xb5\x18\x01\x12\"\n\x14MessageType_CosiSign\x10I\x1a\x08\x80\xa6\x1d\ - \x01\x90\xb5\x18\x01\x12'\n\x19MessageType_CosiSignature\x10J\x1a\x08\ - \x80\xa6\x1d\x01\x98\xb5\x18\x01\x123\n\x1dMessageType_DebugLinkDecision\ - \x10d\x1a\x10\xc0\xb5\x18\x01\xb0\xb5\x18\x01\x80\xa6\x1d\x01\xa0\xb5\ - \x18\x01\x12/\n\x1dMessageType_DebugLinkGetState\x10e\x1a\x0c\x80\xa6\ - \x1d\x01\xb0\xb5\x18\x01\xa0\xb5\x18\x01\x12(\n\x1aMessageType_DebugLink\ - State\x10f\x1a\x08\x80\xa6\x1d\x01\xa8\xb5\x18\x01\x12'\n\x19MessageType\ - _DebugLinkStop\x10g\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12&\n\x18Me\ - ssageType_DebugLinkLog\x10h\x1a\x08\x80\xa6\x1d\x01\xa8\xb5\x18\x01\x12-\ - \n\x1fMessageType_DebugLinkMemoryRead\x10n\x1a\x08\x80\xa6\x1d\x01\xa0\ - \xb5\x18\x01\x12)\n\x1bMessageType_DebugLinkMemory\x10o\x1a\x08\x80\xa6\ - \x1d\x01\xa8\xb5\x18\x01\x12.\n\x20MessageType_DebugLinkMemoryWrite\x10p\ - \x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12-\n\x1fMessageType_DebugLink\ - FlashErase\x10q\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12*\n\x1bMessag\ - eType_DebugLinkLayout\x10\xa9F\x1a\x08\x80\xa6\x1d\x01\xa8\xb5\x18\x01\ - \x120\n!MessageType_DebugLinkReseedRandom\x10\xaaF\x1a\x08\x80\xa6\x1d\ - \x01\xa0\xb5\x18\x01\x120\n!MessageType_DebugLinkRecordScreen\x10\xabF\ - \x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12/\n\x20MessageType_DebugLink\ - EraseSdCard\x10\xadF\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12/\n\x20M\ - essageType_DebugLinkWatchLayout\x10\xaeF\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\ - \x18\x01\x124\n%MessageType_DebugLinkResetDebugEvents\x10\xafF\x1a\x08\ - \x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12+\n\x20MessageType_EthereumGetPublic\ - Key\x10\xc2\x03\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_EthereumPu\ - blicKey\x10\xc3\x03\x1a\x04\x98\xb5\x18\x01\x12(\n\x1eMessageType_Ethere\ - umGetAddress\x108\x1a\x04\x90\xb5\x18\x01\x12%\n\x1bMessageType_Ethereum\ - Address\x109\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EthereumSignT\ - x\x10:\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_EthereumSignTxEIP1559\ - \x10\xc4\x03\x1a\x04\x90\xb5\x18\x01\x12'\n\x1dMessageType_EthereumTxReq\ - uest\x10;\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_EthereumTxAck\ - \x10<\x1a\x04\x90\xb5\x18\x01\x12)\n\x1fMessageType_EthereumSignMessage\ - \x10@\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumVerifyMessage\ - \x10A\x1a\x04\x90\xb5\x18\x01\x12.\n$MessageType_EthereumMessageSignatur\ - e\x10B\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_EthereumSignTypedData\ - \x10\xd0\x03\x1a\x04\x90\xb5\x18\x01\x125\n*MessageType_EthereumTypedDat\ - aStructRequest\x10\xd1\x03\x1a\x04\x98\xb5\x18\x01\x121\n&MessageType_Et\ - hereumTypedDataStructAck\x10\xd2\x03\x1a\x04\x90\xb5\x18\x01\x124\n)Mess\ - ageType_EthereumTypedDataValueRequest\x10\xd3\x03\x1a\x04\x98\xb5\x18\ - \x01\x120\n%MessageType_EthereumTypedDataValueAck\x10\xd4\x03\x1a\x04\ - \x90\xb5\x18\x01\x121\n&MessageType_EthereumTypedDataSignature\x10\xd5\ - \x03\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_EthereumSignTypedHash\ - \x10\xd6\x03\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_NEMGetAddress\ - \x10C\x1a\x04\x90\xb5\x18\x01\x12\x20\n\x16MessageType_NEMAddress\x10D\ - \x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_NEMSignTx\x10E\x1a\x04\ - \x90\xb5\x18\x01\x12!\n\x17MessageType_NEMSignedTx\x10F\x1a\x04\x98\xb5\ - \x18\x01\x12'\n\x1dMessageType_NEMDecryptMessage\x10K\x1a\x04\x90\xb5\ - \x18\x01\x12)\n\x1fMessageType_NEMDecryptedMessage\x10L\x1a\x04\x98\xb5\ - \x18\x01\x12&\n\x1bMessageType_TezosGetAddress\x10\x96\x01\x1a\x04\x90\ - \xb5\x18\x01\x12#\n\x18MessageType_TezosAddress\x10\x97\x01\x1a\x04\x98\ - \xb5\x18\x01\x12\"\n\x17MessageType_TezosSignTx\x10\x98\x01\x1a\x04\x90\ - \xb5\x18\x01\x12$\n\x19MessageType_TezosSignedTx\x10\x99\x01\x1a\x04\x98\ - \xb5\x18\x01\x12(\n\x1dMessageType_TezosGetPublicKey\x10\x9a\x01\x1a\x04\ - \x90\xb5\x18\x01\x12%\n\x1aMessageType_TezosPublicKey\x10\x9b\x01\x1a\ - \x04\x98\xb5\x18\x01\x12$\n\x19MessageType_StellarSignTx\x10\xca\x01\x1a\ - \x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_StellarTxOpRequest\x10\xcb\ - \x01\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_StellarGetAddress\x10\ - \xcf\x01\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_StellarAddress\ - \x10\xd0\x01\x1a\x04\x98\xb5\x18\x01\x12-\n\"MessageType_StellarCreateAc\ - countOp\x10\xd2\x01\x1a\x04\x90\xb5\x18\x01\x12'\n\x1cMessageType_Stella\ - rPaymentOp\x10\xd3\x01\x1a\x04\x90\xb5\x18\x01\x128\n-MessageType_Stella\ - rPathPaymentStrictReceiveOp\x10\xd4\x01\x1a\x04\x90\xb5\x18\x01\x12/\n$M\ - essageType_StellarManageSellOfferOp\x10\xd5\x01\x1a\x04\x90\xb5\x18\x01\ - \x126\n+MessageType_StellarCreatePassiveSellOfferOp\x10\xd6\x01\x1a\x04\ - \x90\xb5\x18\x01\x12*\n\x1fMessageType_StellarSetOptionsOp\x10\xd7\x01\ - \x1a\x04\x90\xb5\x18\x01\x12+\n\x20MessageType_StellarChangeTrustOp\x10\ - \xd8\x01\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_StellarAllowTrust\ - Op\x10\xd9\x01\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_StellarAccount\ - MergeOp\x10\xda\x01\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_Stella\ - rManageDataOp\x10\xdc\x01\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_Ste\ - llarBumpSequenceOp\x10\xdd\x01\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageTyp\ - e_StellarManageBuyOfferOp\x10\xde\x01\x1a\x04\x90\xb5\x18\x01\x125\n*Mes\ - sageType_StellarPathPaymentStrictSendOp\x10\xdf\x01\x1a\x04\x90\xb5\x18\ + \x90\xb5\x18\x01\x12)\n\x1aMessageType_ChangeLanguage\x10\xde\x07\x1a\ + \x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x121\n\"MessageType_TranslationData\ + Request\x10\xdf\x07\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12-\n\x1eMe\ + ssageType_TranslationDataAck\x10\xe0\x07\x1a\x08\x80\xa6\x1d\x01\x90\xb5\ + \x18\x01\x12#\n\x19MessageType_SetU2FCounter\x10?\x1a\x04\x90\xb5\x18\ + \x01\x12'\n\x1dMessageType_GetNextU2FCounter\x10P\x1a\x04\x90\xb5\x18\ + \x01\x12$\n\x1aMessageType_NextU2FCounter\x10Q\x1a\x04\x98\xb5\x18\x01\ + \x125\n-MessageType_Deprecated_PassphraseStateRequest\x10M\x1a\x02\x08\ + \x01\x121\n)MessageType_Deprecated_PassphraseStateAck\x10N\x1a\x02\x08\ + \x01\x12+\n\x19MessageType_FirmwareErase\x10\x06\x1a\x0c\xb8\xb5\x18\x01\ + \x80\xa6\x1d\x01\x90\xb5\x18\x01\x12,\n\x1aMessageType_FirmwareUpload\ + \x10\x07\x1a\x0c\xb8\xb5\x18\x01\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12-\n\ + \x1bMessageType_FirmwareRequest\x10\x08\x1a\x0c\xb8\xb5\x18\x01\x80\xa6\ + \x1d\x01\x98\xb5\x18\x01\x12&\n\x14MessageType_SelfTest\x10\x20\x1a\x0c\ + \xb8\xb5\x18\x01\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12&\n\x18MessageType_G\ + etPublicKey\x10\x0b\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12#\n\x15Me\ + ssageType_PublicKey\x10\x0c\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12\ + \x20\n\x12MessageType_SignTx\x10\x0f\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\ + \x01\x12#\n\x15MessageType_TxRequest\x10\x15\x1a\x08\x80\xa6\x1d\x01\x98\ + \xb5\x18\x01\x12\x1f\n\x11MessageType_TxAck\x10\x16\x1a\x08\x80\xa6\x1d\ + \x01\x90\xb5\x18\x01\x12$\n\x16MessageType_GetAddress\x10\x1d\x1a\x08\ + \x80\xa6\x1d\x01\x90\xb5\x18\x01\x12!\n\x13MessageType_Address\x10\x1e\ + \x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12)\n\x1fMessageType_TxAckPaym\ + entRequest\x10%\x1a\x04\x90\xb5\x18\x01\x12%\n\x17MessageType_SignMessag\ + e\x10&\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12'\n\x19MessageType_Ver\ + ifyMessage\x10'\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12*\n\x1cMessag\ + eType_MessageSignature\x10(\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12(\ + \n\x1aMessageType_GetOwnershipId\x10+\x1a\x08\x80\xa6\x1d\x01\x90\xb5\ + \x18\x01\x12%\n\x17MessageType_OwnershipId\x10,\x1a\x08\x80\xa6\x1d\x01\ + \x98\xb5\x18\x01\x12+\n\x1dMessageType_GetOwnershipProof\x101\x1a\x08\ + \x80\xa6\x1d\x01\x90\xb5\x18\x01\x12(\n\x1aMessageType_OwnershipProof\ + \x102\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12+\n\x1dMessageType_Auth\ + orizeCoinJoin\x103\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12(\n\x1aMes\ + sageType_CipherKeyValue\x10\x17\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\ + \x12*\n\x1cMessageType_CipheredKeyValue\x100\x1a\x08\x80\xa6\x1d\x01\x98\ + \xb5\x18\x01\x12&\n\x18MessageType_SignIdentity\x105\x1a\x08\x80\xa6\x1d\ + \x01\x90\xb5\x18\x01\x12(\n\x1aMessageType_SignedIdentity\x106\x1a\x08\ + \x80\xa6\x1d\x01\x98\xb5\x18\x01\x12+\n\x1dMessageType_GetECDHSessionKey\ + \x10=\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12(\n\x1aMessageType_ECDH\ + SessionKey\x10>\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\x12$\n\x16Messag\ + eType_CosiCommit\x10G\x1a\x08\x80\xa6\x1d\x01\x90\xb5\x18\x01\x12(\n\x1a\ + MessageType_CosiCommitment\x10H\x1a\x08\x80\xa6\x1d\x01\x98\xb5\x18\x01\ + \x12\"\n\x14MessageType_CosiSign\x10I\x1a\x08\x80\xa6\x1d\x01\x90\xb5\ + \x18\x01\x12'\n\x19MessageType_CosiSignature\x10J\x1a\x08\x80\xa6\x1d\ + \x01\x98\xb5\x18\x01\x123\n\x1dMessageType_DebugLinkDecision\x10d\x1a\ + \x10\xc0\xb5\x18\x01\xb0\xb5\x18\x01\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12\ + /\n\x1dMessageType_DebugLinkGetState\x10e\x1a\x0c\x80\xa6\x1d\x01\xb0\ + \xb5\x18\x01\xa0\xb5\x18\x01\x12(\n\x1aMessageType_DebugLinkState\x10f\ + \x1a\x08\x80\xa6\x1d\x01\xa8\xb5\x18\x01\x12'\n\x19MessageType_DebugLink\ + Stop\x10g\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12&\n\x18MessageType_\ + DebugLinkLog\x10h\x1a\x08\x80\xa6\x1d\x01\xa8\xb5\x18\x01\x12-\n\x1fMess\ + ageType_DebugLinkMemoryRead\x10n\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\ + \x12)\n\x1bMessageType_DebugLinkMemory\x10o\x1a\x08\x80\xa6\x1d\x01\xa8\ + \xb5\x18\x01\x12.\n\x20MessageType_DebugLinkMemoryWrite\x10p\x1a\x08\x80\ + \xa6\x1d\x01\xa0\xb5\x18\x01\x12-\n\x1fMessageType_DebugLinkFlashErase\ + \x10q\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12*\n\x1bMessageType_Debu\ + gLinkLayout\x10\xa9F\x1a\x08\x80\xa6\x1d\x01\xa8\xb5\x18\x01\x120\n!Mess\ + ageType_DebugLinkReseedRandom\x10\xaaF\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\ + \x18\x01\x120\n!MessageType_DebugLinkRecordScreen\x10\xabF\x1a\x08\x80\ + \xa6\x1d\x01\xa0\xb5\x18\x01\x12/\n\x20MessageType_DebugLinkEraseSdCard\ + \x10\xadF\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\x12/\n\x20MessageType_\ + DebugLinkWatchLayout\x10\xaeF\x1a\x08\x80\xa6\x1d\x01\xa0\xb5\x18\x01\ + \x124\n%MessageType_DebugLinkResetDebugEvents\x10\xafF\x1a\x08\x80\xa6\ + \x1d\x01\xa0\xb5\x18\x01\x12+\n\x20MessageType_EthereumGetPublicKey\x10\ + \xc2\x03\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_EthereumPublicKey\ + \x10\xc3\x03\x1a\x04\x98\xb5\x18\x01\x12(\n\x1eMessageType_EthereumGetAd\ + dress\x108\x1a\x04\x90\xb5\x18\x01\x12%\n\x1bMessageType_EthereumAddress\ + \x109\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EthereumSignTx\x10:\ + \x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_EthereumSignTxEIP1559\x10\ + \xc4\x03\x1a\x04\x90\xb5\x18\x01\x12'\n\x1dMessageType_EthereumTxRequest\ + \x10;\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_EthereumTxAck\x10<\ + \x1a\x04\x90\xb5\x18\x01\x12)\n\x1fMessageType_EthereumSignMessage\x10@\ + \x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumVerifyMessage\x10A\ + \x1a\x04\x90\xb5\x18\x01\x12.\n$MessageType_EthereumMessageSignature\x10\ + B\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_EthereumSignTypedData\x10\ + \xd0\x03\x1a\x04\x90\xb5\x18\x01\x125\n*MessageType_EthereumTypedDataStr\ + uctRequest\x10\xd1\x03\x1a\x04\x98\xb5\x18\x01\x121\n&MessageType_Ethere\ + umTypedDataStructAck\x10\xd2\x03\x1a\x04\x90\xb5\x18\x01\x124\n)MessageT\ + ype_EthereumTypedDataValueRequest\x10\xd3\x03\x1a\x04\x98\xb5\x18\x01\ + \x120\n%MessageType_EthereumTypedDataValueAck\x10\xd4\x03\x1a\x04\x90\ + \xb5\x18\x01\x121\n&MessageType_EthereumTypedDataSignature\x10\xd5\x03\ + \x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_EthereumSignTypedHash\x10\ + \xd6\x03\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_NEMGetAddress\x10\ + C\x1a\x04\x90\xb5\x18\x01\x12\x20\n\x16MessageType_NEMAddress\x10D\x1a\ + \x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_NEMSignTx\x10E\x1a\x04\x90\ + \xb5\x18\x01\x12!\n\x17MessageType_NEMSignedTx\x10F\x1a\x04\x98\xb5\x18\ + \x01\x12'\n\x1dMessageType_NEMDecryptMessage\x10K\x1a\x04\x90\xb5\x18\ + \x01\x12)\n\x1fMessageType_NEMDecryptedMessage\x10L\x1a\x04\x98\xb5\x18\ + \x01\x12&\n\x1bMessageType_TezosGetAddress\x10\x96\x01\x1a\x04\x90\xb5\ + \x18\x01\x12#\n\x18MessageType_TezosAddress\x10\x97\x01\x1a\x04\x98\xb5\ + \x18\x01\x12\"\n\x17MessageType_TezosSignTx\x10\x98\x01\x1a\x04\x90\xb5\ + \x18\x01\x12$\n\x19MessageType_TezosSignedTx\x10\x99\x01\x1a\x04\x98\xb5\ + \x18\x01\x12(\n\x1dMessageType_TezosGetPublicKey\x10\x9a\x01\x1a\x04\x90\ + \xb5\x18\x01\x12%\n\x1aMessageType_TezosPublicKey\x10\x9b\x01\x1a\x04\ + \x98\xb5\x18\x01\x12$\n\x19MessageType_StellarSignTx\x10\xca\x01\x1a\x04\ + \x90\xb5\x18\x01\x12)\n\x1eMessageType_StellarTxOpRequest\x10\xcb\x01\ + \x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_StellarGetAddress\x10\xcf\ + \x01\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_StellarAddress\x10\ + \xd0\x01\x1a\x04\x98\xb5\x18\x01\x12-\n\"MessageType_StellarCreateAccoun\ + tOp\x10\xd2\x01\x1a\x04\x90\xb5\x18\x01\x12'\n\x1cMessageType_StellarPay\ + mentOp\x10\xd3\x01\x1a\x04\x90\xb5\x18\x01\x128\n-MessageType_StellarPat\ + hPaymentStrictReceiveOp\x10\xd4\x01\x1a\x04\x90\xb5\x18\x01\x12/\n$Messa\ + geType_StellarManageSellOfferOp\x10\xd5\x01\x1a\x04\x90\xb5\x18\x01\x126\ + \n+MessageType_StellarCreatePassiveSellOfferOp\x10\xd6\x01\x1a\x04\x90\ + \xb5\x18\x01\x12*\n\x1fMessageType_StellarSetOptionsOp\x10\xd7\x01\x1a\ + \x04\x90\xb5\x18\x01\x12+\n\x20MessageType_StellarChangeTrustOp\x10\xd8\ + \x01\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_StellarAllowTrustOp\ + \x10\xd9\x01\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_StellarAccountMe\ + rgeOp\x10\xda\x01\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_StellarM\ + anageDataOp\x10\xdc\x01\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_Stell\ + arBumpSequenceOp\x10\xdd\x01\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_\ + StellarManageBuyOfferOp\x10\xde\x01\x1a\x04\x90\xb5\x18\x01\x125\n*Messa\ + geType_StellarPathPaymentStrictSendOp\x10\xdf\x01\x1a\x04\x90\xb5\x18\ \x01\x125\n*MessageType_StellarClaimClaimableBalanceOp\x10\xe1\x01\x1a\ \x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_StellarSignedTx\x10\xe6\x01\ \x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CardanoGetPublicKey\x10\ diff --git a/rust/trezor-client/src/protos/generated/messages_management.rs b/rust/trezor-client/src/protos/generated/messages_management.rs index 8ec3917c1..30347d7ca 100644 --- a/rust/trezor-client/src/protos/generated/messages_management.rs +++ b/rust/trezor-client/src/protos/generated/messages_management.rs @@ -3452,6 +3452,495 @@ impl ::protobuf::reflect::ProtobufValue for ApplySettings { type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; } +// @@protoc_insertion_point(message:hw.trezor.messages.management.ChangeLanguage) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct ChangeLanguage { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.management.ChangeLanguage.data_length) + pub data_length: ::std::option::Option, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.management.ChangeLanguage.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a ChangeLanguage { + fn default() -> &'a ChangeLanguage { + ::default_instance() + } +} + +impl ChangeLanguage { + pub fn new() -> ChangeLanguage { + ::std::default::Default::default() + } + + // required uint32 data_length = 1; + + pub fn data_length(&self) -> u32 { + self.data_length.unwrap_or(0) + } + + pub fn clear_data_length(&mut self) { + self.data_length = ::std::option::Option::None; + } + + pub fn has_data_length(&self) -> bool { + self.data_length.is_some() + } + + // Param is passed by value, moved + pub fn set_data_length(&mut self, v: u32) { + self.data_length = ::std::option::Option::Some(v); + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(1); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "data_length", + |m: &ChangeLanguage| { &m.data_length }, + |m: &mut ChangeLanguage| { &mut m.data_length }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "ChangeLanguage", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for ChangeLanguage { + const NAME: &'static str = "ChangeLanguage"; + + fn is_initialized(&self) -> bool { + if self.data_length.is_none() { + return false; + } + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 8 => { + self.data_length = ::std::option::Option::Some(is.read_uint32()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.data_length { + my_size += ::protobuf::rt::uint32_size(1, v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.data_length { + os.write_uint32(1, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> ChangeLanguage { + ChangeLanguage::new() + } + + fn clear(&mut self) { + self.data_length = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static ChangeLanguage { + static instance: ChangeLanguage = ChangeLanguage { + data_length: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for ChangeLanguage { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("ChangeLanguage").unwrap()).clone() + } +} + +impl ::std::fmt::Display for ChangeLanguage { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for ChangeLanguage { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:hw.trezor.messages.management.TranslationDataRequest) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct TranslationDataRequest { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.management.TranslationDataRequest.data_length) + pub data_length: ::std::option::Option, + // @@protoc_insertion_point(field:hw.trezor.messages.management.TranslationDataRequest.data_offset) + pub data_offset: ::std::option::Option, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.management.TranslationDataRequest.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a TranslationDataRequest { + fn default() -> &'a TranslationDataRequest { + ::default_instance() + } +} + +impl TranslationDataRequest { + pub fn new() -> TranslationDataRequest { + ::std::default::Default::default() + } + + // required uint32 data_length = 1; + + pub fn data_length(&self) -> u32 { + self.data_length.unwrap_or(0) + } + + pub fn clear_data_length(&mut self) { + self.data_length = ::std::option::Option::None; + } + + pub fn has_data_length(&self) -> bool { + self.data_length.is_some() + } + + // Param is passed by value, moved + pub fn set_data_length(&mut self, v: u32) { + self.data_length = ::std::option::Option::Some(v); + } + + // required uint32 data_offset = 2; + + pub fn data_offset(&self) -> u32 { + self.data_offset.unwrap_or(0) + } + + pub fn clear_data_offset(&mut self) { + self.data_offset = ::std::option::Option::None; + } + + pub fn has_data_offset(&self) -> bool { + self.data_offset.is_some() + } + + // Param is passed by value, moved + pub fn set_data_offset(&mut self, v: u32) { + self.data_offset = ::std::option::Option::Some(v); + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(2); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "data_length", + |m: &TranslationDataRequest| { &m.data_length }, + |m: &mut TranslationDataRequest| { &mut m.data_length }, + )); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "data_offset", + |m: &TranslationDataRequest| { &m.data_offset }, + |m: &mut TranslationDataRequest| { &mut m.data_offset }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "TranslationDataRequest", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for TranslationDataRequest { + const NAME: &'static str = "TranslationDataRequest"; + + fn is_initialized(&self) -> bool { + if self.data_length.is_none() { + return false; + } + if self.data_offset.is_none() { + return false; + } + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 8 => { + self.data_length = ::std::option::Option::Some(is.read_uint32()?); + }, + 16 => { + self.data_offset = ::std::option::Option::Some(is.read_uint32()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.data_length { + my_size += ::protobuf::rt::uint32_size(1, v); + } + if let Some(v) = self.data_offset { + my_size += ::protobuf::rt::uint32_size(2, v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.data_length { + os.write_uint32(1, v)?; + } + if let Some(v) = self.data_offset { + os.write_uint32(2, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> TranslationDataRequest { + TranslationDataRequest::new() + } + + fn clear(&mut self) { + self.data_length = ::std::option::Option::None; + self.data_offset = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static TranslationDataRequest { + static instance: TranslationDataRequest = TranslationDataRequest { + data_length: ::std::option::Option::None, + data_offset: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for TranslationDataRequest { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("TranslationDataRequest").unwrap()).clone() + } +} + +impl ::std::fmt::Display for TranslationDataRequest { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for TranslationDataRequest { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + +// @@protoc_insertion_point(message:hw.trezor.messages.management.TranslationDataAck) +#[derive(PartialEq,Clone,Default,Debug)] +pub struct TranslationDataAck { + // message fields + // @@protoc_insertion_point(field:hw.trezor.messages.management.TranslationDataAck.data_chunk) + pub data_chunk: ::std::option::Option<::std::vec::Vec>, + // special fields + // @@protoc_insertion_point(special_field:hw.trezor.messages.management.TranslationDataAck.special_fields) + pub special_fields: ::protobuf::SpecialFields, +} + +impl<'a> ::std::default::Default for &'a TranslationDataAck { + fn default() -> &'a TranslationDataAck { + ::default_instance() + } +} + +impl TranslationDataAck { + pub fn new() -> TranslationDataAck { + ::std::default::Default::default() + } + + // required bytes data_chunk = 1; + + pub fn data_chunk(&self) -> &[u8] { + match self.data_chunk.as_ref() { + Some(v) => v, + None => &[], + } + } + + pub fn clear_data_chunk(&mut self) { + self.data_chunk = ::std::option::Option::None; + } + + pub fn has_data_chunk(&self) -> bool { + self.data_chunk.is_some() + } + + // Param is passed by value, moved + pub fn set_data_chunk(&mut self, v: ::std::vec::Vec) { + self.data_chunk = ::std::option::Option::Some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_data_chunk(&mut self) -> &mut ::std::vec::Vec { + if self.data_chunk.is_none() { + self.data_chunk = ::std::option::Option::Some(::std::vec::Vec::new()); + } + self.data_chunk.as_mut().unwrap() + } + + // Take field + pub fn take_data_chunk(&mut self) -> ::std::vec::Vec { + self.data_chunk.take().unwrap_or_else(|| ::std::vec::Vec::new()) + } + + fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { + let mut fields = ::std::vec::Vec::with_capacity(1); + let mut oneofs = ::std::vec::Vec::with_capacity(0); + fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( + "data_chunk", + |m: &TranslationDataAck| { &m.data_chunk }, + |m: &mut TranslationDataAck| { &mut m.data_chunk }, + )); + ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::( + "TranslationDataAck", + fields, + oneofs, + ) + } +} + +impl ::protobuf::Message for TranslationDataAck { + const NAME: &'static str = "TranslationDataAck"; + + fn is_initialized(&self) -> bool { + if self.data_chunk.is_none() { + return false; + } + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { + while let Some(tag) = is.read_raw_tag_or_eof()? { + match tag { + 10 => { + self.data_chunk = ::std::option::Option::Some(is.read_bytes()?); + }, + tag => { + ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u64 { + let mut my_size = 0; + if let Some(v) = self.data_chunk.as_ref() { + my_size += ::protobuf::rt::bytes_size(1, &v); + } + my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); + self.special_fields.cached_size().set(my_size as u32); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { + if let Some(v) = self.data_chunk.as_ref() { + os.write_bytes(1, v)?; + } + os.write_unknown_fields(self.special_fields.unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn special_fields(&self) -> &::protobuf::SpecialFields { + &self.special_fields + } + + fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { + &mut self.special_fields + } + + fn new() -> TranslationDataAck { + TranslationDataAck::new() + } + + fn clear(&mut self) { + self.data_chunk = ::std::option::Option::None; + self.special_fields.clear(); + } + + fn default_instance() -> &'static TranslationDataAck { + static instance: TranslationDataAck = TranslationDataAck { + data_chunk: ::std::option::Option::None, + special_fields: ::protobuf::SpecialFields::new(), + }; + &instance + } +} + +impl ::protobuf::MessageFull for TranslationDataAck { + fn descriptor() -> ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); + descriptor.get(|| file_descriptor().message_by_package_relative_name("TranslationDataAck").unwrap()).clone() + } +} + +impl ::std::fmt::Display for TranslationDataAck { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for TranslationDataAck { + type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage; +} + // @@protoc_insertion_point(message:hw.trezor.messages.management.ApplyFlags) #[derive(PartialEq,Clone,Default,Debug)] pub struct ApplyFlags { @@ -9774,76 +10263,80 @@ static file_descriptor_proto_data: &'static [u8] = b"\ r.messages.management.SafetyCheckLevelR\x0csafetyChecks\x123\n\x15experi\ mental_features\x18\n\x20\x01(\x08R\x14experimentalFeatures\x129\n\x19hi\ de_passphrase_from_host\x18\x0b\x20\x01(\x08R\x16hidePassphraseFromHost\ - \"\"\n\nApplyFlags\x12\x14\n\x05flags\x18\x01\x20\x02(\rR\x05flags\"#\n\ - \tChangePin\x12\x16\n\x06remove\x18\x01\x20\x01(\x08R\x06remove\"(\n\x0e\ - ChangeWipeCode\x12\x16\n\x06remove\x18\x01\x20\x01(\x08R\x06remove\"\xaa\ - \x01\n\tSdProtect\x12]\n\toperation\x18\x01\x20\x02(\x0e2?.hw.trezor.mes\ - sages.management.SdProtect.SdProtectOperationTypeR\toperation\">\n\x16Sd\ - ProtectOperationType\x12\x0b\n\x07DISABLE\x10\0\x12\n\n\x06ENABLE\x10\ - \x01\x12\x0b\n\x07REFRESH\x10\x02\"O\n\x04Ping\x12\x1a\n\x07message\x18\ - \x01\x20\x01(\t:\0R\x07message\x12+\n\x11button_protection\x18\x02\x20\ - \x01(\x08R\x10buttonProtection\"\x08\n\x06Cancel\"\x20\n\nGetEntropy\x12\ - \x12\n\x04size\x18\x01\x20\x02(\rR\x04size\"#\n\x07Entropy\x12\x18\n\x07\ - entropy\x18\x01\x20\x02(\x0cR\x07entropy\"/\n\x0fGetFirmwareHash\x12\x1c\ - \n\tchallenge\x18\x01\x20\x01(\x0cR\tchallenge\"\"\n\x0cFirmwareHash\x12\ - \x12\n\x04hash\x18\x01\x20\x02(\x0cR\x04hash\"2\n\x12AuthenticateDevice\ - \x12\x1c\n\tchallenge\x18\x01\x20\x02(\x0cR\tchallenge\"U\n\x11Authentic\ - ityProof\x12\"\n\x0ccertificates\x18\x01\x20\x03(\x0cR\x0ccertificates\ - \x12\x1c\n\tsignature\x18\x02\x20\x02(\x0cR\tsignature\"\x0c\n\nWipeDevi\ - ce\"\xb0\x02\n\nLoadDevice\x12\x1c\n\tmnemonics\x18\x01\x20\x03(\tR\tmne\ - monics\x12\x10\n\x03pin\x18\x03\x20\x01(\tR\x03pin\x123\n\x15passphrase_\ - protection\x18\x04\x20\x01(\x08R\x14passphraseProtection\x12!\n\x08langu\ - age\x18\x05\x20\x01(\t:\x05en-USR\x08language\x12\x14\n\x05label\x18\x06\ - \x20\x01(\tR\x05label\x12#\n\rskip_checksum\x18\x07\x20\x01(\x08R\x0cski\ - pChecksum\x12\x1f\n\x0bu2f_counter\x18\x08\x20\x01(\rR\nu2fCounter\x12!\ - \n\x0cneeds_backup\x18\t\x20\x01(\x08R\x0bneedsBackup\x12\x1b\n\tno_back\ - up\x18\n\x20\x01(\x08R\x08noBackup\"\x9c\x03\n\x0bResetDevice\x12%\n\x0e\ - display_random\x18\x01\x20\x01(\x08R\rdisplayRandom\x12\x1f\n\x08strengt\ - h\x18\x02\x20\x01(\r:\x03256R\x08strength\x123\n\x15passphrase_protectio\ - n\x18\x03\x20\x01(\x08R\x14passphraseProtection\x12%\n\x0epin_protection\ - \x18\x04\x20\x01(\x08R\rpinProtection\x12!\n\x08language\x18\x05\x20\x01\ - (\t:\x05en-USR\x08language\x12\x14\n\x05label\x18\x06\x20\x01(\tR\x05lab\ - el\x12\x1f\n\x0bu2f_counter\x18\x07\x20\x01(\rR\nu2fCounter\x12\x1f\n\ - \x0bskip_backup\x18\x08\x20\x01(\x08R\nskipBackup\x12\x1b\n\tno_backup\ - \x18\t\x20\x01(\x08R\x08noBackup\x12Q\n\x0bbackup_type\x18\n\x20\x01(\ - \x0e2).hw.trezor.messages.management.BackupType:\x05Bip39R\nbackupType\"\ - \x0e\n\x0cBackupDevice\"\x10\n\x0eEntropyRequest\"&\n\nEntropyAck\x12\ - \x18\n\x07entropy\x18\x01\x20\x02(\x0cR\x07entropy\"\xd4\x03\n\x0eRecove\ - ryDevice\x12\x1d\n\nword_count\x18\x01\x20\x01(\rR\twordCount\x123\n\x15\ - passphrase_protection\x18\x02\x20\x01(\x08R\x14passphraseProtection\x12%\ - \n\x0epin_protection\x18\x03\x20\x01(\x08R\rpinProtection\x12\x1a\n\x08l\ - anguage\x18\x04\x20\x01(\tR\x08language\x12\x14\n\x05label\x18\x05\x20\ - \x01(\tR\x05label\x12)\n\x10enforce_wordlist\x18\x06\x20\x01(\x08R\x0fen\ - forceWordlist\x12T\n\x04type\x18\x08\x20\x01(\x0e2@.hw.trezor.messages.m\ - anagement.RecoveryDevice.RecoveryDeviceTypeR\x04type\x12\x1f\n\x0bu2f_co\ - unter\x18\t\x20\x01(\rR\nu2fCounter\x12\x17\n\x07dry_run\x18\n\x20\x01(\ - \x08R\x06dryRun\"Z\n\x12RecoveryDeviceType\x12%\n!RecoveryDeviceType_Scr\ - ambledWords\x10\0\x12\x1d\n\x19RecoveryDeviceType_Matrix\x10\x01\"\xc5\ - \x01\n\x0bWordRequest\x12N\n\x04type\x18\x01\x20\x02(\x0e2:.hw.trezor.me\ - ssages.management.WordRequest.WordRequestTypeR\x04type\"f\n\x0fWordReque\ - stType\x12\x19\n\x15WordRequestType_Plain\x10\0\x12\x1b\n\x17WordRequest\ - Type_Matrix9\x10\x01\x12\x1b\n\x17WordRequestType_Matrix6\x10\x02\"\x1d\ - \n\x07WordAck\x12\x12\n\x04word\x18\x01\x20\x02(\tR\x04word\"0\n\rSetU2F\ - Counter\x12\x1f\n\x0bu2f_counter\x18\x01\x20\x02(\rR\nu2fCounter\"\x13\n\ - \x11GetNextU2FCounter\"1\n\x0eNextU2FCounter\x12\x1f\n\x0bu2f_counter\ - \x18\x01\x20\x02(\rR\nu2fCounter\"\x11\n\x0fDoPreauthorized\"\x16\n\x14P\ - reauthorizedRequest\"\x15\n\x13CancelAuthorization\"\xe5\x01\n\x12Reboot\ - ToBootloader\x12o\n\x0cboot_command\x18\x01\x20\x01(\x0e2=.hw.trezor.mes\ - sages.management.RebootToBootloader.BootCommand:\rSTOP_AND_WAITR\x0bboot\ - Command\x12'\n\x0ffirmware_header\x18\x02\x20\x01(\x0cR\x0efirmwareHeade\ - r\"5\n\x0bBootCommand\x12\x11\n\rSTOP_AND_WAIT\x10\0\x12\x13\n\x0fINSTAL\ - L_UPGRADE\x10\x01\"\x10\n\x08GetNonce:\x04\x88\xb2\x19\x01\"#\n\x05Nonce\ - \x12\x14\n\x05nonce\x18\x01\x20\x02(\x0cR\x05nonce:\x04\x88\xb2\x19\x01\ - \";\n\nUnlockPath\x12\x1b\n\taddress_n\x18\x01\x20\x03(\rR\x08addressN\ - \x12\x10\n\x03mac\x18\x02\x20\x01(\x0cR\x03mac\"'\n\x13UnlockedPathReque\ - st\x12\x10\n\x03mac\x18\x01\x20\x01(\x0cR\x03mac\"\x14\n\x12ShowDeviceTu\ - torial\"\x12\n\x10UnlockBootloader*>\n\nBackupType\x12\t\n\x05Bip39\x10\ - \0\x12\x10\n\x0cSlip39_Basic\x10\x01\x12\x13\n\x0fSlip39_Advanced\x10\ - \x02*G\n\x10SafetyCheckLevel\x12\n\n\x06Strict\x10\0\x12\x10\n\x0cPrompt\ - Always\x10\x01\x12\x15\n\x11PromptTemporarily\x10\x02*0\n\x10HomescreenF\ - ormat\x12\x08\n\x04Toif\x10\x01\x12\x08\n\x04Jpeg\x10\x02\x12\x08\n\x04T\ - oiG\x10\x03BB\n#com.satoshilabs.trezor.lib.protobufB\x17TrezorMessageMan\ - agement\x80\xa6\x1d\x01\ + \"1\n\x0eChangeLanguage\x12\x1f\n\x0bdata_length\x18\x01\x20\x02(\rR\nda\ + taLength\"Z\n\x16TranslationDataRequest\x12\x1f\n\x0bdata_length\x18\x01\ + \x20\x02(\rR\ndataLength\x12\x1f\n\x0bdata_offset\x18\x02\x20\x02(\rR\nd\ + ataOffset\"3\n\x12TranslationDataAck\x12\x1d\n\ndata_chunk\x18\x01\x20\ + \x02(\x0cR\tdataChunk\"\"\n\nApplyFlags\x12\x14\n\x05flags\x18\x01\x20\ + \x02(\rR\x05flags\"#\n\tChangePin\x12\x16\n\x06remove\x18\x01\x20\x01(\ + \x08R\x06remove\"(\n\x0eChangeWipeCode\x12\x16\n\x06remove\x18\x01\x20\ + \x01(\x08R\x06remove\"\xaa\x01\n\tSdProtect\x12]\n\toperation\x18\x01\ + \x20\x02(\x0e2?.hw.trezor.messages.management.SdProtect.SdProtectOperati\ + onTypeR\toperation\">\n\x16SdProtectOperationType\x12\x0b\n\x07DISABLE\ + \x10\0\x12\n\n\x06ENABLE\x10\x01\x12\x0b\n\x07REFRESH\x10\x02\"O\n\x04Pi\ + ng\x12\x1a\n\x07message\x18\x01\x20\x01(\t:\0R\x07message\x12+\n\x11butt\ + on_protection\x18\x02\x20\x01(\x08R\x10buttonProtection\"\x08\n\x06Cance\ + l\"\x20\n\nGetEntropy\x12\x12\n\x04size\x18\x01\x20\x02(\rR\x04size\"#\n\ + \x07Entropy\x12\x18\n\x07entropy\x18\x01\x20\x02(\x0cR\x07entropy\"/\n\ + \x0fGetFirmwareHash\x12\x1c\n\tchallenge\x18\x01\x20\x01(\x0cR\tchalleng\ + e\"\"\n\x0cFirmwareHash\x12\x12\n\x04hash\x18\x01\x20\x02(\x0cR\x04hash\ + \"2\n\x12AuthenticateDevice\x12\x1c\n\tchallenge\x18\x01\x20\x02(\x0cR\t\ + challenge\"U\n\x11AuthenticityProof\x12\"\n\x0ccertificates\x18\x01\x20\ + \x03(\x0cR\x0ccertificates\x12\x1c\n\tsignature\x18\x02\x20\x02(\x0cR\ts\ + ignature\"\x0c\n\nWipeDevice\"\xb0\x02\n\nLoadDevice\x12\x1c\n\tmnemonic\ + s\x18\x01\x20\x03(\tR\tmnemonics\x12\x10\n\x03pin\x18\x03\x20\x01(\tR\ + \x03pin\x123\n\x15passphrase_protection\x18\x04\x20\x01(\x08R\x14passphr\ + aseProtection\x12!\n\x08language\x18\x05\x20\x01(\t:\x05en-USR\x08langua\ + ge\x12\x14\n\x05label\x18\x06\x20\x01(\tR\x05label\x12#\n\rskip_checksum\ + \x18\x07\x20\x01(\x08R\x0cskipChecksum\x12\x1f\n\x0bu2f_counter\x18\x08\ + \x20\x01(\rR\nu2fCounter\x12!\n\x0cneeds_backup\x18\t\x20\x01(\x08R\x0bn\ + eedsBackup\x12\x1b\n\tno_backup\x18\n\x20\x01(\x08R\x08noBackup\"\x9c\ + \x03\n\x0bResetDevice\x12%\n\x0edisplay_random\x18\x01\x20\x01(\x08R\rdi\ + splayRandom\x12\x1f\n\x08strength\x18\x02\x20\x01(\r:\x03256R\x08strengt\ + h\x123\n\x15passphrase_protection\x18\x03\x20\x01(\x08R\x14passphrasePro\ + tection\x12%\n\x0epin_protection\x18\x04\x20\x01(\x08R\rpinProtection\ + \x12!\n\x08language\x18\x05\x20\x01(\t:\x05en-USR\x08language\x12\x14\n\ + \x05label\x18\x06\x20\x01(\tR\x05label\x12\x1f\n\x0bu2f_counter\x18\x07\ + \x20\x01(\rR\nu2fCounter\x12\x1f\n\x0bskip_backup\x18\x08\x20\x01(\x08R\ + \nskipBackup\x12\x1b\n\tno_backup\x18\t\x20\x01(\x08R\x08noBackup\x12Q\n\ + \x0bbackup_type\x18\n\x20\x01(\x0e2).hw.trezor.messages.management.Backu\ + pType:\x05Bip39R\nbackupType\"\x0e\n\x0cBackupDevice\"\x10\n\x0eEntropyR\ + equest\"&\n\nEntropyAck\x12\x18\n\x07entropy\x18\x01\x20\x02(\x0cR\x07en\ + tropy\"\xd4\x03\n\x0eRecoveryDevice\x12\x1d\n\nword_count\x18\x01\x20\ + \x01(\rR\twordCount\x123\n\x15passphrase_protection\x18\x02\x20\x01(\x08\ + R\x14passphraseProtection\x12%\n\x0epin_protection\x18\x03\x20\x01(\x08R\ + \rpinProtection\x12\x1a\n\x08language\x18\x04\x20\x01(\tR\x08language\ + \x12\x14\n\x05label\x18\x05\x20\x01(\tR\x05label\x12)\n\x10enforce_wordl\ + ist\x18\x06\x20\x01(\x08R\x0fenforceWordlist\x12T\n\x04type\x18\x08\x20\ + \x01(\x0e2@.hw.trezor.messages.management.RecoveryDevice.RecoveryDeviceT\ + ypeR\x04type\x12\x1f\n\x0bu2f_counter\x18\t\x20\x01(\rR\nu2fCounter\x12\ + \x17\n\x07dry_run\x18\n\x20\x01(\x08R\x06dryRun\"Z\n\x12RecoveryDeviceTy\ + pe\x12%\n!RecoveryDeviceType_ScrambledWords\x10\0\x12\x1d\n\x19RecoveryD\ + eviceType_Matrix\x10\x01\"\xc5\x01\n\x0bWordRequest\x12N\n\x04type\x18\ + \x01\x20\x02(\x0e2:.hw.trezor.messages.management.WordRequest.WordReques\ + tTypeR\x04type\"f\n\x0fWordRequestType\x12\x19\n\x15WordRequestType_Plai\ + n\x10\0\x12\x1b\n\x17WordRequestType_Matrix9\x10\x01\x12\x1b\n\x17WordRe\ + questType_Matrix6\x10\x02\"\x1d\n\x07WordAck\x12\x12\n\x04word\x18\x01\ + \x20\x02(\tR\x04word\"0\n\rSetU2FCounter\x12\x1f\n\x0bu2f_counter\x18\ + \x01\x20\x02(\rR\nu2fCounter\"\x13\n\x11GetNextU2FCounter\"1\n\x0eNextU2\ + FCounter\x12\x1f\n\x0bu2f_counter\x18\x01\x20\x02(\rR\nu2fCounter\"\x11\ + \n\x0fDoPreauthorized\"\x16\n\x14PreauthorizedRequest\"\x15\n\x13CancelA\ + uthorization\"\xe5\x01\n\x12RebootToBootloader\x12o\n\x0cboot_command\ + \x18\x01\x20\x01(\x0e2=.hw.trezor.messages.management.RebootToBootloader\ + .BootCommand:\rSTOP_AND_WAITR\x0bbootCommand\x12'\n\x0ffirmware_header\ + \x18\x02\x20\x01(\x0cR\x0efirmwareHeader\"5\n\x0bBootCommand\x12\x11\n\r\ + STOP_AND_WAIT\x10\0\x12\x13\n\x0fINSTALL_UPGRADE\x10\x01\"\x10\n\x08GetN\ + once:\x04\x88\xb2\x19\x01\"#\n\x05Nonce\x12\x14\n\x05nonce\x18\x01\x20\ + \x02(\x0cR\x05nonce:\x04\x88\xb2\x19\x01\";\n\nUnlockPath\x12\x1b\n\tadd\ + ress_n\x18\x01\x20\x03(\rR\x08addressN\x12\x10\n\x03mac\x18\x02\x20\x01(\ + \x0cR\x03mac\"'\n\x13UnlockedPathRequest\x12\x10\n\x03mac\x18\x01\x20\ + \x01(\x0cR\x03mac\"\x14\n\x12ShowDeviceTutorial\"\x12\n\x10UnlockBootloa\ + der*>\n\nBackupType\x12\t\n\x05Bip39\x10\0\x12\x10\n\x0cSlip39_Basic\x10\ + \x01\x12\x13\n\x0fSlip39_Advanced\x10\x02*G\n\x10SafetyCheckLevel\x12\n\ + \n\x06Strict\x10\0\x12\x10\n\x0cPromptAlways\x10\x01\x12\x15\n\x11Prompt\ + Temporarily\x10\x02*0\n\x10HomescreenFormat\x12\x08\n\x04Toif\x10\x01\ + \x12\x08\n\x04Jpeg\x10\x02\x12\x08\n\x04ToiG\x10\x03BB\n#com.satoshilabs\ + .trezor.lib.protobufB\x17TrezorMessageManagement\x80\xa6\x1d\x01\ "; /// `FileDescriptorProto` object which was a source for this generated file @@ -9862,7 +10355,7 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { let mut deps = ::std::vec::Vec::with_capacity(1); deps.push(super::messages::file_descriptor().clone()); - let mut messages = ::std::vec::Vec::with_capacity(41); + let mut messages = ::std::vec::Vec::with_capacity(44); messages.push(Initialize::generated_message_descriptor_data()); messages.push(GetFeatures::generated_message_descriptor_data()); messages.push(Features::generated_message_descriptor_data()); @@ -9870,6 +10363,9 @@ pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { messages.push(SetBusy::generated_message_descriptor_data()); messages.push(EndSession::generated_message_descriptor_data()); messages.push(ApplySettings::generated_message_descriptor_data()); + messages.push(ChangeLanguage::generated_message_descriptor_data()); + messages.push(TranslationDataRequest::generated_message_descriptor_data()); + messages.push(TranslationDataAck::generated_message_descriptor_data()); messages.push(ApplyFlags::generated_message_descriptor_data()); messages.push(ChangePin::generated_message_descriptor_data()); messages.push(ChangeWipeCode::generated_message_descriptor_data()); diff --git a/storage/storage.c b/storage/storage.c index 203237432..16ea982e2 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -154,6 +154,7 @@ const uint8_t WIPE_CODE_EMPTY[] = {0, 0, 0, 0}; #define GUARD_KEY_MODULUS 6311 #define GUARD_KEY_REMAINDER 15 +// TODO: handle translation const char *const VERIFYING_PIN_MSG = "Verifying PIN"; const char *const PROCESSING_MSG = "Processing"; const char *const STARTING_MSG = "Starting up"; diff --git a/tests/click_tests/recovery.py b/tests/click_tests/recovery.py index 306cc7539..afaac538b 100644 --- a/tests/click_tests/recovery.py +++ b/tests/click_tests/recovery.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING from .. import buttons +from .. import translations as TR from .common import go_next if TYPE_CHECKING: @@ -40,11 +41,10 @@ def enter_word( def confirm_recovery(debug: "DebugLink") -> None: layout = debug.wait_layout() + TR.assert_equals(layout.title(), "recovery.title") if debug.model == "T": - assert layout.title().startswith(("RECOVER WALLET", "BACKUP CHECK")) debug.click(buttons.OK, wait=True) elif debug.model == "Safe 3": - assert layout.title() == "RECOVER WALLET" debug.press_right(wait=True) debug.press_right() @@ -54,13 +54,8 @@ def select_number_of_words( ) -> None: if wait: debug.wait_layout() + TR.assert_equals(debug.read_layout().text_content(), "recovery.num_of_words") if debug.model == "T": - assert "number of words" in debug.read_layout().text_content() - assert debug.read_layout().title() in ( - "BACKUP CHECK", - "RECOVER WALLET", - ) - # click the number word_option_offset = 6 word_options = (12, 18, 20, 24, 33) @@ -70,10 +65,8 @@ def select_number_of_words( coords = buttons.grid34(index % 3, index // 3) layout = debug.click(coords, wait=True) elif debug.model == "Safe 3": - assert "number of words" in debug.read_layout().text_content() layout = debug.press_right(wait=True) - - assert layout.title() == "NUMBER OF WORDS" + TR.assert_equals(layout.title(), "word_count.title") # navigate to the number and confirm it word_options = (12, 18, 20, 24, 33) @@ -85,67 +78,62 @@ def select_number_of_words( raise ValueError("Unknown model") if num_of_words in (20, 33): - assert "Enter any share" in layout.text_content() + TR.assert_in(layout.text_content(), "recovery.enter_any_share") else: - assert "Enter your backup" in layout.text_content() + TR.assert_in(layout.text_content(), "recovery.enter_backup") def enter_share( debug: "DebugLink", share: str, is_first: bool = True ) -> "LayoutContent": - if debug.model == "T": - layout = debug.click(buttons.OK, wait=True) - - assert layout.main_component() == "MnemonicKeyboard" - for word in share.split(" "): - layout = enter_word(debug, word, is_slip39=True) - - return layout - elif debug.model == "Safe 3": - assert "RECOVER WALLET" in debug.wait_layout().title() + TR.assert_in(debug.read_layout().title(), "recovery.title_recover") + if debug.model == "Safe 3": layout = debug.press_right(wait=True) if is_first: # Word entering info debug.press_right() layout = debug.press_right(wait=True) - assert "MnemonicKeyboard" in layout.all_components() + else: + layout = debug.click(buttons.OK, wait=True) - for word in share.split(" "): - layout = enter_word(debug, word, is_slip39=True) + assert "MnemonicKeyboard" in layout.all_components() - return layout - else: - raise ValueError("Unknown model") + for word in share.split(" "): + layout = enter_word(debug, word, is_slip39=True) + + return layout def enter_shares(debug: "DebugLink", shares: list[str]) -> None: - layout = debug.read_layout() - expected_text = "Enter any share" + TR.assert_in(debug.read_layout().text_content(), "recovery.enter_any_share") for index, share in enumerate(shares): - assert expected_text in layout.text_content() - layout = enter_share(debug, share, is_first=index == 0) - expected_text = f"{index + 1} of {len(shares)} shares entered" + enter_share(debug, share, is_first=index == 0) + if index < len(shares) - 1: + TR.assert_in( + debug.read_layout().text_content(), + "recovery.x_of_y_entered_template", + template=(index + 1, len(shares)), + ) - assert "Wallet recovered successfully" in layout.text_content() + TR.assert_in(debug.read_layout().text_content(), "recovery.wallet_recovered") def enter_seed(debug: "DebugLink", seed_words: list[str]) -> None: - assert "Enter" in debug.read_layout().text_content() + TR.assert_in(debug.read_layout().text_content(), "recovery.enter_backup") if debug.model == "T": - layout = debug.click(buttons.OK, wait=True) - assert layout.main_component() == "MnemonicKeyboard" + debug.click(buttons.OK, wait=True) elif debug.model == "Safe 3": - layout = debug.press_right(wait=True) - assert "RECOVER WALLET" in layout.title() + debug.press_right(wait=True) + TR.assert_equals(debug.read_layout().title(), "recovery.title_recover") debug.press_right() - layout = debug.press_right(wait=True) - assert "MnemonicKeyboard" in layout.all_components() + debug.press_right(wait=True) + assert "MnemonicKeyboard" in debug.read_layout().all_components() for word in seed_words: - layout = enter_word(debug, word, is_slip39=False) + enter_word(debug, word, is_slip39=False) - assert "Wallet recovered successfully" in layout.text_content() # type: ignore + TR.assert_in(debug.read_layout().text_content(), "recovery.wallet_recovered") def finalize(debug: "DebugLink") -> None: diff --git a/tests/click_tests/reset.py b/tests/click_tests/reset.py index 5824cc153..075958c0d 100644 --- a/tests/click_tests/reset.py +++ b/tests/click_tests/reset.py @@ -1,3 +1,4 @@ +import re from typing import TYPE_CHECKING from shamir_mnemonic import shamir # type: ignore @@ -5,14 +6,14 @@ from shamir_mnemonic import shamir # type: ignore from trezorlib import messages from .. import buttons +from .. import translations as TR if TYPE_CHECKING: from trezorlib.debuglink import DebugLink def confirm_new_wallet(debug: "DebugLink") -> None: - layout = debug.wait_layout() - assert layout.title().startswith("CREATE WALLET") + TR.assert_startswith(debug.read_layout().title(), "reset.title_create_wallet") if debug.model == "T": debug.click(buttons.OK, wait=True) elif debug.model == "Safe 3": @@ -20,32 +21,14 @@ def confirm_new_wallet(debug: "DebugLink") -> None: debug.press_right(wait=True) -def confirm_read(debug: "DebugLink", title: str, middle_r: bool = False) -> None: - layout = debug.read_layout() - if title == "Caution": - assert "Never make a digital copy" in layout.text_content() - elif title == "Success": - # TODO: improve this - assert any( - text in layout.text_content() - for text in ( - "success", - "finished", - "done", - "created", - "Keep it safe", - ) - ) - elif title == "Checklist": - assert "number of shares" in layout.text_content().lower() - else: - assert title.upper() in layout.title() - +def confirm_read(debug: "DebugLink", middle_r: bool = False) -> None: if debug.model == "T": debug.click(buttons.OK, wait=True) elif debug.model == "Safe 3": - if layout.page_count() > 1: - debug.press_right(wait=True) + page_count = debug.read_layout().page_count() + if page_count > 1: + for _ in range(page_count - 1): + debug.press_right(wait=True) if middle_r: debug.press_middle(wait=True) else: @@ -60,7 +43,9 @@ def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> Non debug.click(buttons.OK, wait=True) elif debug.model == "Safe 3": layout = debug.read_layout() - if layout.title() in ("NUMBER OF SHARES", "THRESHOLD"): + if layout.title() in TR.translate( + "reset.title_number_of_shares" + ) + TR.translate("words.title_threshold"): # Special info screens layout = debug.press_right(wait=True) assert "NumberInput" in layout.all_components() @@ -77,27 +62,12 @@ def read_words( debug: "DebugLink", backup_type: messages.BackupType, do_htc: bool = True ) -> list[str]: words: list[str] = [] - layout = debug.read_layout() - if debug.model == "T": - if backup_type == messages.BackupType.Slip39_Advanced: - assert layout.title().startswith("GROUP") - elif backup_type == messages.BackupType.Slip39_Basic: - assert layout.title().startswith("RECOVERY SHARE #") - else: - assert layout.title() == "RECOVERY SEED" - elif debug.model == "Safe 3": - if backup_type == messages.BackupType.Slip39_Advanced: - assert "SHARE" in layout.title() - elif backup_type == messages.BackupType.Slip39_Basic: - assert layout.title().startswith("SHARE #") - else: - assert layout.title() == "STANDARD BACKUP" - - assert "Write down" in layout.text_content() - layout = debug.press_right(wait=True) + if debug.model == "Safe 3": + debug.press_right(wait=True) # Swiping through all the pages and loading the words + layout = debug.read_layout() for _ in range(layout.page_count() - 1): words.extend(layout.seed_words()) layout = debug.swipe_up(wait=True) @@ -122,11 +92,13 @@ def read_words( def confirm_words(debug: "DebugLink", words: list[str]) -> None: layout = debug.wait_layout() if debug.model == "T": - assert "Select word" in layout.text_content() + TR.assert_template(layout.text_content(), "reset.select_word_x_of_y_template") for _ in range(3): # "Select word 3 of 20" # ^ - word_pos = int(layout.text_content().split()[2]) + word_pos_match = re.search(r"\d+", debug.wait_layout().text_content()) + assert word_pos_match is not None + word_pos = int(word_pos_match.group(0)) # Unifying both the buttons and words to lowercase btn_texts = [ text.lower() for text in layout.tt_check_seed_button_contents() @@ -135,7 +107,7 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None: button_pos = btn_texts.index(wanted_word) layout = debug.click(buttons.RESET_WORD_CHECK[button_pos], wait=True) elif debug.model == "Safe 3": - assert "Select the correct word" in layout.text_content() + TR.assert_in(layout.text_content(), "reset.select_correct_word") layout = debug.press_right(wait=True) for _ in range(3): # "SELECT 2ND WORD" diff --git a/tests/click_tests/test_autolock.py b/tests/click_tests/test_autolock.py index b15503a22..48606fbb0 100644 --- a/tests/click_tests/test_autolock.py +++ b/tests/click_tests/test_autolock.py @@ -25,6 +25,7 @@ from trezorlib.protobuf import MessageType from trezorlib.tools import parse_path from .. import buttons, common +from .. import translations as TR from ..device_tests.bitcoin.payment_req import make_coinjoin_request from ..tx_cache import TxCache from . import recovery @@ -63,10 +64,12 @@ def set_autolock_delay(device_handler: "BackgroundDeviceHandler", delay_ms: int) debug.input("1234") - assert ( - f"Auto-lock your Trezor after {delay_ms // 1000} seconds" - in debug.wait_layout().text_content() + TR.assert_in( + debug.wait_layout().text_content(), + "auto_lock.change_template", + template=[f"{delay_ms // 1000} seconds"], ) + layout = go_next(debug, wait=True) assert layout.main_component() == "Homescreen" assert device_handler.result() == "Settings applied" @@ -103,11 +106,13 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"): if debug.model == "T": debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK, wait=True) - assert "Total amount: 0.0039 BTC" in layout.text_content() + TR.assert_in(layout.text_content(), "send.total_amount") + assert "0.0039 BTC" in layout.text_content() elif debug.model == "Safe 3": debug.press_right(wait=True) layout = debug.press_right(wait=True) - assert "Total amount: 0.0039 BTC" in layout.text_content() + TR.assert_in(layout.text_content(), "send.total_amount") + assert "0.0039 BTC" in layout.text_content() # wait for autolock to kick in time.sleep(10.1) @@ -148,11 +153,13 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa if debug.model == "T": debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK, wait=True) - assert "Total amount: 0.0039 BTC" in layout.text_content() + TR.assert_in(layout.text_content(), "send.total_amount") + assert "0.0039 BTC" in layout.text_content() elif debug.model == "Safe 3": debug.press_right(wait=True) layout = debug.press_right(wait=True) - assert "Total amount: 0.0039 BTC" in layout.text_content() + TR.assert_in(layout.text_content(), "send.total_amount") + assert "0.0039 BTC" in layout.text_content() def sleepy_filter(msg: MessageType) -> MessageType: time.sleep(10.1) @@ -242,7 +249,7 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler def unlock_dry_run(debug: "DebugLink") -> "LayoutContent": - assert "Check your backup?" in debug.wait_layout().text_content() + TR.assert_in(debug.wait_layout().text_content(), "recovery.check_dry_run") layout = go_next(debug, wait=True) assert "PinKeyboard" in layout.all_components() @@ -259,7 +266,7 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle device_handler.run(device.recover, dry_run=True) # type: ignore layout = unlock_dry_run(debug) - assert "number of words" in layout.text_content() + TR.assert_in(debug.wait_layout().text_content(), "recovery.num_of_words") if debug.model == "Safe 3": debug.press_right(wait=True) @@ -279,7 +286,7 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle assert layout is not None # we are back at homescreen - assert "number of words" in layout.text_content() + TR.assert_in(debug.wait_layout().text_content(), "recovery.num_of_words") @pytest.mark.setup_client(pin=PIN4) diff --git a/tests/click_tests/test_passphrase_tr.py b/tests/click_tests/test_passphrase_tr.py index 508211c26..aac277e3e 100644 --- a/tests/click_tests/test_passphrase_tr.py +++ b/tests/click_tests/test_passphrase_tr.py @@ -21,6 +21,7 @@ import pytest from trezorlib import exceptions +from .. import translations as TR from ..common import get_test_address from .common import ( CommonPass, @@ -55,9 +56,22 @@ assert len(AAA_51) == 51 assert AAA_51_ADDRESS == AAA_50_ADDRESS +def _get_possible_btns(path: str) -> str: + return "|".join(TR.translate(path)) + + +def _get_cancel_or_delete() -> str: + paths = ["inputs.cancel", "inputs.delete"] + return "|".join(_get_possible_btns(path) for path in paths) + + +BACK = _get_possible_btns("inputs.back") +SHOW = _get_possible_btns("inputs.show") +ENTER = _get_possible_btns("inputs.enter") +SPACE = _get_possible_btns("inputs.space") +CANCEL_OR_DELETE = _get_cancel_or_delete() # fmt: off -BACK = "BACK" -MENU_ACTIONS = ["SHOW", "CANCEL|DELETE", "ENTER", "abc", "ABC", "123", "#$!", "SPACE"] +MENU_ACTIONS = [SHOW, CANCEL_OR_DELETE, ENTER, "abc", "ABC", "123", "#$!", SPACE] DIGITS_ACTIONS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", BACK] LOWERCASE_ACTIONS = [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", @@ -143,7 +157,7 @@ def press_char(debug: "DebugLink", char: str) -> None: # Space is a special case if char == " ": go_to_category(debug, PassphraseCategory.MENU) - navigate_to_action_and_press(debug, "SPACE", _current_actions(debug)) + navigate_to_action_and_press(debug, SPACE, _current_actions(debug)) else: char_category = get_char_category(char) go_to_category(debug, char_category) @@ -162,19 +176,19 @@ def input_passphrase(debug: "DebugLink", passphrase: str) -> None: def show_passphrase(debug: "DebugLink") -> None: """Show a passphrase""" go_to_category(debug, PassphraseCategory.MENU) - navigate_to_action_and_press(debug, "SHOW", _current_actions(debug)) + navigate_to_action_and_press(debug, SHOW, _current_actions(debug)) def enter_passphrase(debug: "DebugLink") -> None: """Enter a passphrase""" go_to_category(debug, PassphraseCategory.MENU) - navigate_to_action_and_press(debug, "ENTER", _current_actions(debug)) + navigate_to_action_and_press(debug, ENTER, _current_actions(debug)) def delete_char(debug: "DebugLink") -> None: """Deletes the last char""" go_to_category(debug, PassphraseCategory.MENU) - navigate_to_action_and_press(debug, "CANCEL|DELETE", _current_actions(debug)) + navigate_to_action_and_press(debug, CANCEL_OR_DELETE, _current_actions(debug)) def cancel(debug: "DebugLink") -> None: diff --git a/tests/click_tests/test_pin.py b/tests/click_tests/test_pin.py index 50e4e5921..011ec4b2e 100644 --- a/tests/click_tests/test_pin.py +++ b/tests/click_tests/test_pin.py @@ -23,6 +23,7 @@ import pytest from trezorlib import device, exceptions from .. import buttons +from .. import translations as TR from .common import go_back, go_next, navigate_to_action_and_press if TYPE_CHECKING: @@ -41,10 +42,20 @@ PIN24 = "875163065288639289952973" PIN50 = "31415926535897932384626433832795028841971693993751" PIN60 = PIN50 + "9" * 10 + +def _get_possible_btns(path: str) -> str: + return "|".join(TR.translate(path)) + + +DELETE = _get_possible_btns("inputs.delete") +SHOW = _get_possible_btns("inputs.show") +ENTER = _get_possible_btns("inputs.enter") + + TR_PIN_ACTIONS = [ - "DELETE", - "SHOW", - "ENTER", + DELETE, + SHOW, + ENTER, "0", "1", "2", @@ -84,7 +95,7 @@ def prepare( elif situation == Situation.PIN_SETUP: # Set new PIN device_handler.run(device.change_pin) # type: ignore - assert "Turn on" in debug.wait_layout().text_content() + TR.assert_in(debug.wait_layout().text_content(), "pin.turn_on") if debug.model == "T": go_next(debug) elif debug.model == "Safe 3": @@ -96,7 +107,7 @@ def prepare( # Change PIN device_handler.run(device.change_pin) # type: ignore _input_see_confirm(debug, old_pin) - assert "Change PIN" in debug.read_layout().text_content() + TR.assert_in(debug.wait_layout().text_content(), "pin.change") go_next(debug, wait=True) _input_see_confirm(debug, old_pin) elif situation == Situation.WIPE_CODE_SETUP: @@ -104,7 +115,7 @@ def prepare( device_handler.run(device.change_wipe_code) # type: ignore if old_pin: _input_see_confirm(debug, old_pin) - assert "Turn on" in debug.wait_layout().text_content() + TR.assert_in(debug.wait_layout().text_content(), "wipe_code.turn_on") go_next(debug, wait=True) if debug.model == "Safe 3": go_next(debug, wait=True) @@ -150,7 +161,7 @@ def _see_pin(debug: "DebugLink") -> None: if debug.model == "T": debug.click(buttons.TOP_ROW, wait=True) elif debug.model == "Safe 3": - navigate_to_action_and_press(debug, "SHOW", TR_PIN_ACTIONS) + navigate_to_action_and_press(debug, SHOW, TR_PIN_ACTIONS) def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -> None: @@ -162,7 +173,7 @@ def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) - if debug.model == "T": debug.click(buttons.pin_passphrase_grid(9), wait=True) elif debug.model == "Safe 3": - navigate_to_action_and_press(debug, "DELETE", TR_PIN_ACTIONS) + navigate_to_action_and_press(debug, DELETE, TR_PIN_ACTIONS) if check: after = debug.read_layout().pin() @@ -174,7 +185,7 @@ def _delete_all(debug: "DebugLink", check: bool = True) -> None: if debug.model == "T": debug.click_hold(buttons.pin_passphrase_grid(9), hold_ms=1500) elif debug.model == "Safe 3": - navigate_to_action_and_press(debug, "DELETE", TR_PIN_ACTIONS, hold_ms=1000) + navigate_to_action_and_press(debug, DELETE, TR_PIN_ACTIONS, hold_ms=1000) if check: after = debug.read_layout().pin() @@ -193,7 +204,7 @@ def _confirm_pin(debug: "DebugLink") -> None: if debug.model == "T": debug.click(buttons.pin_passphrase_grid(11), wait=True) elif debug.model == "Safe 3": - navigate_to_action_and_press(debug, "ENTER", TR_PIN_ACTIONS) + navigate_to_action_and_press(debug, ENTER, TR_PIN_ACTIONS) def _input_see_confirm(debug: "DebugLink", pin: str) -> None: diff --git a/tests/click_tests/test_reset_bip39.py b/tests/click_tests/test_reset_bip39.py index 62da0724c..7fd349e27 100644 --- a/tests/click_tests/test_reset_bip39.py +++ b/tests/click_tests/test_reset_bip39.py @@ -50,10 +50,10 @@ def test_reset_bip39(device_handler: "BackgroundDeviceHandler"): reset.confirm_new_wallet(debug) # confirm back up - reset.confirm_read(debug, "Success") + reset.confirm_read(debug) # confirm backup warning - reset.confirm_read(debug, "Caution", middle_r=True) + reset.confirm_read(debug, middle_r=True) # read words words = reset.read_words(debug, messages.BackupType.Bip39) @@ -62,7 +62,7 @@ def test_reset_bip39(device_handler: "BackgroundDeviceHandler"): reset.confirm_words(debug, words) # confirm backup done - reset.confirm_read(debug, "Success") + reset.confirm_read(debug) # Your backup is done go_next(debug) diff --git a/tests/click_tests/test_reset_slip39_advanced.py b/tests/click_tests/test_reset_slip39_advanced.py index 427ba71af..a49c58357 100644 --- a/tests/click_tests/test_reset_slip39_advanced.py +++ b/tests/click_tests/test_reset_slip39_advanced.py @@ -62,10 +62,10 @@ def test_reset_slip39_advanced( reset.confirm_new_wallet(debug) # confirm back up - reset.confirm_read(debug, "Success") + reset.confirm_read(debug) # confirm checklist - reset.confirm_read(debug, "Checklist") + reset.confirm_read(debug) # set num of groups - default is 5 if group_count < 5: @@ -74,7 +74,7 @@ def test_reset_slip39_advanced( reset.set_selection(debug, buttons.RESET_PLUS, group_count - 5) # confirm checklist - reset.confirm_read(debug, "Checklist") + reset.confirm_read(debug) # set group threshold # TODO: could make it general as well @@ -86,7 +86,7 @@ def test_reset_slip39_advanced( raise RuntimeError("not a supported combination") # confirm checklist - reset.confirm_read(debug, "Checklist") + reset.confirm_read(debug) # set share num and threshold for groups for _ in range(group_count): @@ -106,7 +106,7 @@ def test_reset_slip39_advanced( raise RuntimeError("not a supported combination") # confirm backup warning - reset.confirm_read(debug, "Caution", middle_r=True) + reset.confirm_read(debug, middle_r=True) all_words: list[str] = [] for _ in range(group_count): @@ -120,12 +120,12 @@ def test_reset_slip39_advanced( reset.confirm_words(debug, words) # confirm share checked - reset.confirm_read(debug, "Success") + reset.confirm_read(debug) all_words.append(" ".join(words)) # confirm backup done - reset.confirm_read(debug, "Success") + reset.confirm_read(debug) # generate secret locally internal_entropy = debug.state().reset_entropy diff --git a/tests/click_tests/test_reset_slip39_basic.py b/tests/click_tests/test_reset_slip39_basic.py index d9a264343..e0e3617bb 100644 --- a/tests/click_tests/test_reset_slip39_basic.py +++ b/tests/click_tests/test_reset_slip39_basic.py @@ -59,10 +59,10 @@ def test_reset_slip39_basic( reset.confirm_new_wallet(debug) # confirm back up - reset.confirm_read(debug, "Success") + reset.confirm_read(debug) # confirm checklist - reset.confirm_read(debug, "Checklist") + reset.confirm_read(debug) # set num of shares - default is 5 if num_of_shares < 5: @@ -71,7 +71,7 @@ def test_reset_slip39_basic( reset.set_selection(debug, buttons.RESET_PLUS, num_of_shares - 5) # confirm checklist - reset.confirm_read(debug, "Checklist") + reset.confirm_read(debug) # set threshold # TODO: could make it general as well @@ -83,10 +83,10 @@ def test_reset_slip39_basic( raise RuntimeError("not a supported combination") # confirm checklist - reset.confirm_read(debug, "Checklist") + reset.confirm_read(debug) # confirm backup warning - reset.confirm_read(debug, "Caution", middle_r=True) + reset.confirm_read(debug, middle_r=True) all_words: list[str] = [] for _ in range(num_of_shares): @@ -97,12 +97,12 @@ def test_reset_slip39_basic( reset.confirm_words(debug, words) # confirm share checked - reset.confirm_read(debug, "Success") + reset.confirm_read(debug) all_words.append(" ".join(words)) # confirm backup done - reset.confirm_read(debug, "Success") + reset.confirm_read(debug) # generate secret locally internal_entropy = debug.state().reset_entropy diff --git a/tests/click_tests/test_tutorial.py b/tests/click_tests/test_tutorial.py index 0cb70387b..a7ce84ec1 100644 --- a/tests/click_tests/test_tutorial.py +++ b/tests/click_tests/test_tutorial.py @@ -22,6 +22,8 @@ import pytest from trezorlib import device from trezorlib.exceptions import Cancelled +from .. import translations as TR + if TYPE_CHECKING: from trezorlib.debuglink import DebugLink @@ -55,7 +57,7 @@ def go_through_tutorial(debug: "DebugLink") -> None: debug.press_right(wait=True) debug.press_right(wait=True) layout = debug.press_middle(wait=True) - assert layout.title() == "TUTORIAL COMPLETE" + TR.assert_equals(layout.title(), "tutorial.title_tutorial_complete") @pytest.mark.setup_client(uninitialized=True) diff --git a/tests/common.py b/tests/common.py index 0b165e9f2..f07e9b469 100644 --- a/tests/common.py +++ b/tests/common.py @@ -214,7 +214,11 @@ def read_and_confirm_mnemonic_tt( # check share for _ in range(3): - word_pos = int(debug.wait_layout().text_content().split()[2]) + # Word position is the first number in the text + word_pos_match = re.search(r"\d+", debug.wait_layout().text_content()) + assert word_pos_match is not None + word_pos = int(word_pos_match.group(0)) + index = word_pos - 1 if choose_wrong: debug.input(mnemonic[(index + 1) % len(mnemonic)]) @@ -278,7 +282,7 @@ def get_test_address(client: "Client") -> str: return btc.get_address(client, "Testnet", TEST_ADDRESS_N) -def compact_size(n) -> bytes: +def compact_size(n: int) -> bytes: if n < 253: return n.to_bytes(1, "little") elif n < 0x1_0000: diff --git a/tests/conftest.py b/tests/conftest.py index 432cd22fe..5216db44f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,9 +23,9 @@ from typing import TYPE_CHECKING, Generator, Iterator import pytest import xdist -from trezorlib import debuglink, log +from trezorlib import debuglink, log, translations from trezorlib.debuglink import TrezorClientDebugLink as Client -from trezorlib.device import apply_settings +from trezorlib.device import apply_settings, change_language from trezorlib.device import wipe as wipe_device from trezorlib.transport import enumerate_devices, get_transport @@ -42,6 +42,11 @@ if TYPE_CHECKING: HERE = Path(__file__).resolve().parent +CORE = HERE.parent / "core" +TRANSLATIONS = CORE / "embed" / "rust" / "src" / "ui" / "translations" + +CS_JSON = TRANSLATIONS / "cs.json" +FR_JSON = TRANSLATIONS / "fr.json" # So that we see details of failed asserts from this module @@ -128,14 +133,41 @@ def _raw_client(request: pytest.FixtureRequest) -> Client: # Requesting the emulator fixture only if relevant. if request.session.config.getoption("control_emulators"): emu_fixture = request.getfixturevalue("emulator") - return emu_fixture.client + client = emu_fixture.client else: interact = os.environ.get("INTERACT") == "1" path = os.environ.get("TREZOR_PATH") if path: - return _client_from_path(request, path, interact) + client = _client_from_path(request, path, interact) else: - return _find_client(request, interact) + client = _find_client(request, interact) + + # Setting the appropriate language + # Not doing it for T1 + if client.features.model != "1": + lang = request.session.config.getoption("lang") or "en" + _set_language(client, lang) # type: ignore + + return client + + +def _set_language(client: Client, lang: str) -> Client: + model = client.features.model or "" + if lang == "en": + with client: + change_language(client, language_data=b"") + elif lang == "cs": + change_language( + client, language_data=translations.blob_from_file(CS_JSON, model) + ) + elif lang == "fr": + change_language( + client, language_data=translations.blob_from_file(FR_JSON, model) + ) + else: + raise RuntimeError(f"Unknown language: {lang}") + + return client def _client_from_path( @@ -357,6 +389,12 @@ def pytest_addoption(parser: "Parser") -> None: help="Generating a master-diff report. " "This shows all unique differing screens compared to master.", ) + parser.addoption( + "--lang", + action="store", + choices=["en", "cs", "fr"], + help="Run tests with a specified language: 'en' (default), 'cs' or 'fr'", + ) def pytest_configure(config: "Config") -> None: diff --git a/tests/device_tests/bitcoin/test_signmessage.py b/tests/device_tests/bitcoin/test_signmessage.py index ad4d04d92..f60a88f17 100644 --- a/tests/device_tests/bitcoin/test_signmessage.py +++ b/tests/device_tests/bitcoin/test_signmessage.py @@ -364,9 +364,9 @@ def test_signmessage_pagination(client: Client, message: str): # We cannot differentiate between a newline and space in the message read from Trezor. # TODO: do the check also for model R if client.features.model == "T": - expected_message = message.replace("\n", "").replace(" ", "") message_read = IF.message_read.replace(" ", "").replace("...", "") - assert expected_message == message_read + signed_message = message.replace("\n", "").replace(" ", "") + assert signed_message in message_read @pytest.mark.skip_t1 diff --git a/tests/device_tests/test_language.py b/tests/device_tests/test_language.py new file mode 100644 index 000000000..3b8d2bdcd --- /dev/null +++ b/tests/device_tests/test_language.py @@ -0,0 +1,324 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2019 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import json +from contextlib import contextmanager +from copy import deepcopy +from pathlib import Path +from typing import Any, Dict, Generator + +import pytest + +from trezorlib import debuglink, device, exceptions, messages, translations +from trezorlib.debuglink import TrezorClientDebugLink as Client + +pytestmark = pytest.mark.skip_t1 + +HERE = Path(__file__).parent.resolve() +CORE = HERE.parent.parent / "core" +TRANSLATIONS = CORE / "embed" / "rust" / "src" / "ui" / "translations" +FONTS = TRANSLATIONS / "fonts" +ORDER_FILE = TRANSLATIONS / "order.json" + +EN_JSON = TRANSLATIONS / "en.json" +CS_JSON = TRANSLATIONS / "cs.json" +FR_JSON = TRANSLATIONS / "fr.json" + +MAX_DATA_LENGTH = {"T": 48 * 1024, "Safe 3": 32 * 1024} + + +def _read_confirm_word(file: Path) -> str: + content = json.loads(file.read_text()) + return content["translations"]["words"]["confirm"] + + +ENGLISH_CONFIRM = _read_confirm_word(EN_JSON) +CZECH_CONFIRM = _read_confirm_word(CS_JSON) +FRENCH_CONFIRM = _read_confirm_word(FR_JSON) + + +@contextmanager +def _set_english_return_back(client: Client) -> Generator[Client, None, None]: + lang_before = client.features.language or "" + try: + _set_default_english(client) + yield client + finally: + if lang_before.startswith("en"): + _set_default_english(client) + elif lang_before == "cs": + _set_full_czech(client) + elif lang_before == "fr": + _set_full_french(client) + else: + raise RuntimeError(f"Unknown language: {lang_before}") + + +def _set_full_czech(client: Client): + device.change_language( + client, + language_data=translations.blob_from_file(CS_JSON, client.features.model or ""), + ) + + +def _set_full_french(client: Client): + device.change_language( + client, + language_data=translations.blob_from_file(FR_JSON, client.features.model or ""), + ) + + +def _set_default_english(client: Client): + with client: + device.change_language(client, language_data=b"") + + +def _get_data_from_dict(data: Dict[str, Any], client: Client) -> bytes: + return translations.blob_from_dict( + data, + font_dir=FONTS, + order_json_file=ORDER_FILE, + model=client.features.model or "", + ) + + +def _check_ping_screen_texts(client: Client, title: str, right_button: str) -> None: + def ping_input_flow(client: Client, title: str, right_button: str): + yield + layout = client.debug.wait_layout() + assert layout.title() == title.upper() + assert layout.button_contents()[-1] == right_button.upper() + client.debug.press_yes() + + # TT does not have a right button text (but a green OK tick) + if client.features.model == "T": + right_button = "-" + + with client: + client.watch_layout(True) + client.set_input_flow(ping_input_flow(client, title, right_button)) + ping = client.call(messages.Ping(message="ahoj!", button_protection=True)) + assert ping == messages.Success(message="ahoj!") + + +def test_change_language_errors(client: Client): + with _set_english_return_back(client) as client: + assert client.features.language == "en-US" + + # Translations too short + # Sending less data than the header length + with pytest.raises( + exceptions.TrezorFailure, match="Translations too short" + ), client: + bad_data = (translations.HEADER_LEN - 1) * b"a" + device.change_language(client, language_data=bad_data) + assert client.features.language == "en-US" + + # Translations too long + # Sending more than allowed by the flash capacity + max_length = MAX_DATA_LENGTH[client.features.model] + with pytest.raises( + exceptions.TrezorFailure, match="Translations too long" + ), client: + bad_data = (max_length + 1) * b"a" + device.change_language(client, language_data=bad_data) + assert client.features.language == "en-US" + + # Invalid header data length + # Sending more data than advertised in the header + with pytest.raises( + exceptions.TrezorFailure, match="Invalid header data length" + ), client: + good_data = translations.blob_from_file( + CS_JSON, client.features.model or "" + ) + bad_data = good_data + b"abcd" + device.change_language(client, language_data=bad_data) + assert client.features.language == "en-US" + + # Invalid header magic + # Does not match the expected magic + with pytest.raises( + exceptions.TrezorFailure, match="Invalid header magic" + ), client: + good_data = translations.blob_from_file( + CS_JSON, client.features.model or "" + ) + bad_data = 4 * b"a" + good_data[4:] + device.change_language(client, language_data=bad_data) + assert client.features.language == "en-US" + + # Invalid header data + # Putting non-zero bytes where zero is expected + with pytest.raises( + exceptions.TrezorFailure, match="Invalid header data" + ), client: + good_data = translations.blob_from_file( + CS_JSON, client.features.model or "" + ) + pre_sig_pos = translations.HEADER_LEN - translations.SIG_LEN + bad_data = good_data[: pre_sig_pos - 4] + 4 * b"a" + good_data[pre_sig_pos:] + device.change_language( + client, + language_data=bad_data, + ) + assert client.features.language == "en-US" + + # Invalid data hash + # Changing the data after their hash has been calculated + with pytest.raises(exceptions.TrezorFailure, match="Invalid data hash"), client: + good_data = translations.blob_from_file( + CS_JSON, client.features.model or "" + ) + bad_data = good_data[:-8] + 8 * b"a" + device.change_language( + client, + language_data=bad_data, + ) + assert client.features.language == "en-US" + + # Invalid translations version + # Change the version to one not matching the current device + with pytest.raises( + exceptions.TrezorFailure, match="Invalid translations version" + ), client: + with open(CS_JSON, "r") as f: + data = json.load(f) + data["header"]["version"] = "3.5.4" + device.change_language( + client, + language_data=_get_data_from_dict(data, client), + ) + assert client.features.language == "en-US" + + # Invalid header version + # Version is not a valid semver with integers + with pytest.raises( + exceptions.TrezorFailure, match="Invalid header version" + ), client: + with open(CS_JSON, "r") as f: + data = json.load(f) + data["header"]["version"] = "ABC.XYZ.DEF" + device.change_language( + client, + language_data=_get_data_from_dict(data, client), + ) + assert client.features.language == "en-US" + + # Invalid translations signature + # Modifying the signature part of the header + with pytest.raises( + exceptions.TrezorFailure, match="Invalid translations signature" + ), client: + good_data = translations.blob_from_file( + CS_JSON, client.features.model or "" + ) + bad_data = ( + good_data[: translations.HEADER_LEN - 8] + + 8 * b"a" + + good_data[translations.HEADER_LEN :] + ) + device.change_language( + client, + language_data=bad_data, + ) + assert client.features.language == "en-US" + + _check_ping_screen_texts(client, ENGLISH_CONFIRM, ENGLISH_CONFIRM) + + +def test_full_language_change(client: Client): + with _set_english_return_back(client) as client: + assert client.features.language == "en-US" + + # Setting cs language + _set_full_czech(client) + assert client.features.language == "cs" + _check_ping_screen_texts(client, CZECH_CONFIRM, CZECH_CONFIRM) + + # Setting fr language + _set_full_french(client) + assert client.features.language == "fr" + _check_ping_screen_texts(client, FRENCH_CONFIRM, FRENCH_CONFIRM) + + # Setting the default language via empty data + _set_default_english(client) + assert client.features.language == "en-US" + _check_ping_screen_texts(client, ENGLISH_CONFIRM, ENGLISH_CONFIRM) + + +def test_language_stays_after_wipe(client: Client): + with _set_english_return_back(client) as client: + assert client.features.language == "en-US" + + _check_ping_screen_texts(client, ENGLISH_CONFIRM, ENGLISH_CONFIRM) + + # Setting cs language + _set_full_czech(client) + assert client.features.language == "cs" + + _check_ping_screen_texts(client, CZECH_CONFIRM, CZECH_CONFIRM) + + # Wipe device + device.wipe(client) + assert client.features.language == "cs" + + # Load it again + debuglink.load_device( + client, + mnemonic=" ".join(["all"] * 12), + pin=None, + passphrase_protection=False, + label="test", + ) + assert client.features.language == "cs" + + _check_ping_screen_texts(client, CZECH_CONFIRM, CZECH_CONFIRM) + + +def test_translations_renders_on_screen(client: Client): + with open(CS_JSON, "r") as f: + czech_data = json.load(f) + + # Setting some values of words__confirm key and checking that in ping screen title + with _set_english_return_back(client) as client: + assert client.features.language == "en-US" + + # Normal english + _check_ping_screen_texts(client, ENGLISH_CONFIRM, ENGLISH_CONFIRM) + + # Normal czech + _set_full_czech(client) + assert client.features.language == "cs" + _check_ping_screen_texts(client, CZECH_CONFIRM, CZECH_CONFIRM) + + # Modified czech - changed value + czech_data_copy = deepcopy(czech_data) + new_czech_confirm = "ABCD" + czech_data_copy["translations"]["words"]["confirm"] = new_czech_confirm + device.change_language( + client, + language_data=_get_data_from_dict(czech_data_copy, client), + ) + _check_ping_screen_texts(client, new_czech_confirm, CZECH_CONFIRM) + + # Modified czech - key deleted completely, english is shown + czech_data_copy = deepcopy(czech_data) + del czech_data_copy["translations"]["words"]["confirm"] + device.change_language( + client, language_data=_get_data_from_dict(czech_data_copy, client) + ) + _check_ping_screen_texts(client, ENGLISH_CONFIRM, CZECH_CONFIRM) diff --git a/tests/device_tests/test_sdcard.py b/tests/device_tests/test_sdcard.py index 983d1d914..ed6a3079c 100644 --- a/tests/device_tests/test_sdcard.py +++ b/tests/device_tests/test_sdcard.py @@ -21,6 +21,8 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.exceptions import TrezorFailure from trezorlib.messages import SdProtectOperationType as Op +from .. import translations as TR + pytestmark = [pytest.mark.skip_t1, pytest.mark.skip_tr] @@ -57,7 +59,7 @@ def test_sd_protect_unlock(client: Client): client.debug.input("1234") yield # do you really want to enable SD protection - assert "SD card protection" in layout().text_content() + TR.assert_in(layout().text_content(), "sd_card.enable") client.debug.press_yes() yield # enter current PIN @@ -65,7 +67,7 @@ def test_sd_protect_unlock(client: Client): client.debug.input("1234") yield # you have successfully enabled SD protection - assert "You have successfully enabled SD protection." in layout().text_content() + TR.assert_in(layout().text_content(), "sd_card.enabled") client.debug.press_yes() with client: @@ -75,7 +77,7 @@ def test_sd_protect_unlock(client: Client): def input_flow_change_pin(): yield # do you really want to change PIN? - assert "PIN SETTINGS" == layout().title() + TR.assert_equals(layout().title(), "pin.title_settings") client.debug.press_yes() yield # enter current PIN @@ -91,7 +93,7 @@ def test_sd_protect_unlock(client: Client): client.debug.input("1234") yield # Pin change successful - assert "PIN changed" in layout().text_content() + TR.assert_in(layout().text_content(), "pin.changed") client.debug.press_yes() with client: @@ -103,7 +105,7 @@ def test_sd_protect_unlock(client: Client): def input_flow_change_pin_format(): yield # do you really want to change PIN? - assert "PIN SETTINGS" == layout().title() + TR.assert_equals(layout().title(), "pin.title_settings") client.debug.press_yes() yield # enter current PIN @@ -111,7 +113,7 @@ def test_sd_protect_unlock(client: Client): client.debug.input("1234") yield # SD card problem - assert "Wrong SD card" in layout().text_content() + TR.assert_in(layout().text_content(), "sd_card.unplug_and_insert_correct") client.debug.press_no() # close with client, pytest.raises(TrezorFailure) as e: diff --git a/tests/device_tests/test_session_id_and_passphrase.py b/tests/device_tests/test_session_id_and_passphrase.py index 2bb9677e5..291bfc152 100644 --- a/tests/device_tests/test_session_id_and_passphrase.py +++ b/tests/device_tests/test_session_id_and_passphrase.py @@ -24,6 +24,8 @@ from trezorlib.exceptions import TrezorFailure from trezorlib.messages import FailureType, SafetyCheckLevel from trezorlib.tools import parse_path +from .. import translations as TR + XPUB_PASSPHRASES = { "A": "xpub6CekxGcnqnJ6osfY4Rrq7W5ogFtR54KUvz4H16XzaQuukMFZCGebEpVznfq4yFcKEmYyShwj2UKjL7CazuNSuhdkofF4mHabHkLxCMVvsqG", "B": "xpub6CFxuyQpgryoR64QC38w42dLgDv5P4qWXhn1fbaN62UYzu1wJXZyrYqGnkq5d8xPUK68RXtXFBiqp3rfLGpeQ57zLtx675ZZn5ezKMAWQfu", @@ -395,16 +397,13 @@ def test_hide_passphrase_from_host(client: Client): def input_flow(): yield - layout = client.debug.wait_layout() + TR.assert_in( + client.debug.wait_layout().text_content(), + "passphrase.access_hidden_wallet", + ) if client.debug.model == "T": - assert ( - "Passphrase provided by host will be used but will not be displayed due to the device settings." - in layout.text_content() - ) client.debug.press_yes() elif client.debug.model == "Safe 3": - layout = client.debug.wait_layout() - assert "will not be displayed" in layout.text_content() client.debug.press_right() client.debug.press_right() client.debug.press_yes() @@ -433,15 +432,18 @@ def test_hide_passphrase_from_host(client: Client): def input_flow(): yield - layout = client.debug.wait_layout() - assert "Next screen will show the passphrase" in layout.text_content() + TR.assert_in( + client.debug.wait_layout().text_content(), + "passphrase.next_screen_will_show_passphrase", + ) client.debug.press_yes() yield - layout = client.debug.wait_layout() - assert "confirm passphrase" in layout.title().lower() - - assert passphrase in layout.text_content() + TR.assert_equals( + client.debug.wait_layout().title(), + "passphrase.title_confirm", + ) + assert passphrase in client.debug.wait_layout().text_content() client.debug.press_yes() client.watch_layout() diff --git a/tests/gitlab.py b/tests/gitlab.py index b7719bf38..d6056249a 100644 --- a/tests/gitlab.py +++ b/tests/gitlab.py @@ -24,14 +24,30 @@ RAW_REPORT_URL_TEMPLATE = ( "https://gitlab.com/satoshilabs/trezor/trezor-firmware/-/jobs/{}/raw" ) -UI_JOB_NAMES = ( +UI_JOBS_ENGLISH = [ "core click R test", "core device R test", "core click test", "core device test", "core persistence test", "legacy device test", -) +] + +UI_JOBS_FRENCH = [ + "core click R test french", + "core device R test french", + "core click test french", + "core device test french", +] + +UI_JOBS_CZECH = [ + "core click R test czech", + "core device R test czech", + "core click test czech", + "core device test czech", +] + +UI_JOB_NAMES = UI_JOBS_ENGLISH + UI_JOBS_FRENCH + UI_JOBS_CZECH SAVE_GRAPHQL_RESULTS = False diff --git a/tests/input_flows.py b/tests/input_flows.py index f1a590fbc..c296a2f7f 100644 --- a/tests/input_flows.py +++ b/tests/input_flows.py @@ -20,6 +20,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import multipage_content from . import buttons +from . import translations as TR from .common import ( BRGeneratorType, check_pin_backoff_time, @@ -264,7 +265,7 @@ class InputFlowSignMessageInfo(InputFlowBase): self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.click(buttons.CORNER_BUTTON, wait=True) self.debug.press_no(wait=True) - self.debug.synchronize_at("mismatch") + self.debug.synchronize_at("IconDialog") # address mismatch? self.debug.press_no() yield @@ -358,7 +359,8 @@ class InputFlowShowMultisigXPUBs(InputFlowBase): yield # show address layout = self.debug.wait_layout() - assert "RECEIVE ADDRESS\n(MULTISIG)" == layout.title() + TR.assert_in(layout.title(), "address.title_receive_address") + assert "(MULTISIG)" in layout.title() assert layout.text_content().replace(" ", "") == self.address self.debug.click(buttons.CORNER_BUTTON) @@ -367,15 +369,14 @@ class InputFlowShowMultisigXPUBs(InputFlowBase): layout = self.debug.swipe_left(wait=True) # address details assert "Multisig 2 of 3" in layout.screen_content() - assert "Derivation path:" in layout.screen_content() + TR.assert_in(layout.screen_content(), "address_details.derivation_path") # Three xpub pages with the same testing logic for xpub_num in range(3): - expected_title = f"MULTISIG XPUB #{xpub_num + 1}\n" + ( - "(YOURS)" if self.index == xpub_num else "(COSIGNER)" - ) + # TODO: might also check YOURS vs COSIGNER + expected_title = f"MULTISIG XPUB #{xpub_num + 1}" layout = self.debug.swipe_left(wait=True) - assert expected_title == layout.title() + assert expected_title in layout.title() content = layout.text_content().replace(" ", "") assert self.xpubs[xpub_num] in content @@ -393,7 +394,8 @@ class InputFlowShowMultisigXPUBs(InputFlowBase): yield # show address layout = self.debug.wait_layout() - assert "RECEIVE ADDRESS (MULTISIG)" in layout.title() + TR.assert_in(layout.title(), "address.title_receive_address") + assert "(MULTISIG)" in layout.title() assert layout.text_content().replace(" ", "") == self.address self.debug.press_right() @@ -406,9 +408,8 @@ class InputFlowShowMultisigXPUBs(InputFlowBase): # Three xpub pages with the same testing logic for xpub_num in range(3): - expected_title = f"MULTISIG XPUB #{xpub_num + 1} " + ( - "(YOURS)" if self.index == xpub_num else "(COSIGNER)" - ) + # TODO: might also check YOURS vs COSIGNER + expected_title = f"MULTISIG XPUB #{xpub_num + 1}" layout = self.debug.press_right(wait=True) assert expected_title in layout.title() xpub_part_1 = layout.text_content().replace(" ", "") @@ -554,7 +555,7 @@ def sign_tx_go_to_info(client: Client) -> Generator[None, None, str]: client.debug.press_info() layout = client.debug.wait_layout() - content = layout.text_content().lower() + content = layout.text_content() client.debug.click(buttons.CORNER_BUTTON, wait=True) @@ -595,20 +596,20 @@ class InputFlowSignTxInformation(InputFlowBase): def __init__(self, client: Client): super().__init__(client) - def assert_content(self, content: str) -> None: - assert "sending from" in content - assert "legacy #6" in content - assert "fee rate" in content + def assert_content(self, content: str, title_path: str) -> None: + TR.assert_in(content, title_path) + assert "Legacy #6" in content + TR.assert_in(content, "confirm_total.fee_rate") assert "71.56 sat" in content def input_flow_tt(self) -> BRGeneratorType: content = yield from sign_tx_go_to_info(self.client) - self.assert_content(content) + self.assert_content(content, "confirm_total.sending_from_account") self.debug.press_yes() def input_flow_tr(self) -> BRGeneratorType: content = yield from sign_tx_go_to_info_tr(self.client) - self.assert_content(content.lower()) + self.assert_content(content, "confirm_total.title_sending_from") self.debug.press_yes() @@ -616,10 +617,10 @@ class InputFlowSignTxInformationMixed(InputFlowBase): def __init__(self, client: Client): super().__init__(client) - def assert_content(self, content: str) -> None: - assert "sending from" in content - assert "multiple accounts" in content - assert "fee rate" in content + def assert_content(self, content: str, title_path: str) -> None: + TR.assert_in(content, title_path) + TR.assert_in(content, "bitcoin.multiple_accounts") + TR.assert_in(content, "confirm_total.fee_rate") assert "18.33 sat" in content def input_flow_tt(self) -> BRGeneratorType: @@ -628,12 +629,16 @@ class InputFlowSignTxInformationMixed(InputFlowBase): self.debug.press_yes() content = yield from sign_tx_go_to_info(self.client) - self.assert_content(content) + self.assert_content(content, "confirm_total.sending_from_account") self.debug.press_yes() def input_flow_tr(self) -> BRGeneratorType: + # multiple accounts warning + yield + self.debug.press_yes() + content = yield from sign_tx_go_to_info_tr(self.client) - self.assert_content(content.lower()) + self.assert_content(content, "confirm_total.title_sending_from") self.debug.press_yes() @@ -734,7 +739,7 @@ class InputFlowLockTimeBlockHeight(InputFlowBase): def assert_func(self, debug: DebugLink) -> None: layout_text = debug.wait_layout().text_content() - assert "blockheight" in layout_text + TR.assert_in(layout_text, "bitcoin.locktime_set_to_blockheight") assert self.block_height in layout_text def input_flow_tt(self) -> BRGeneratorType: @@ -753,7 +758,7 @@ class InputFlowLockTimeDatetime(InputFlowBase): def assert_func(self, debug: DebugLink): layout_text = debug.wait_layout().text_content() - assert "Locktime" in layout_text + TR.assert_in(layout_text, "bitcoin.locktime_set_to") assert self.lock_time_str in layout_text def input_flow_tt(self) -> BRGeneratorType: @@ -1594,10 +1599,15 @@ class InputFlowResetSkipBackup(InputFlowBase): def input_flow_common(self) -> BRGeneratorType: yield from self.BAK.confirm_new_wallet() yield # Skip Backup - assert "New wallet created" in self.text_content() + info_path = ( + "backup.new_wallet_created" + if self.debug.model == "Safe 3" + else "backup.new_wallet_successfully_created" + ) + TR.assert_in(self.text_content(), info_path) if self.debug.model == "Safe 3": self.debug.press_right() self.debug.press_no() yield # Confirm skip backup - assert "skip the backup" in self.text_content() + TR.assert_in(self.text_content(), "backup.want_to_skip") self.debug.press_no() diff --git a/tests/input_flows_helpers.py b/tests/input_flows_helpers.py index 5b73924d1..3b73f8741 100644 --- a/tests/input_flows_helpers.py +++ b/tests/input_flows_helpers.py @@ -1,6 +1,7 @@ from trezorlib import messages from trezorlib.debuglink import TrezorClientDebugLink as Client +from . import translations as TR from .common import BRGeneratorType B = messages.ButtonRequestType @@ -19,7 +20,9 @@ class PinFlow: self.debug.input(pin) if self.debug.model == "Safe 3": yield # Reenter PIN - assert "re-enter PIN" in self.debug.wait_layout().text_content() + TR.assert_in( + self.debug.wait_layout().text_content(), "pin.reenter_to_confirm" + ) self.debug.press_yes() yield # Enter PIN again assert "PinKeyboard" in self.debug.wait_layout().all_components() @@ -36,7 +39,7 @@ class BackupFlow: def confirm_new_wallet(self) -> BRGeneratorType: yield - assert "By continuing you agree" in self.debug.wait_layout().text_content() + TR.assert_in(self.debug.wait_layout().text_content(), "reset.by_continuing") if self.debug.model == "Safe 3": self.debug.press_right() self.debug.press_yes() @@ -47,16 +50,19 @@ class RecoveryFlow: self.client = client self.debug = self.client.debug + def _text_content(self) -> str: + return self.debug.wait_layout().text_content() + def confirm_recovery(self) -> BRGeneratorType: yield - assert "By continuing you agree" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "reset.by_continuing") if self.debug.model == "Safe 3": self.debug.press_right() self.debug.press_yes() def confirm_dry_run(self) -> BRGeneratorType: yield - assert "Check your backup" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.check_dry_run") self.debug.press_yes() def setup_slip39_recovery(self, num_words: int) -> BRGeneratorType: @@ -73,16 +79,17 @@ class RecoveryFlow: def tr_recovery_homescreen(self) -> BRGeneratorType: yield - assert "number of words" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.num_of_words") self.debug.press_yes() def enter_your_backup(self) -> BRGeneratorType: yield - assert "Enter your backup" in self.debug.wait_layout().text_content() - if ( - self.debug.model == "Safe 3" - and "BACKUP CHECK" not in self.debug.wait_layout().title() - ): + TR.assert_in(self._text_content(), "recovery.enter_backup") + is_dry_run = any( + title in self.debug.wait_layout().title() + for title in TR.translate("recovery.title_dry_run") + ) + if self.debug.model == "Safe 3" and not is_dry_run: # Normal recovery has extra info (not dry run) self.debug.press_right(wait=True) self.debug.press_right(wait=True) @@ -90,11 +97,12 @@ class RecoveryFlow: def enter_any_share(self) -> BRGeneratorType: yield - assert "Enter any share" in self.debug.wait_layout().text_content() - if ( - self.debug.model == "Safe 3" - and "BACKUP CHECK" not in self.debug.wait_layout().title() - ): + TR.assert_in(self._text_content(), "recovery.enter_any_share") + is_dry_run = any( + title in self.debug.wait_layout().title() + for title in TR.translate("recovery.title_dry_run") + ) + if self.debug.model == "Safe 3" and not is_dry_run: # Normal recovery has extra info (not dry run) self.debug.press_right(wait=True) self.debug.press_right(wait=True) @@ -103,13 +111,13 @@ class RecoveryFlow: def abort_recovery(self, confirm: bool) -> BRGeneratorType: yield if self.debug.model == "Safe 3": - assert "number of words" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.num_of_words") else: - assert "Enter any share" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.enter_any_share") self.debug.press_no() yield - assert "cancel the recovery" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.wanna_cancel_recovery") if self.debug.model == "Safe 3": self.debug.press_right() if confirm: @@ -121,105 +129,99 @@ class RecoveryFlow: br = yield assert br.code == B.MnemonicWordCount if self.debug.model == "Safe 3": - assert "NUMBER OF WORDS" in self.debug.wait_layout().title() + TR.assert_in(self.debug.wait_layout().title(), "word_count.title") else: - assert "number of words" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.num_of_words") self.debug.input(str(num_words)) def warning_invalid_recovery_seed(self) -> BRGeneratorType: br = yield assert br.code == B.Warning - assert "Invalid recovery seed" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.invalid_seed_entered") self.debug.press_yes() def warning_invalid_recovery_share(self) -> BRGeneratorType: br = yield assert br.code == B.Warning - assert "Invalid recovery share" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.invalid_share_entered") self.debug.press_yes() def warning_group_threshold_reached(self) -> BRGeneratorType: br = yield assert br.code == B.Warning - assert "Group threshold reached" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.group_threshold_reached") self.debug.press_yes() def warning_share_already_entered(self) -> BRGeneratorType: br = yield assert br.code == B.Warning - assert "Share already entered" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.share_already_entered") self.debug.press_yes() def warning_share_from_another_shamir(self) -> BRGeneratorType: br = yield assert br.code == B.Warning - assert ( - "You have entered a share from another Shamir Backup" - in self.debug.wait_layout().text_content() - ) + TR.assert_in(self._text_content(), "recovery.share_from_another_shamir") self.debug.press_yes() def success_share_group_entered(self) -> BRGeneratorType: yield - assert "You have entered" in self.debug.wait_layout().text_content() - assert "Group" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.you_have_entered") self.debug.press_yes() def success_wallet_recovered(self) -> BRGeneratorType: br = yield assert br.code == B.Success - assert ( - "Wallet recovered successfully" in self.debug.wait_layout().text_content() - ) + TR.assert_in(self._text_content(), "recovery.wallet_recovered") self.debug.press_yes() def success_bip39_dry_run_valid(self) -> BRGeneratorType: br = yield assert br.code == B.Success - assert "recovery seed is valid" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.dry_run_bip39_valid_match") self.debug.press_yes() def success_slip39_dryrun_valid(self) -> BRGeneratorType: br = yield assert br.code == B.Success - assert "recovery shares are valid" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.dry_run_slip39_valid_match") self.debug.press_yes() def warning_slip39_dryrun_mismatch(self) -> BRGeneratorType: br = yield assert br.code == B.Warning - assert "do not match" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.dry_run_slip39_valid_mismatch") self.debug.press_yes() def warning_bip39_dryrun_mismatch(self) -> BRGeneratorType: br = yield assert br.code == B.Warning - assert "does not match" in self.debug.wait_layout().text_content() + TR.assert_in(self._text_content(), "recovery.dry_run_bip39_valid_mismatch") self.debug.press_yes() def success_more_shares_needed( self, count_needed: int | None = None ) -> BRGeneratorType: yield - assert ( - "1 more share needed" in self.debug.wait_layout().text_content().lower() - or "more shares needed" in self.debug.wait_layout().text_content().lower() - ) + # TODO: do this plural assert + # assert ( + # "1 more share needed" in self._text_content() + # or "More shares needed" in self._text_content() + # ) if count_needed is not None: - assert str(count_needed) in self.debug.wait_layout().text_content() + assert str(count_needed) in self._text_content() self.debug.press_yes() def input_mnemonic(self, mnemonic: list[str]) -> BRGeneratorType: br = yield assert br.code == B.MnemonicInput assert "MnemonicKeyboard" in self.debug.wait_layout().all_components() - for index, word in enumerate(mnemonic): - if self.debug.model == "Safe 3": - assert f"WORD {index + 1}" in self.debug.wait_layout().title() - else: - assert ( - f"Type word {index + 1}" in self.debug.wait_layout().text_content() - ) + for _, word in enumerate(mnemonic): + # TODO: do this format assert + # if self.debug.model == "Safe 3": + # assert f"WORD {index + 1}" in self.debug.wait_layout().title() + # else: + # assert f"Type word {index + 1}" in self._text_content() self.debug.input(word) def input_all_slip39_shares( @@ -258,8 +260,9 @@ class EthereumFlow: def confirm_data(self, info: bool = False, cancel: bool = False) -> BRGeneratorType: yield - assert self.debug.wait_layout().title() == "CONFIRM DATA" - assert "Size:" in self.debug.wait_layout().text_content() + TR.assert_equals( + self.debug.wait_layout().title(), "ethereum.title_confirm_data" + ) if info: self.debug.press_info() elif cancel: @@ -269,7 +272,9 @@ class EthereumFlow: def paginate_data(self) -> BRGeneratorType: br = yield - assert self.debug.wait_layout().title() == "CONFIRM DATA" + TR.assert_equals( + self.debug.wait_layout().title(), "ethereum.title_confirm_data" + ) assert br.pages is not None for i in range(br.pages): self.debug.wait_layout() @@ -279,7 +284,9 @@ class EthereumFlow: def paginate_data_go_back(self) -> BRGeneratorType: br = yield - assert self.debug.wait_layout().title() == "CONFIRM DATA" + TR.assert_equals( + self.debug.wait_layout().title(), "ethereum.title_confirm_data" + ) assert br.pages is not None assert br.pages > 2 if self.debug.model == "T": @@ -300,7 +307,7 @@ class EthereumFlow: go_back_from_summary: bool = False, ) -> BRGeneratorType: yield - assert self.debug.wait_layout().title() == "RECIPIENT" + TR.assert_equals(self.debug.wait_layout().title(), "send.title_recipient") if self.debug.model == "T": if cancel: @@ -308,8 +315,12 @@ class EthereumFlow: else: self.debug.press_yes() yield - assert self.debug.wait_layout().title() == "SUMMARY" - assert "Maximum fee:" in self.debug.wait_layout().text_content() + TR.assert_equals( + self.debug.wait_layout().title(), "words.title_summary" + ) + TR.assert_in( + self.debug.wait_layout().text_content(), "send.maximum_fee" + ) if go_back_from_summary: self.debug.press_no() yield @@ -317,8 +328,12 @@ class EthereumFlow: yield if info: self.debug.press_info(wait=True) - assert "Gas limit:" in self.debug.wait_layout().text_content() - assert "Gas price:" in self.debug.wait_layout().text_content() + TR.assert_in( + self.debug.wait_layout().text_content(), "ethereum.gas_limit" + ) + TR.assert_in( + self.debug.wait_layout().text_content(), "ethereum.gas_price" + ) self.debug.press_no(wait=True) self.debug.press_yes() else: @@ -327,7 +342,9 @@ class EthereumFlow: else: self.debug.press_right() yield - assert "Maximum fee:" in self.debug.wait_layout().text_content() + TR.assert_in( + self.debug.wait_layout().text_content(), "send.maximum_fee" + ) if go_back_from_summary: self.debug.press_left() yield @@ -335,9 +352,13 @@ class EthereumFlow: yield if info: self.debug.press_right(wait=True) - assert "Gas limit:" in self.debug.wait_layout().text_content() + TR.assert_in( + self.debug.wait_layout().text_content(), "ethereum.gas_limit" + ) self.debug.press_right(wait=True) - assert "Gas price:" in self.debug.wait_layout().text_content() + TR.assert_in( + self.debug.wait_layout().text_content(), "ethereum.gas_price" + ) self.debug.press_left(wait=True) self.debug.press_left(wait=True) self.debug.press_middle() diff --git a/tests/translations.py b/tests/translations.py new file mode 100644 index 000000000..8bf51f865 --- /dev/null +++ b/tests/translations.py @@ -0,0 +1,73 @@ +import json +from pathlib import Path +from typing import Any, Iterable + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parent + +TRANSLATIONS = ROOT / "core" / "embed" / "rust" / "src" / "ui" / "translations" + +LANGUAGES = ["cs", "en", "fr"] + + +def _get_all_language_data() -> list[dict[str, dict[str, str]]]: + return [_get_language_data(language) for language in LANGUAGES] + + +def _get_language_data(language: str) -> dict[str, dict[str, str]]: + file = TRANSLATIONS / f"{language}.json" + return json.loads(file.read_text())["translations"] + + +all_language_data = _get_all_language_data() + + +def _resolve_path_to_texts(path: str, template: Iterable[Any] = ()) -> list[str]: + texts: list[str] = [] + lookups = path.split(".") + for language_data in all_language_data: + data: dict[str, Any] | str = language_data + for lookup in lookups: + assert isinstance(data, dict), f"{lookup} is not a dict" + data = data[lookup] + assert isinstance(data, str), f"{path} is not a string" + if template: + data = data.format(*template) + texts.append(data) + return texts + + +def assert_equals(text: str, path: str, template: Iterable[Any] = ()) -> None: + # TODO: we can directly pass in the current device language + texts = _resolve_path_to_texts(path, template) + assert text in texts, f"{text} not found in {texts}" + + +def assert_in(text: str, path: str, template: Iterable[Any] = ()) -> None: + texts = _resolve_path_to_texts(path, template) + for t in texts: + if t in text: + return + assert False, f"{text} not found in {texts}" + + +def assert_startswith(text: str, path: str, template: Iterable[Any] = ()) -> None: + texts = _resolve_path_to_texts(path, template) + for t in texts: + if text.startswith(t): + return + assert False, f"{text} not found in {texts}" + + +def assert_template(text: str, template_path: str) -> None: + templates = _resolve_path_to_texts(template_path) + for t in templates: + # Checking at least the first part + first_part = t.split("{")[0] + if text.startswith(first_part): + return + assert False, f"{text} not found in {templates}" + + +def translate(path: str, template: Iterable[Any] = ()) -> list[str]: + return _resolve_path_to_texts(path, template) diff --git a/tests/ui_tests/common.py b/tests/ui_tests/common.py index f71dfc0dd..1482c22ee 100644 --- a/tests/ui_tests/common.py +++ b/tests/ui_tests/common.py @@ -36,6 +36,11 @@ FixturesType = t.NewType("FixturesType", "dict[str, dict[str, dict[str, str]]]") FIXTURES: FixturesType = FixturesType({}) +ENGLISH_LANGUAGE_TREZOR = "en-US" +ENGLISH_LANGUAGE = "en" +FOREIGN_LANGUAGES = ["cs", "fr"] +SUPPORTED_LANGUAGES = FOREIGN_LANGUAGES + [ENGLISH_LANGUAGE] + def get_current_fixtures() -> FixturesType: global FIXTURES @@ -51,9 +56,9 @@ def prepare_fixtures( ) -> tuple[FixturesType, set[TestCase]]: """Prepare contents of fixtures.json""" # set up brand new contents - grouped_tests: dict[tuple[str, str], dict[str, str]] = {} + grouped_tests: dict[tuple[str, str, str], dict[str, str]] = {} for result in results: - idx = result.test.model, result.test.group + idx = result.test.model, result.test.group, result.test.language group = grouped_tests.setdefault(idx, {}) group[result.test.fixtures_name] = result.actual_hash @@ -61,16 +66,23 @@ def prepare_fixtures( # merge with previous fixtures fixtures = deepcopy(get_current_fixtures()) - for (model, group), new_content in grouped_tests.items(): + for (model, group, language), new_content in grouped_tests.items(): # for every model/group, update the data with the new content current_content = fixtures.setdefault(model, {}).setdefault(group, {}) if remove_missing: + # Need to preserve all the languages except the current one + diff_languages: dict[str, str] = {} + for key in list(current_content.keys()): + if TestCase.get_language_from_fixture_name(key) != language: + diff_languages[key] = current_content.pop(key) + new_tests = set(new_content.keys()) old_tests = set(current_content.keys()) missing_tests |= { - TestCase(model, group, test) for test in old_tests - new_tests + TestCase(model, group, test, language) for test in old_tests - new_tests } current_content.clear() + current_content.update(diff_languages) current_content.update(new_content) @@ -220,6 +232,7 @@ class TestCase: model: str group: str name: str + language: str @classmethod def build(cls, client: Client, request: pytest.FixtureRequest) -> Self: @@ -228,19 +241,45 @@ class TestCase: if model == "Safe 3": model = "R" name, group = _get_test_name_and_group(request.node.nodeid) + language = client.features.language or "" + if language == ENGLISH_LANGUAGE_TREZOR: + language = ENGLISH_LANGUAGE + assert language in SUPPORTED_LANGUAGES return cls( model=f"T{model}", name=name, group=group, + language=language, ) + @staticmethod + def get_language_from_fixture_name(fixture_name: str) -> str: + lang = fixture_name.split("_")[1] + if lang in FOREIGN_LANGUAGES: + return lang + # English (currently) is implicit there + return ENGLISH_LANGUAGE + + def is_english(self) -> bool: + return self.language == ENGLISH_LANGUAGE + @property def id(self) -> str: - return f"{self.model}-{self.group}-{self.name}" + if self.is_english(): + return f"{self.model}-{self.group}-{self.name}" + else: + return f"{self.model}_{self.language}-{self.group}-{self.name}" @property def fixtures_name(self) -> str: - return f"{self.model}_{self.name}" + # Not changing the fixture name for english to be compatible + # with previous test results. + # TODO: maybe change this after we merge it to master + # (when we verify that the english UI diff is OK) + if self.is_english(): + return f"{self.model}_{self.name}" + else: + return f"{self.model}_{self.language}_{self.name}" @property def dir(self) -> Path: @@ -312,6 +351,7 @@ class TestResult: model=metadata["test"]["model"], group=metadata["test"]["group"], name=metadata["test"]["name"], + language=metadata["test"]["language"], ) return cls( test=test, diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index e046b7ddb..adf56412f 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -737,10 +737,92 @@ }, "TR": { "click_tests": { +"TR_cs_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "ba7324f4a6c2fa5c80fb2883f1d898f73881ac596bab355b62e221c242b0ef0b", +"TR_cs_test_autolock.py::test_autolock_does_not_interrupt_signing": "00e06cf2d9d7b3cd77e9f430b80e52bb29648371bbd2c7d14c8fde8be6a70842", +"TR_cs_test_autolock.py::test_autolock_interrupts_passphrase": "e76213cc955626f3ab04c7651c03def3801eab2e39679331635ffb8a8fdd1485", +"TR_cs_test_autolock.py::test_autolock_interrupts_signing": "e52d91e8846c767a7a2b13a0ce2138dad6096d82a509ba8726a8d2cb0ee2ac5d", +"TR_cs_test_autolock.py::test_autolock_passphrase_keyboard": "82624482e3f3f3ef081fc6ab81024897b4ce9374425723abcce0f988c0c0efeb", +"TR_cs_test_autolock.py::test_dryrun_enter_word_slowly": "cc2a0a8622e447582f93396d088f6c804c0194b20e4ac80b364c556d5790688e", +"TR_cs_test_autolock.py::test_dryrun_locks_at_number_of_words": "c1251942fcc331669961dcd219a73f9132ea2b49de8253ac31378af12838443d", +"TR_cs_test_autolock.py::test_dryrun_locks_at_word_entry": "714f89b1697b73eabc3296fac5e0eaea6f40148661d317dc5bf0a638bfe6f6bd", +"TR_cs_test_lock.py::test_hold_to_lock": "9a9948b85cca946c349074c888bd8f7ef487d408ceea656d09b053528a980187", +"TR_cs_test_passphrase_tr.py::test_cancel": "060d834bb26b94d8f09315efbbc9e02c43543b4248c8950c4207c975b64b0f19", +"TR_cs_test_passphrase_tr.py::test_passphrase_delete": "8f1e7e4bb8817f1fda6d9173b9e67ad556b1751e238f66653004860a33c0e84f", +"TR_cs_test_passphrase_tr.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-381132c0": "a0f3e9b9ea878d3200285aeb0e7dd23a035c220f54465a099f0f838d963874c5", +"TR_cs_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-166c898a": "fd2e3a0a658b652e3594ec987c176c9bfbdacf4c373549b59f64dcb367f6c95d", +"TR_cs_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-334262e9": "97dd7ac6f4ba4a52a4bec95e1f6d61123309096ba0f535e2b87609dae85722f9", +"TR_cs_test_passphrase_tr.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "860f9af92c697a7d1d50e43f251eff951510a8832fdcd025a9b761e48fadd3c2", +"TR_cs_test_passphrase_tr.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "c944521a0d798023f953224c72d38a5702e60601ad221951cad872990feedb63", +"TR_cs_test_passphrase_tr.py::test_passphrase_input_over_50_chars": "460efd4bafc7b378c7e37aaa632deb759182a3da3bf9506cbeadf61a942385bd", +"TR_cs_test_passphrase_tr.py::test_passphrase_loop_all_characters": "9b5c985c5c4ebd340d16784eee77a6d7f60faa1dc86d1532222e892d0d112b99", +"TR_cs_test_pin.py::test_pin_change": "0542e29ed7eb32ddd68a381c223d9caa061fbbc524c138d2606186a7df40ddfa", +"TR_cs_test_pin.py::test_pin_delete_hold": "823fd5cde4840b77063d96cae6e83cafd610f1fdf32f417b947902a7a9f37eb8", +"TR_cs_test_pin.py::test_pin_empty_cannot_send": "68b9e9e43264cabfac3a114f960e5df5ead0b56b2233b693c88dfefab652cc11", +"TR_cs_test_pin.py::test_pin_incorrect": "586307a976f1c63958d67bb1f394f755afd2b9a4b39eb31afcee406897a64efd", +"TR_cs_test_pin.py::test_pin_long": "1f0d716b3e8f57b2f6e310a8dc224f61b1b0ad6a97176f36bf6ccaa1d405ff19", +"TR_cs_test_pin.py::test_pin_long_delete": "4997a2959f793e7d7e203d2e41dbb5bf1175d6f3b90a7393bc495fa9a5574a67", +"TR_cs_test_pin.py::test_pin_longer_than_max": "bfd162a64259dfd930f4115fa361fe46973c69d1280be74c07e0007b15094dd4", +"TR_cs_test_pin.py::test_pin_same_as_wipe_code": "52759d5ee5002cf0668664207b645785f807d543f9bab4a97597b334a18e9d31", +"TR_cs_test_pin.py::test_pin_setup": "cc6920332700177760a0e296353c4635a0c5e18f93b5cd184a1bb1939b1166b8", +"TR_cs_test_pin.py::test_pin_setup_mismatch": "4a61f1b882e91079c6efe2ce636e04dd93bb9befcae00d46b231d633da7201bc", +"TR_cs_test_pin.py::test_pin_short": "69eb1d0d6834ba37487cf5152013caa1c0bb63dd38bd5e32dd135121037d5aff", +"TR_cs_test_pin.py::test_wipe_code_same_as_pin": "137980abf51cd4be113fe4547bdba2d9ca37ed87ddc01fc3a9df276930972482", +"TR_cs_test_pin.py::test_wipe_code_setup": "ccf72b989f9ffed19357bdd5b2e93da42aad335f333488bfdeb32881d00bec26", +"TR_cs_test_recovery.py::test_recovery_bip39": "d33ecfb915ae70ea4dc259ee88d91c640ef10b1b7c832cdaa10bbaf0e01500f8", +"TR_cs_test_recovery.py::test_recovery_slip39_basic": "35705b48b24c9ce55635d5a50c37ebc6b27ac017f89c47e900341da825af9920", +"TR_cs_test_reset_bip39.py::test_reset_bip39": "b391e73dd5a062a1f6bf8b1d16b55908848a163b680e6ef8e1739a6d2d3821fe", +"TR_cs_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "1c646ee1e96bc9dbcaa7c2eace8dd8a9b7ee1f913f36d0e93390f01f6eac802d", +"TR_cs_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "993dd8887c429bdc4473413091cc97fc8fada8eedb5e1c901b8ffeabd23e6722", +"TR_cs_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "7d43e38ddeaf7f138826658a78664faa988ff8dd0333546a4918eed87759a9f3", +"TR_cs_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "c8d69c7e3bb0b26074207e1ac5d1ecbb6d73c63f2ddec9937ad81fc5ba9e11be", +"TR_cs_test_tutorial.py::test_tutorial_again_and_skip": "292dacb8166a2ea4c0a7c0428fc6818f3f2354d6d7607ff0760dad416186c903", +"TR_cs_test_tutorial.py::test_tutorial_finish": "8827cf41c37b61e076dfcf5aefadcc2d5c156762826578e379f079ab8e1e27ae", +"TR_cs_test_tutorial.py::test_tutorial_skip": "6252f47deb0912c8e5b2be1e63a9fabd8e60c3d1c23fe641e75c08a82f8ded26", +"TR_fr_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "9feaa1606edf1b097def7f4407ec83568a85f747b83de454a06ec719409d0187", +"TR_fr_test_autolock.py::test_autolock_does_not_interrupt_signing": "3f12d4acf2fe78643c49f4852d163ae91e9c07c08b47c660f1e49e19abf74f89", +"TR_fr_test_autolock.py::test_autolock_interrupts_passphrase": "aa94ed767fea90a98b2a30e4b5fe4dd618670bcb2064d9a872d49a290ec0ae5f", +"TR_fr_test_autolock.py::test_autolock_interrupts_signing": "72d2ab8e2a48f43c8532277603dcea63d81f4fb0df0d706e0435fbb0e6416c84", +"TR_fr_test_autolock.py::test_autolock_passphrase_keyboard": "ef7e91220031c119260b97dba5496e4da0b6d4f308ddecb448ac0ae3971a31a2", +"TR_fr_test_autolock.py::test_dryrun_enter_word_slowly": "e91f4ea80787d850d44a8196d986dd7a7d5ccf86652be11d14e384d3917cd628", +"TR_fr_test_autolock.py::test_dryrun_locks_at_number_of_words": "a2c6853096da3e1e5c5ba9ca33daaab8893098b11ba04b0dfd518a3aa016416a", +"TR_fr_test_autolock.py::test_dryrun_locks_at_word_entry": "e4d7c58afd99cab73f97d867b94957ee0a41266246a5db6d64c928ec8052fb7f", +"TR_fr_test_lock.py::test_hold_to_lock": "fdb2da5c62a416577892fae9e7a2c0f65664b34d1e9d62d7532099d0c6319737", +"TR_fr_test_passphrase_tr.py::test_cancel": "57b9d3719c212d8a2579e0099672b98220eb6f86748cd74f0e2b7b74a08b27d7", +"TR_fr_test_passphrase_tr.py::test_passphrase_delete": "fdceac823e22350cea1768944526eeea3638f5c7df0342f5154d27703561119b", +"TR_fr_test_passphrase_tr.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-381132c0": "ab3db7ff302d95e316ac9d9e837afb83ad6a05b56a882acc85b86bf87cf0a2df", +"TR_fr_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-166c898a": "c3dcbc12e992da02f7a7ee1c7834d5275f7b60da15b887fea84a091ccfc4395a", +"TR_fr_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-334262e9": "22fea2f404fe862524bd7221939f59054637c5d9175c32e228a3c7407b1a0e93", +"TR_fr_test_passphrase_tr.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "b8611858a7ffbc01fe30636a6c0a992fcf079e34f62b2c839bc4889c4a3c872b", +"TR_fr_test_passphrase_tr.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "2cc15a3dd7ffd6681fe7c8d227615f4362fa7f1ea258a04b2a06fe45082334a3", +"TR_fr_test_passphrase_tr.py::test_passphrase_input_over_50_chars": "5ba89589f43330c519770053a93b78de03bc7334b1345100cb33f4fb533eb419", +"TR_fr_test_passphrase_tr.py::test_passphrase_loop_all_characters": "329ed9b6ed9d8d672c10340de2123ea33d74594be5e6507329ed13dfab39d9b3", +"TR_fr_test_pin.py::test_pin_change": "685bb7960daef82ee09e54e3aeb08bcae1af7ec3d3cdf4b403ad6de9540da996", +"TR_fr_test_pin.py::test_pin_delete_hold": "95ce34cd235bc3d94a5b174e19c63f3c74ece53dbb30eb32f2835ec7fbfa2678", +"TR_fr_test_pin.py::test_pin_empty_cannot_send": "32086ba0b1320d09df3d5a389abe8b821158ab2513898127d5c688d0889943ce", +"TR_fr_test_pin.py::test_pin_incorrect": "c4bee4fcafbabdeeeb00b89b22e7fb5bd45a564ef226f182defafdea630579ad", +"TR_fr_test_pin.py::test_pin_long": "a1ca2ed2c1fe96aa8d90006ed21a9c6a80922d23a63e11f0e75fbb2a934921ab", +"TR_fr_test_pin.py::test_pin_long_delete": "d3d2e8f1f6c34e0ca70310e18e12493cfa5f485ffcb5365921dd1f8e2ff4f4b6", +"TR_fr_test_pin.py::test_pin_longer_than_max": "703e6647d58f8827fc9683d2fb6205e1c31310d610418944aadb6b4395abf646", +"TR_fr_test_pin.py::test_pin_same_as_wipe_code": "0ebe0be37bbfe1e3fb8e3e17dc71ce474266e63b563b0e43b2f3b1474460036e", +"TR_fr_test_pin.py::test_pin_setup": "1fca5a0b923e1bdd37344095c386b78def4ddefcd47145db4f1377fc33009b46", +"TR_fr_test_pin.py::test_pin_setup_mismatch": "d8cde9eb0f1549fedbb5ae47811550dbdcdee9c204b15de404676ee10a9edc78", +"TR_fr_test_pin.py::test_pin_short": "bb71dd2573f7b8ee77c46cd4a70c4383bb5a5d49c24ca8e71a0236e560ca4be4", +"TR_fr_test_pin.py::test_wipe_code_same_as_pin": "c8c518b1ec5f71ca4ac5b35ed657309bd80a5de26f5ea6e8c90cc464fe612f29", +"TR_fr_test_pin.py::test_wipe_code_setup": "e8c7edb9910918cbe73fbdc2b22b1b7b51089c21e213738bb762eb8f0e5fe0ea", +"TR_fr_test_recovery.py::test_recovery_bip39": "b00b902ccd659579e134318bb435a817cfc30f821dcdc8b1c1db6da7b1d5866c", +"TR_fr_test_recovery.py::test_recovery_slip39_basic": "fc033e03a753c1477deb947791afa2ea9df7d362e2e07d50d29e102dfef7ab7f", +"TR_fr_test_reset_bip39.py::test_reset_bip39": "d62268b054dc39c2628df908d9fbc6cb11ae893c9a7e7d01aa174eee763cf396", +"TR_fr_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "24019a25a6ea64b9d7944f2d64fa49faa33ab70283a6a7c3f423e53f54e5d0f0", +"TR_fr_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "ef61e8c4a57562e0aba78b781285345e094da97d31cdf0a5284e4097fa2a9d8d", +"TR_fr_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "5372205adce32e1b3fe9d639cb1ef934d6b874970989607a5048ff21ace410a7", +"TR_fr_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "6742faab6868aa1b4055825246b576955dd7237161e1eb1d5c4efcefddc20eb1", +"TR_fr_test_tutorial.py::test_tutorial_again_and_skip": "2769e1b67b9cfd9eb8e106cc78102d9d1c3035d954d772fc667e51262f19ea84", +"TR_fr_test_tutorial.py::test_tutorial_finish": "6b4a758c23df5c645a7fdb9628a8c88c67fd3d000996d6fdddf71fe0eb191b99", +"TR_fr_test_tutorial.py::test_tutorial_skip": "cef17ee9a91269b5f76044ef00b9ba38baa9a55182b9d9d57782eb81fc79f010", "TR_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "db31938e0b4969204c2411069f00ed8a314b31e0dec251cce371adb0bec1c524", -"TR_test_autolock.py::test_autolock_does_not_interrupt_signing": "21d405bf393cdb8e25817a8c9b893bd5a4d5c05fa9e1d33e98e6ba512c8c4496", +"TR_test_autolock.py::test_autolock_does_not_interrupt_signing": "660b490363bb7fc810c6f0b9da5431d0864a7c56b2e610f4b2a074be52f2fd0a", "TR_test_autolock.py::test_autolock_interrupts_passphrase": "82f963d13f42b754f8749c2587466e8a1ca3843581ccda63fb846e1311ce6f7f", -"TR_test_autolock.py::test_autolock_interrupts_signing": "0ede63ae0d813a3d27fef17f3385c96111415932e3e525c7f421e7054e2c9887", +"TR_test_autolock.py::test_autolock_interrupts_signing": "ae0a2fbbdd3fedbf1aa23254bb4ca490529715f6b0bbcb6bb3bf197e3a7a30eb", "TR_test_autolock.py::test_autolock_passphrase_keyboard": "44274f7e1e714fde732df8c48ffb3617d1b4ecf50d32256ce6f167f40fbd5e6d", "TR_test_autolock.py::test_dryrun_enter_word_slowly": "69c6f17ea55eb9428ea6daf64409f23702952b0dccd5f0640c10b1f4c62287eb", "TR_test_autolock.py::test_dryrun_locks_at_number_of_words": "df7a17ab9cd2c617ce22a615e1da9bedee41f978459cbe103f2eecb8dfe8db12", @@ -766,7 +848,7 @@ "TR_test_pin.py::test_pin_setup": "040256a62fb20f5284bbec0ababab6d94f6fbed1b9b9a0cebe58a73ccae7ff2d", "TR_test_pin.py::test_pin_setup_mismatch": "4cafbb73ee107509f8766d73d2389ef246d27ade3540b835cdfaf83d2cb4a416", "TR_test_pin.py::test_pin_short": "a87619c455ec81763cbb133279f9e7fbb3b9a5a02c58e1da63640d51a4e107c3", -"TR_test_pin.py::test_wipe_code_same_as_pin": "f47457df24228743f3d7f3cc9e60c209b2c628c35d00bb1f965dda2692b4a632", +"TR_test_pin.py::test_wipe_code_same_as_pin": "329962b3b949dc1429d361e4ae6c5224b4a75c981ae833fd3bf1618df44fbd2a", "TR_test_pin.py::test_wipe_code_setup": "4afe5ceb8f953892eea8b6b7c6997a1b5afa40b81372265a84a567b61773d89b", "TR_test_recovery.py::test_recovery_bip39": "43a56fb1dea38503cfddf55c4a6e8d5a5188be7354bd5c2b31fde197f2af3443", "TR_test_recovery.py::test_recovery_slip39_basic": "40df8fd6302577ff4f1d1d6d214d1ffd22791912ee4225ae86460a3fa3f3ac0f", @@ -798,17 +880,17 @@ "TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx[False]": "bf962269c96493db17d34978f049d3dba44ca7a311fbf53822046f7f1e3ec4df", "TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx[True]": "bf962269c96493db17d34978f049d3dba44ca7a311fbf53822046f7f1e3ec4df", "TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx_large": "4f275de439c812363140d3839ebddd9243e2bb34d80d02a487361148b2bbab71", -"TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx_migration": "4cf48d6bb48a9efbff9e2949d657fde4dea7ae9e92f47cafdfcd11d7765d76b8", -"TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx_spend": "db453154c6d8318befea7230eb2a9639fece5bdfd83c62fbb7a1e9195b77ac1b", +"TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx_migration": "acb754e2a25298afd3ab3b1d7a10975f5dbca1ab9ba4ecaa3905029b3419547b", +"TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx_spend": "ad55d3695ad9b53c60dfb1c6cd87a1431a46b839f59bf027544dcdb548fdbe1a", "TR_bitcoin-test_authorize_coinjoin.py::test_wrong_account_type": "9321620fe3fa3eaf6c83d313cc3c40b4b59b454ad24d6954440d3bb9285b9b5c", "TR_bitcoin-test_authorize_coinjoin.py::test_wrong_coordinator": "9321620fe3fa3eaf6c83d313cc3c40b4b59b454ad24d6954440d3bb9285b9b5c", -"TR_bitcoin-test_bcash.py::test_attack_change_input": "3e07e565d7626007cfd2a3be476ffd6efaabc6b835bee91f0decb62b838a9b39", -"TR_bitcoin-test_bcash.py::test_send_bch_change": "5bfd23d4e276c67071b8e2829c9445491053309c04b58256f8d4f789766683a2", -"TR_bitcoin-test_bcash.py::test_send_bch_external_presigned": "d71ca13edae80607527a670d8ea4f34b0d119a1a96de74e01e8ddaa0973603c9", -"TR_bitcoin-test_bcash.py::test_send_bch_multisig_change": "f5a62283c4254cb10a7180a888957daf90e57bbeffff66704eee192c4469b044", -"TR_bitcoin-test_bcash.py::test_send_bch_multisig_wrongchange": "fa268b2481cce18a041c8f8bf773f16cbfed6ad372a89c7d7220a2de3aa9042e", -"TR_bitcoin-test_bcash.py::test_send_bch_nochange": "395635f081e0c9bdf3f6de9a1540d9211c3a1d68665d8ac13ffb8c236add0a3b", -"TR_bitcoin-test_bcash.py::test_send_bch_oldaddr": "3957efb1b584f974d8b32523eadb4c18596956b38dec2c85005e7e4f0bf85593", +"TR_bitcoin-test_bcash.py::test_attack_change_input": "88d086a6815ac5e8565a3b1b205a215e76e6b68171471e49213752941231010b", +"TR_bitcoin-test_bcash.py::test_send_bch_change": "78691133efc09009b225fb2c301b3049542b07350c7e0ef136f66588fb3dfd62", +"TR_bitcoin-test_bcash.py::test_send_bch_external_presigned": "98ec2c43611f0be4fe466fbb4b3188df3eae4e53156ff3cf93758a23c9069bd2", +"TR_bitcoin-test_bcash.py::test_send_bch_multisig_change": "2f121ac3cc80c105425fdcf8cbaaa6b18f99ab392eec4ac83989736ce4221af7", +"TR_bitcoin-test_bcash.py::test_send_bch_multisig_wrongchange": "c0ccd14f4c68a47f1398121c0f73282d93a71a64095c85964c9db0401925316c", +"TR_bitcoin-test_bcash.py::test_send_bch_nochange": "043e28fddd93043bb5d627a0fa9a044b6f4e2a9106d9495d0cea019c4b7cade2", +"TR_bitcoin-test_bcash.py::test_send_bch_oldaddr": "65d31e564ecfc89a29566fb58edbe268566538d8b6b51be3e6b875fd8c4e1354", "TR_bitcoin-test_descriptors.py::test_descriptors[Bitcoin-0-10025-InputScriptType.SPENDTAPROOT--301d7568": "7fb7f797b6fb89460beab4df5fbd5f61cbdcd2931349b8c2bae696bf45195b7c", "TR_bitcoin-test_descriptors.py::test_descriptors[Bitcoin-0-44-InputScriptType.SPENDADDRESS-pkh-a1b0211f": "ad9329ebacf27f00e736d1d3d8a33a539512a23fd134806ddfe6da4e4b517d1a", "TR_bitcoin-test_descriptors.py::test_descriptors[Bitcoin-0-49-InputScriptType.SPENDP2SHWITNESS-75f8d49f": "cb4cea77c61cafeece033128c8c050060cba7c64e4ef255dc59b235324bcf213", @@ -827,8 +909,8 @@ "TR_bitcoin-test_descriptors.py::test_descriptors[Testnet-1-49-InputScriptType.SPENDP2SHWITNESS-6a0c7b09": "6b7c12363fc566de9d35013be5156c970e9fba33c2ed73b4a7e9b06e0974e867", "TR_bitcoin-test_descriptors.py::test_descriptors[Testnet-1-84-InputScriptType.SPENDWITNESS-wpk-7c651f2d": "7714578913a2a1e573193ac78cddc20c2df6c2856ad8c50d447906bfee41e4f6", "TR_bitcoin-test_descriptors.py::test_descriptors[Testnet-1-86-InputScriptType.SPENDTAPROOT-tr(-b37d77de": "094264a5b04a8042806562f315c71c9edecb2018af6db957a001fcdc31b0296d", -"TR_bitcoin-test_firo.py::test_spend_lelantus": "aff133d149549783adbdaa9563cb990af6cc5f8fafba3f50d82bbbd2765bb4ff", -"TR_bitcoin-test_fujicoin.py::test_send_p2tr": "6ab906b521bb73e0725c3d0f3eb5a6ecf9f582853f4cd71672f73190f15fb917", +"TR_bitcoin-test_firo.py::test_spend_lelantus": "da0fef24c0a4534fe31fbe0dd16eb1be751552c27376a2c7335578d0ae3224bd", +"TR_bitcoin-test_fujicoin.py::test_send_p2tr": "6924d57797f74e8aead107dcee07143cee61755ec3afbba95b78df237496f67e", "TR_bitcoin-test_getaddress.py::test_address_mac": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_bitcoin-test_getaddress.py::test_altcoin_address_mac": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_bitcoin-test_getaddress.py::test_bch": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", @@ -956,28 +1038,28 @@ "TR_bitcoin-test_getpublickey_curve.py::test_publickey_curve[nist256p1-path3-03b93f7e6c777143ad-2d6b178b": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_bitcoin-test_getpublickey_curve.py::test_publickey_curve[secp256k1-path0-02f65ce170451f66f4-9c982c22": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_bitcoin-test_getpublickey_curve.py::test_publickey_curve[secp256k1-path1-0212f4629f4f224db0-0209bb73": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_bitcoin-test_grs.py::test_legacy": "8851aab5526e4f26857d9f5c38253c3049dafc709c90663239fe035ad623caca", -"TR_bitcoin-test_grs.py::test_legacy_change": "c24250da19b0bd73d13211b264f7efd11c4189816257964a38abc92bfee9adfa", -"TR_bitcoin-test_grs.py::test_send_p2tr": "0c91f2782119205af71bf270f3867080ed5656eb7d60805c06106ebb1dd5e4c2", -"TR_bitcoin-test_grs.py::test_send_segwit_native": "8d1cdf1394656f316a87e2f490b228035de71427e38196af7fea6c1da360f9ec", -"TR_bitcoin-test_grs.py::test_send_segwit_native_change": "3516574f2630080af30474774ba4d43151b4e144e6e3af146c27d7c5dc038b89", -"TR_bitcoin-test_grs.py::test_send_segwit_p2sh": "644dcabb2d8cf624a031a2853f55af687b627d93b3fde6cc4b860f3dc3156138", -"TR_bitcoin-test_grs.py::test_send_segwit_p2sh_change": "de03cf73f4249bd7b955e5a1d363d297f2fed470d8773d14ed46fc0f48a784fb", -"TR_bitcoin-test_komodo.py::test_one_one_fee_sapling": "76984380f30ebd7f5f04cc5cb6581887c89713d0f79ac230e8cffaba2ddbb9bc", -"TR_bitcoin-test_komodo.py::test_one_one_rewards_claim": "12a3b1ad73fce377a5c90138eb1cf767a02de8a2d55db75e0a69becc5fdf7ae2", -"TR_bitcoin-test_multisig.py::test_15_of_15": "d2362894ba0b1365f4b50c9fa5adf6242a9f71312e5a51295ecfcbda25bad273", -"TR_bitcoin-test_multisig.py::test_2_of_3[False]": "43ef2841c1847f9eb425e3ba1cdba4a8880363889347c5fa9ba70e7654d181f0", -"TR_bitcoin-test_multisig.py::test_2_of_3[True]": "23960e739861c482ed71941947f8709425d20fc6dd98cd15abb9eecd56b53676", -"TR_bitcoin-test_multisig.py::test_attack_change_input": "4a75904f94e42e74c7c7129cdb090a0bde4506c91116dc20535ef60dfe069cfc", -"TR_bitcoin-test_multisig.py::test_missing_pubkey": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", -"TR_bitcoin-test_multisig_change.py::test_external_external": "08a4cb41e2b55807b6d48741cb70f738ff1ce1d3d55d693cd3fccfad33ac2988", -"TR_bitcoin-test_multisig_change.py::test_external_internal": "83a561913853d3cf70cc2088e32b086453a009dde887887762c3076c127d2f9c", -"TR_bitcoin-test_multisig_change.py::test_internal_external": "1e6eaebc9072f131bca04c9d004b1d3f5993525235594bb8830954e5c8fa33d9", -"TR_bitcoin-test_multisig_change.py::test_multisig_change_match_first": "22e12387b3d05855dc757e3749e02604ff4cfd055c7385db73e2f968322a08e9", -"TR_bitcoin-test_multisig_change.py::test_multisig_change_match_second": "f3ce5beacfa792f601afc89f2b0551d36e3dce4794227a8f44dd77c674c75db8", -"TR_bitcoin-test_multisig_change.py::test_multisig_external_external": "d65c1194102f2cb8d5473a4681502e0f04826c11cd2e0e2deb9bcd694b50e757", -"TR_bitcoin-test_multisig_change.py::test_multisig_mismatch_change": "2d655de9224ae23b3632c2d2d3a9f97477c8ebb5d0ff2e276678aae6bae4e9d3", -"TR_bitcoin-test_multisig_change.py::test_multisig_mismatch_inputs": "337fd5ab84f2240326c28a09f7663010180641a1c130c99966c8b7d76b5d8b56", +"TR_bitcoin-test_grs.py::test_legacy": "f3c40ae1f0506c61f9ec57a0e4ccab373fc0e89c5ee70e668750185816980718", +"TR_bitcoin-test_grs.py::test_legacy_change": "541991675037e797c3cf22cb1ad77ae2f4e5f0786d619a96263406df75811466", +"TR_bitcoin-test_grs.py::test_send_p2tr": "a8f6ad24b3997952ce28909c11f0483a150683b3fbbc837e3cd74a8fa008b9ec", +"TR_bitcoin-test_grs.py::test_send_segwit_native": "4918ff248e147a01ea5a745bcd19f36c5c0b5250cf89b419a1fb8332dc84d4d6", +"TR_bitcoin-test_grs.py::test_send_segwit_native_change": "08357fd4fb2a82e05b7b9e5c50a0980704d447b321386f302f334ff2e4f5b144", +"TR_bitcoin-test_grs.py::test_send_segwit_p2sh": "10052eada6d3e83aedc356a14f141913ea0faa5411d04a21aec7425e3ee4de34", +"TR_bitcoin-test_grs.py::test_send_segwit_p2sh_change": "53d4f40c08afecfc3d58394073fd77de26a6604d365eb473008185c9d0788fc7", +"TR_bitcoin-test_komodo.py::test_one_one_fee_sapling": "50ab043f319cfe8ffbae9f7a4414f15948083daa321ff04c58fbc3b3c20e289f", +"TR_bitcoin-test_komodo.py::test_one_one_rewards_claim": "dc2591e352fddca64ea5106b3cced78d64305d993b0f2801907da004da036baf", +"TR_bitcoin-test_multisig.py::test_15_of_15": "b6a03cfb6439f07883effa5040fed9c598d8c4b68015b1ef77c9e941103e27a5", +"TR_bitcoin-test_multisig.py::test_2_of_3[False]": "10964b92f18a1b1616aec3503f43ca40a518c2a24d91bad6abb0eeda9e6827c1", +"TR_bitcoin-test_multisig.py::test_2_of_3[True]": "77666a9a651316cefffc55c7957bb9eaf1e0ea64bc15d8ca5645f499b187d195", +"TR_bitcoin-test_multisig.py::test_attack_change_input": "182d091d7f355b4fceddd0ae0f074a311bb2ee88e13e4aa8b1dd4ae6a37ef39c", +"TR_bitcoin-test_multisig.py::test_missing_pubkey": "c679ae4ba5eccd8eeb4aab713265192f97433e7ec6f45506da49cfee4d3afc3f", +"TR_bitcoin-test_multisig_change.py::test_external_external": "5b287527345c690e9e1f98541ad35983a02e736cc4501f6fbae28f83466e1716", +"TR_bitcoin-test_multisig_change.py::test_external_internal": "3fb049367d6857931319b60b448279f54c631f94c2bde24b45cba1b58af831eb", +"TR_bitcoin-test_multisig_change.py::test_internal_external": "bde00e0958718dc68eca665e096c1348df07aabf3787772ce1aa1791d6b058f4", +"TR_bitcoin-test_multisig_change.py::test_multisig_change_match_first": "0566905b475fd4e3956b3f88e58301cce29f2aa85c13b5301f6a7b4224bd4d8c", +"TR_bitcoin-test_multisig_change.py::test_multisig_change_match_second": "593dcf277d763d6234453082b29bffa5b370be1ff79961f662f85b27cf1d04b4", +"TR_bitcoin-test_multisig_change.py::test_multisig_external_external": "0c6ab882d3fc82b7ec6676097e7ae0c67f329d065237356e27f0be6311079b1d", +"TR_bitcoin-test_multisig_change.py::test_multisig_mismatch_change": "1a776c717896f75ab817d936ade08ac47066b5fb25ed4ee8931d7a7b72310384", +"TR_bitcoin-test_multisig_change.py::test_multisig_mismatch_inputs": "d750f88ab090b38499440c523323c3961ce87a3404a77fbb1473272485645833", "TR_bitcoin-test_nonstandard_paths.py::test_getaddress[m-1195487518-6-255-script_types3-False]": "3f96244768b52da1808a6d47e4b4f5759ec59ec07041b9a7c37ee3d277382539", "TR_bitcoin-test_nonstandard_paths.py::test_getaddress[m-1195487518-6-255-script_types3-True]": "7bb78992b4d49c39e4f7379cc417a647096d5bd46ac1273ceeaae0c1de14d805", "TR_bitcoin-test_nonstandard_paths.py::test_getaddress[m-1195487518-script_types2-False]": "1cc94552fe8aca4039e349e720667c09e336ec03ee8d2c38c9ad0a4dd4aece55", @@ -1006,25 +1088,25 @@ "TR_bitcoin-test_nonstandard_paths.py::test_signmessage[m-3h-100h-4-255-script_types1]": "c95bb7567e849d6f7f3a2727aa96e66f881a042f15e1a012816735186042a2ff", "TR_bitcoin-test_nonstandard_paths.py::test_signmessage[m-4-255-script_types0]": "7d18bc469cc81017aa6ce0609afe49344158ec01734c5cc58638d8300589763f", "TR_bitcoin-test_nonstandard_paths.py::test_signmessage[m-49-0-63-0-255-script_types4]": "264b37c100c13dad663f56192c196189283cd55109eec26e60255b06d15bbab3", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-6-255-script_types3]": "6efc81302232a4d9b87ff3ae245596aaf0aeb97090d7f5844a96eb403a20de84", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-script_types2]": "07494195abc7cbc3a9c948c1f6d520ba43316282f8207d3729b602dda94003be", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-3h-100h-4-255-script_types1]": "f303bf1b6a12f2c5543e57515c0654f7fcc651c9fcfbf03e9d570df27c2162f1", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-4-255-script_types0]": "e6cf269cdab80d6cd18f93674884a99fbda1a76385f5e8e061690f73a9a5d40a", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-49-0-63-0-255-script_types4]": "0cf5d19f80b1d74f2cec901a1a095700a94cb53e6f887da0c8d51ca23e5b584c", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths0-address_index0]": "1f383ccf1c3df308b4e43957cf0b1d67ee7c4c719e4642245e39f17c7de1e296", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths1-address_index1]": "192d26d28907315a36327ac77548bd5cf957e563c2e1bf11e7424e9b2322adde", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths2-address_index2]": "af9cb6826569a182c1693d6d5ddce14039093c378ac2eea3efa6133cf65ed05e", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths3-address_index3]": "450d03c22c1252649093670e082d35167adb83bd67175d49240eee74a5f0170e", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths4-address_index4]": "f2b3b16cbdf88b96cec3e151c2a26dcbd3724e1d93d60220eb4a2d7e4c5ec8b1", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths5-address_index5]": "e885031efd52320a7df07721f8a1c7f2004272587da75a715c3d3b303596cbfe", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths6-address_index6]": "5684222d881d8562f8f80647411ad586a2dcf8a8714674c0a61b47ad24b6a437", -"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths7-address_index7]": "97ea07aa32f9fb33610d4cd5c00917a1bd93675e1d3a54670ce51d84ebed76f6", -"TR_bitcoin-test_op_return.py::test_nonzero_opreturn": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", -"TR_bitcoin-test_op_return.py::test_opreturn": "db53dd4ad174320b7c36cf58c294487db96a6a9c1608a41acd87424c1c11e30b", -"TR_bitcoin-test_op_return.py::test_opreturn_address": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", -"TR_bitcoin-test_peercoin.py::test_timestamp_included": "57bd71bfa6680712736b78aab1f3da95d1cfff2b369fb8b2d87024b43c392994", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-6-255-script_types3]": "cb1402066fe5108f39822ab65c6adad5e10fff0b529e03e6b6a888fb2b844e1e", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-script_types2]": "b5567f915c8282b05589fb4d9e488cb88fc0f7529d9a6f49c26e9fc910eb2196", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-3h-100h-4-255-script_types1]": "a9b18e85e984cc07c7139ec7ec6b91a01c03d6acc829acebfb5627e082606a6c", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-4-255-script_types0]": "f5b954cb729a665dc0d0557f4a8350aed0c0f8f121750eeb957befb66d6d3e96", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx[m-49-0-63-0-255-script_types4]": "b7742756d64921a2e25232e05de36c95dff1c1d25f9edbcf5effef723523a862", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths0-address_index0]": "b7c78731984574439bfae51a747a05ac53ce6b75984e7f53c21c1bf3b098fd70", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths1-address_index1]": "363509b8130da9a0642660b230e50e44364e71982d5c6312dba52a3661d1575e", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths2-address_index2]": "1b20bc747a2a1e3b1df877b70eab993513496e057001f0bd739db530681cd3ed", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths3-address_index3]": "c6746a2db19cd112d2bd740c3cee14f95586e12f784460c3c20e7330568a7fb7", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths4-address_index4]": "e2282c6608c1da47306ce53b9597e613a7f23f77bd2a5f5ed5b49ee203b3b6fe", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths5-address_index5]": "7902a1f444f10bda8878d169fbe2ab7746dbd3a70bc0c7bf5648374f51ff5b2c", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths6-address_index6]": "716b9ada8513f0a1d56935b7354ab556fc17ebec2f8b252704340bfcc7c9df81", +"TR_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths7-address_index7]": "5a4846861d768f1a8bde0d0932e2d0003773624d06f0a286f1ddc6719f6f37fc", +"TR_bitcoin-test_op_return.py::test_nonzero_opreturn": "c679ae4ba5eccd8eeb4aab713265192f97433e7ec6f45506da49cfee4d3afc3f", +"TR_bitcoin-test_op_return.py::test_opreturn": "5d2a46ad2eae655de077a615be7dfaa39bca948e86750415b94ae5f72c542587", +"TR_bitcoin-test_op_return.py::test_opreturn_address": "c679ae4ba5eccd8eeb4aab713265192f97433e7ec6f45506da49cfee4d3afc3f", +"TR_bitcoin-test_peercoin.py::test_timestamp_included": "5d09f1d2a0150b3c7426a0fb472655a94623d2713732f096fb396a6f3ce70a29", "TR_bitcoin-test_peercoin.py::test_timestamp_missing": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_bitcoin-test_peercoin.py::test_timestamp_missing_prevtx": "c1ded2fec294b01d6b2f1925b9cebd1c64533009ce6c3f02125be2f8c1935bee", +"TR_bitcoin-test_peercoin.py::test_timestamp_missing_prevtx": "b92099a61c9e41c9803fbb4ffcf821810ef3367bcb455c3b53750f3b15b7d6fc", "TR_bitcoin-test_signmessage.py::test_signmessage[NFC message]": "ab6dacbf0439a55b40f5082da0036ab5b4cfeea7e4a214b99d6e449a287d0455", "TR_bitcoin-test_signmessage.py::test_signmessage[NFKD message]": "ab6dacbf0439a55b40f5082da0036ab5b4cfeea7e4a214b99d6e449a287d0455", "TR_bitcoin-test_signmessage.py::test_signmessage[bcash]": "4d6cd4001e91a85c9df6ab9d93068155e10085ca731f3f4eefbc282d0ff99ca2", @@ -1091,51 +1173,51 @@ "TR_bitcoin-test_signtx.py::test_signtx_forbidden_fields[expiry-9]": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_bitcoin-test_signtx.py::test_signtx_forbidden_fields[timestamp-42]": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_bitcoin-test_signtx.py::test_signtx_forbidden_fields[version_group_id-69]": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_bitcoin-test_signtx.py::test_spend_coinbase": "233e91efb19035433834af5f90bcff8688dd6bb829350e535f4af5eecd14baf3", -"TR_bitcoin-test_signtx.py::test_testnet_big_amount": "26696705a7d6faf8b1be9d66dbb31aa132a038365632af0a7c01684a1ef40cdb", -"TR_bitcoin-test_signtx.py::test_testnet_fee_high_warning": "90d7c8a448c8f5de9921eda5e27202d6f6498f53a8c0f407906199da610003c9", -"TR_bitcoin-test_signtx.py::test_testnet_one_two_fee": "c5c2c08d6f10ea75a800a243b7839fb6fd6f6bca5b727a2e8a8194935e08c2cf", -"TR_bitcoin-test_signtx.py::test_two_changes": "adb7a86357895a902688d9e65f470638d18b3663168841f42cad3bbed215bab3", -"TR_bitcoin-test_signtx.py::test_two_two": "ee8741bff119e6c1049eac69fec502d7434558bc28933275212c4b573d117de8", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[AmountUnit.BITCOIN]": "29b8a8baf48815ed37ab4f9d65a1585614f5d6c1b582d27c9575fc7215141b7e", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[AmountUnit.MICROBITCOIN]": "20490fea8715c06a0686aad914763bc8ec9ef6a5e86a01242cc109353ddb6d91", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[AmountUnit.MILLIBITCOIN]": "b9714014b218a5c37b2b716e23298351752a493798815afdd70718987ab9c57e", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[AmountUnit.SATOSHI]": "b9767163657841add95850f3831a337c709d40469cd615246d0fdb99711753c6", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[None]": "29b8a8baf48815ed37ab4f9d65a1585614f5d6c1b582d27c9575fc7215141b7e", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[AmountUnit.BITCOIN]": "b8a637d74434fa4460492aa8ffedde9e28f30a09967bd0a68bce035537264f13", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[AmountUnit.MICROBITCOIN]": "d2c55e76375386fc82f2647e7955eaffa3f82f824efb557b1351163766fbb0ac", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[AmountUnit.MILLIBITCOIN]": "7b1a114e28d69893ee3d29dfd7c43757521aabe9fc36cdd6af7ddcf4d46b02e6", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[AmountUnit.SATOSHI]": "f3f31f1c58aeb37221a0d29125507b685f4e2e9c6c30555f01c0e4da61a7e426", -"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[None]": "b8a637d74434fa4460492aa8ffedde9e28f30a09967bd0a68bce035537264f13", -"TR_bitcoin-test_signtx_external.py::test_p2pkh_presigned": "fa651a3d73795053b95513f8e1384d79f696e46db71f20ec9af75e91f4f3a9f7", +"TR_bitcoin-test_signtx.py::test_spend_coinbase": "9e5f78e87991cd49216014cf2092841fc6bb8cf589dcba69243017a12e2091b9", +"TR_bitcoin-test_signtx.py::test_testnet_big_amount": "171cf39ca504a781f051000c26567165723ee47da1ed0c98068983523a0d618b", +"TR_bitcoin-test_signtx.py::test_testnet_fee_high_warning": "10c468b1d135b7d459fef2e4f8265a4c7c2649af5965e1a1f30751e268b7d760", +"TR_bitcoin-test_signtx.py::test_testnet_one_two_fee": "fd6000839286e92853fd7e4b5286f3b3308964c8680d12f8c327bfa3171b385d", +"TR_bitcoin-test_signtx.py::test_two_changes": "b35edd5d3b5ce4d4b18c8f60e2308c57f48e1c0eb2c4360ab4a39d2ae2f98186", +"TR_bitcoin-test_signtx.py::test_two_two": "87b562f0420cc9b2087e56fc9f5621f79a4d091338de8291c5b232427af1f428", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[AmountUnit.BITCOIN]": "cfb75d7c8abe03e30d761ccabe0affb4dbf920f099727109bb91fb8617e88baf", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[AmountUnit.MICROBITCOIN]": "dd5dd46e10726c516ebcb9cf6258a6d2f76089df1e711da1fb6ba2962d3aaa13", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[AmountUnit.MILLIBITCOIN]": "a6c93009d7f25f9c1db6dc872a58634f3364960fc2969753652820bec82d5420", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[AmountUnit.SATOSHI]": "fcdc6fb6eaadfef611ca38454a4e2f419fc608c1e1fd70ac1e88b866fddae0c3", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_btc[None]": "cfb75d7c8abe03e30d761ccabe0affb4dbf920f099727109bb91fb8617e88baf", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[AmountUnit.BITCOIN]": "8e270a10163f36478f1c1b9e477f829555f3fddb4b6cdce10c6f9d1a9143c509", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[AmountUnit.MICROBITCOIN]": "23b07cd4bf902926b0562c98068f5ffeb2b855ed389eb8f4ad9d6d156e4b02b2", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[AmountUnit.MILLIBITCOIN]": "fd262c78073ab1aa6fadd821150adbfe8f548772204574ec33093ce98cd6fc7e", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[AmountUnit.SATOSHI]": "64617060e84f1ec931d47cc7a78d0edbf15f1128d3689a9f0703f4a998c2764c", +"TR_bitcoin-test_signtx_amount_unit.py::test_signtx_testnet[None]": "8e270a10163f36478f1c1b9e477f829555f3fddb4b6cdce10c6f9d1a9143c509", +"TR_bitcoin-test_signtx_external.py::test_p2pkh_presigned": "80343cb561bd4766189b059b3ecec921d345ed4983bdafeba14c0faabeefc800", "TR_bitcoin-test_signtx_external.py::test_p2pkh_with_proof": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_bitcoin-test_signtx_external.py::test_p2tr_external_presigned": "10517bdbe7e530082ada2e0246fe11bce0dfdc741ff71b5ce0b1bb33d4d4bcae", -"TR_bitcoin-test_signtx_external.py::test_p2tr_external_unverified": "20c8f0429b26bb5475aa7ea438f93bd976d83554185962d9c049f84e7e112c1d", -"TR_bitcoin-test_signtx_external.py::test_p2tr_with_proof": "0f6b1ab9e3a80d96d4645a902edad4a83f384c774b78f4aa7fdb9c8598b15540", -"TR_bitcoin-test_signtx_external.py::test_p2wpkh_external_unverified": "aa8d89d26b9d6ad2829956e0a4f1700dab9757394d2fe7c795e7625c45728491", -"TR_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_presigned": "c028e8e71c6ef7da9857f3696f383d339b6ec72d2cb71df49fffb0f56df71b66", +"TR_bitcoin-test_signtx_external.py::test_p2tr_external_presigned": "e3bc07f93a75005776c3e575904df0d85b5cd738630370ef84fad16ed8d55e39", +"TR_bitcoin-test_signtx_external.py::test_p2tr_external_unverified": "ba041f1c57d2790ce9fb07504226b4776f31e97dc2af86cfa66f7b9c274e5b89", +"TR_bitcoin-test_signtx_external.py::test_p2tr_with_proof": "89ce7a88a8a8bb5f901c34bccf5f3bef747a7b10926388ebedba5482893c7ab9", +"TR_bitcoin-test_signtx_external.py::test_p2wpkh_external_unverified": "f3a4fc276f8f41aed90ad26ca849f4d35c340e71ec6625e7f5ebd540cac48e98", +"TR_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_presigned": "06eb11f3c039907b344d3cfe6cbb904291e373b87d9b3e5804b5d07b53e707b9", "TR_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_with_proof": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_bitcoin-test_signtx_external.py::test_p2wpkh_presigned": "f95c5fe6cbad5ec7838ebb416cff4b77ca7f458d5dea033223e7dc6623802839", -"TR_bitcoin-test_signtx_external.py::test_p2wpkh_with_false_proof": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", -"TR_bitcoin-test_signtx_external.py::test_p2wpkh_with_proof": "3e83b98315dfbfbd6c9d8fdefb86b9aa7e0537f1c869f76b64e7b70df09c258d", -"TR_bitcoin-test_signtx_external.py::test_p2wsh_external_presigned": "647905b99af69da785bae3b1edea3a59413ebfcf13a7e04b8f87b4d90bdce074", -"TR_bitcoin-test_signtx_invalid_path.py::test_attack_path_segwit": "be79d3dde7efa8bc8c50610f06b2ce499266ccb9aaed9c13d38e4d335fb44930", -"TR_bitcoin-test_signtx_invalid_path.py::test_invalid_path_fail": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", -"TR_bitcoin-test_signtx_invalid_path.py::test_invalid_path_fail_asap": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", -"TR_bitcoin-test_signtx_invalid_path.py::test_invalid_path_pass_forkid": "7e221e550fe2b75e67bb0610b9afda4a136ff8fd7b9134c08268ae51f527870f", -"TR_bitcoin-test_signtx_invalid_path.py::test_invalid_path_prompt": "52d2b069bd357af0e4aa5bfa6ad33a3f3748c8265f826ab30af37fee7634f329", -"TR_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_inputs": "4f27f131f39f02fbfbe88f8dff94e075ada9249cc775a5b2b541474e85b472b2", -"TR_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_non_segwit_inputs": "7752a73c3d1e41265f6852cc035f800e6a631b243d1c74a690b268f2f668bbf0", -"TR_bitcoin-test_signtx_mixed_inputs.py::test_segwit_non_segwit_inputs": "4f27f131f39f02fbfbe88f8dff94e075ada9249cc775a5b2b541474e85b472b2", -"TR_bitcoin-test_signtx_mixed_inputs.py::test_segwit_non_segwit_segwit_inputs": "18801fd48cebc1a9acf3abfd2450b5669279604214a6285eb54fff4b9d8a850c", -"TR_bitcoin-test_signtx_payreq.py::test_payment_req_wrong_amount": "c072f90fecb07a693c09038a26d44eadae88854df02e0537aa99f946d641af83", -"TR_bitcoin-test_signtx_payreq.py::test_payment_req_wrong_mac_refund": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", -"TR_bitcoin-test_signtx_payreq.py::test_payment_req_wrong_output": "249e65809ac735eec03907514834fa347df6fa2f44d63dfc09cc89defd85885e", -"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out0+out1]": "9c81e8183e299ca32345f6cf667574ff6dd946ef86211eef3551004c67679c4d", -"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out012]": "04147f6c0686415b540e46b8d0d3f449371c45a6c02f6ff65c3e3faa5e40421b", -"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out01]": "fff78cd833a5bb893566cb32d503e1de4485a827cdf1b95ee8cc79a774ac0bde", -"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out12]": "69c0dc1281d27c538baa617ae0c429365ae498a6c0e5cf5d3cebad23ed3ca1d2", -"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out2]": "1bdefb37f5e565ad195d1ca61bc772d48276caab1965f96cbaf9f16580fcf5eb", +"TR_bitcoin-test_signtx_external.py::test_p2wpkh_presigned": "89fe3cf49f281f8596fef9220422a058ed4eb03645542691eea08a315f2e9717", +"TR_bitcoin-test_signtx_external.py::test_p2wpkh_with_false_proof": "c679ae4ba5eccd8eeb4aab713265192f97433e7ec6f45506da49cfee4d3afc3f", +"TR_bitcoin-test_signtx_external.py::test_p2wpkh_with_proof": "99cca93258d66242e1628faafeca77d2f7806bd58830631f08e7417eda60f2ca", +"TR_bitcoin-test_signtx_external.py::test_p2wsh_external_presigned": "8421533adf832ca96acc40270174b7e9960d5cd8d4f7f12f8f7040f60504190c", +"TR_bitcoin-test_signtx_invalid_path.py::test_attack_path_segwit": "6d66a2bac3313604e8e889f74c9ff396643627fb9c3b75e06679e6c6f7f70c60", +"TR_bitcoin-test_signtx_invalid_path.py::test_invalid_path_fail": "c679ae4ba5eccd8eeb4aab713265192f97433e7ec6f45506da49cfee4d3afc3f", +"TR_bitcoin-test_signtx_invalid_path.py::test_invalid_path_fail_asap": "c679ae4ba5eccd8eeb4aab713265192f97433e7ec6f45506da49cfee4d3afc3f", +"TR_bitcoin-test_signtx_invalid_path.py::test_invalid_path_pass_forkid": "93ee275294381ad24750ba495c4d288deeebae19589ab5412721e24d19c0b403", +"TR_bitcoin-test_signtx_invalid_path.py::test_invalid_path_prompt": "67208e147c20bebcbc5c4b113f2c1a40bc0f0ef19d44b3816f22420647dc5915", +"TR_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_inputs": "f430a0c741237961fbef447d5d62771f9a813002e3456388561e051a2cedfae5", +"TR_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_non_segwit_inputs": "468064a7e89dfbc98f64b2b00474f8fbc373dd680d6c57f423811571f77ecd66", +"TR_bitcoin-test_signtx_mixed_inputs.py::test_segwit_non_segwit_inputs": "f430a0c741237961fbef447d5d62771f9a813002e3456388561e051a2cedfae5", +"TR_bitcoin-test_signtx_mixed_inputs.py::test_segwit_non_segwit_segwit_inputs": "8fa98b19f71bd376397d6d8a8e99a7ebae7825f7b860dfafaa313ddcca8d9b8b", +"TR_bitcoin-test_signtx_payreq.py::test_payment_req_wrong_amount": "539af17e31670a62d96c8603575815ed235c388444f38f010aa202feefc1297a", +"TR_bitcoin-test_signtx_payreq.py::test_payment_req_wrong_mac_refund": "c679ae4ba5eccd8eeb4aab713265192f97433e7ec6f45506da49cfee4d3afc3f", +"TR_bitcoin-test_signtx_payreq.py::test_payment_req_wrong_output": "514731e9d98222ba02b498d4df4b25aa0f220c121f90fa00d8971131ee332c04", +"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out0+out1]": "853d575e1a3efeb29862d6598e8a3c7f81d3cc46b963c36a52029e52bd4894cd", +"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out012]": "7d478a7a886d6b4d35497ec0592ad43700e5df34472b19b3926026c7f7860b7a", +"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out01]": "740bdd31cc3c957a0a6bbd7c659f3bcaebea97546d69459869b5c43e04ef7d79", +"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out12]": "3f94839c96fc8dea19e4d2c9dce26bd51b2126ac18504ddeb95963bff4166aad", +"TR_bitcoin-test_signtx_payreq.py::test_payment_request[out2]": "31d23d41459ae136351739644298c111d90d56b55f8b670c4567226a365ce689", "TR_bitcoin-test_signtx_payreq.py::test_payment_request_details": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash[]": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", "TR_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash[hello world]": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", @@ -1209,9 +1291,9 @@ "TR_bitcoin-test_verifymessage_segwit_native.py::test_message_testnet": "cf9f0fee72a2c1822250f938ba980a358abf67894a193cfaf9e7ab39e746405f", "TR_bitcoin-test_verifymessage_segwit_native.py::test_message_verify": "314a767de1949e7ec83868654cdf8f3940624ab95db558e932bddea52518dafc", "TR_bitcoin-test_verifymessage_segwit_native.py::test_verify_utf": "d2b1c66934dab005f46883e0b4b60bb0d107f5ead5968c4b58ee9b5db41b6a85", -"TR_bitcoin-test_zcash.py::test_external_presigned": "234d3d65084105e95d632c69bd0e8624988cb98479f02da591104911877f01f7", -"TR_bitcoin-test_zcash.py::test_one_one_fee_sapling": "3dcdde5408fab5932d7a582dc69968c180cb5022bce02f2c9eb5ffa0d1e6005f", -"TR_bitcoin-test_zcash.py::test_spend_old_versions": "a06b4a19285610c38774283f39d1228bc89136e7f3d0efe9b866f24af44d89a1", +"TR_bitcoin-test_zcash.py::test_external_presigned": "6b8d40cfb38951eeb543ca6ed925e9e15233a5d96a0edd96b03b4c2caec0b923", +"TR_bitcoin-test_zcash.py::test_one_one_fee_sapling": "7977dce22e1063b1203ea4b6b5158669e9f20fddf1f98c9ca3d39361d1e710ca", +"TR_bitcoin-test_zcash.py::test_spend_old_versions": "3d09b3ae2137551e1fa6539cb837316cd7c6096de17f9bf70e359a7b3430cde3", "TR_bitcoin-test_zcash.py::test_v3_not_supported": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_bitcoin-test_zcash.py::test_version_group_id_missing": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_cardano-test_address_public_key.py::test_cardano_get_address[False-icarus-derivation]": "545061dc27940209d764ef8e128ab4ebf0c2d6bb87f5dadbd37931b895c1f1c7", @@ -1975,6 +2057,10 @@ "TR_test_debuglink.py::test_softlock_instability": "cff029b728b242ec07f405a0fcd12a77212f0a28e9ec14f1b9e2db1b63293783", "TR_test_firmware_hash.py::test_firmware_hash_emu": "5dde95f0c09df69cdb1e6017ff3269781e499f2c1894db677e1d06971e45d0a1", "TR_test_firmware_hash.py::test_firmware_hash_hw": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", +"TR_test_language.py::test_change_language_errors": "e0c2fc7460e6c839bf6ca5fe009935006c80ff2dd2ddd51af99e78738e07cb81", +"TR_test_language.py::test_full_language_change": "895e00bc158c5cbccc5592ccaef83df439729132c65dc191361de82c91a9f104", +"TR_test_language.py::test_language_stays_after_wipe": "7c287cf28c1737579e493e276a0525b6f9bdf83a37b3c26e4181645e1e17ff8f", +"TR_test_language.py::test_translations_renders_on_screen": "69c2983dd788706d72d6e51202bd89b8dfdc86b97060f4cf3404f71d7b430ba7", "TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_good": "fc78876e183a82d8099263e5b3858620f65f7842f7d31f4679dddcb045f1dfb2", "TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_with_long_label": "57dc2b38db364a7ba43566c700ab27a3e98a08e64efc564db53b61aa6f4098aa", "TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_with_notification": "5f2cecf0c5bf0e7c8743a3a9f241149abf4a09431d21b801c6989c171208aa6e", @@ -1999,7 +2085,7 @@ "TR_test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "ea276d47a239b8389071eeed9fc51e962340f808ef642343fb30db48c8ff699d", "TR_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "3c12cf8b0abe61410de9eb830b922c2ef6154d3cc67a08c9b30f7f861d25293a", "TR_test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "d9304c9e8f08c28b5d34e4e3575850febe0b6540e526a4a6056a6af16f5c9ec8", -"TR_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "b0e4f94c4255c5dcbd6532f3b93481344cce89a44aa3cc186ad47c2abd34bf92", +"TR_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "d8c9b5e0276545c008069e4acd6118e7dbf6ad4a00aeef77c0cc4bf794c2c63e", "TR_test_msg_changepin_t2.py::test_change_failed": "ace510751a5c91022e21e3e088013ff4260dfb1c25732fac80cdc88345d0cdd2", "TR_test_msg_changepin_t2.py::test_change_invalid_current": "c8a5fdc1a70727a9992d07f44d6dbdd9886ccddd99b3a50b41eb75a7e2c2647c", "TR_test_msg_changepin_t2.py::test_change_pin": "a218c9db7f5567458053cf075cef32d3afaec724111c3ba9f23d53b468d4b0a2", @@ -2033,7 +2119,7 @@ "TR_test_protection_levels.py::test_passphrase_reporting[True]": "ddf4eaf9c9d1d341cd484658135750b684ed31a7d77a5f4e6972fa8553bd0a29", "TR_test_protection_levels.py::test_ping": "4ffbed72e7ed7fbab85f830952200adf7758af81b658b56de4672344120456a6", "TR_test_protection_levels.py::test_sign_message": "3bfb26e19709830584f27aa1b1f114e7bf41cf15ca57a4ec9bfa618fcba80ad0", -"TR_test_protection_levels.py::test_signtx": "e8f34f91bacd50ffc4c446e5e381c06c18b2a376c2ca9a99c77dc637e806a90e", +"TR_test_protection_levels.py::test_signtx": "c475a94e5e267bf8f2f0563db887b54c68c83b3de89da28c3c80b0406e77036c", "TR_test_protection_levels.py::test_unlocked": "e76b12522fd28fc08af0135271615d63a13a0ba9f013ebd1afe20ef72abe0803", "TR_test_protection_levels.py::test_verify_message_t2": "5f04a88b0acc2daa3e87c02074a64969aeb34157006adbf15a3655e94f838bdf", "TR_test_protection_levels.py::test_wipe_device": "bc06c63c7f5b9e7f0ada0d3d342125905b4fc463fb0eb2ba363b300909dedb9c", @@ -2077,19 +2163,107 @@ "TR_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "e8be8e0c3a7b9e565ebeaec54199fe8475a7b1ffe9dec22d001ccfb9ec4927ec", "TR_webauthn-test_msg_webauthn.py::test_add_remove": "d8385fdba14ee5d71f75145ccd6574a736d2e3c3915acd15597cc40618b14c94", "TR_webauthn-test_u2f_counter.py::test_u2f_counter": "097bbc8fb0ed96383c110ee6430e1d168d837ec110ba04d29179bb0789cdc3d6", -"TR_zcash-test_sign_tx.py::test_external_presigned": "234d3d65084105e95d632c69bd0e8624988cb98479f02da591104911877f01f7", -"TR_zcash-test_sign_tx.py::test_one_two": "f99b201fb591f2a94c4aeac0f41c509a78b09d6113833d995e312ff7e0953e2f", -"TR_zcash-test_sign_tx.py::test_refuse_replacement_tx": "19d738c0babfd39c17793e98e5d621e147d02367f4e4a7c712bb08c0f914a04e", -"TR_zcash-test_sign_tx.py::test_send_to_multisig": "0a786b0abad93ef3cf66bdc60384134414e83aecf0bdf960c49a79fe8fbcbdb7", -"TR_zcash-test_sign_tx.py::test_spend_multisig": "f85e7c0dbe7919db2c8a599ba17cc2c94f90a29fb259546089fc08494cdd894b", -"TR_zcash-test_sign_tx.py::test_spend_v4_input": "eda208bcbe9c956ddab927e5189ae25d79a92f6c30cd1b13f281341f2333d18b", -"TR_zcash-test_sign_tx.py::test_spend_v5_input": "e8ebe34d69bb1870a8fadf3ed0fee90c228ede047b99b91c02331e79ec80d7eb", -"TR_zcash-test_sign_tx.py::test_unified_address": "704e29c0e444f569843ae894614af7f774580d5fd23fc8e0eaadec4cf71c794a", +"TR_zcash-test_sign_tx.py::test_external_presigned": "6b8d40cfb38951eeb543ca6ed925e9e15233a5d96a0edd96b03b4c2caec0b923", +"TR_zcash-test_sign_tx.py::test_one_two": "8761a870d70090942fccc9627b2743edabba126bbcf47f9d0a6c4fb962855b6a", +"TR_zcash-test_sign_tx.py::test_refuse_replacement_tx": "c679ae4ba5eccd8eeb4aab713265192f97433e7ec6f45506da49cfee4d3afc3f", +"TR_zcash-test_sign_tx.py::test_send_to_multisig": "b964d3cd90c1389c8cc3fe9dd9714b6e94556a18825a1593d21d2c8e9e752201", +"TR_zcash-test_sign_tx.py::test_spend_multisig": "d266bbca02d8c77b5c6d3b86b2a8fb77f6031aa5c5ea5442da5ff01f04444a65", +"TR_zcash-test_sign_tx.py::test_spend_v4_input": "53f4a8ad140affa4745957801c330ceededebf6a6d06cf36b6f32a90e8fd30bf", +"TR_zcash-test_sign_tx.py::test_spend_v5_input": "ee3cddc7bf558c98e3b0f050b8131c7ab7b873f97af2d70e17ec5f1c7eeafe09", +"TR_zcash-test_sign_tx.py::test_unified_address": "f5f3e0908edf6b079a0e4ab2fb44bc2f6b46d0d31078ae57882ea8e305436ef3", "TR_zcash-test_sign_tx.py::test_version_group_id_missing": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095" } }, "TT": { "click_tests": { +"TT_cs_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "8ac8b851bdbbc0a6ed0c1d44523dbe776a8d5a4f3865532d6da4d87c5f2eabc5", +"TT_cs_test_autolock.py::test_autolock_does_not_interrupt_signing": "e423cc8c7e6233c9ca407b9971ee85d2ad1c31c51c6c7a603d83d073834bf74e", +"TT_cs_test_autolock.py::test_autolock_interrupts_passphrase": "279e97bd1f4984d54f842a15f8e3f2344b00104aa8b9c4eedaedd18789e6357e", +"TT_cs_test_autolock.py::test_autolock_interrupts_signing": "7772cf50bbb03c6539e3be241e772590e1a968652c7dad10d8a152b453a3e112", +"TT_cs_test_autolock.py::test_autolock_passphrase_keyboard": "e1c4c1e8430e0d9584e436e5e772b980730c3d11ba3305906506e01466cbf950", +"TT_cs_test_autolock.py::test_dryrun_enter_word_slowly": "4e2670a3b2affae6299b87dcd223fcf8deb6d43dc741f12765f25c70df987661", +"TT_cs_test_autolock.py::test_dryrun_locks_at_number_of_words": "5356fbd4e69139b000f3942ecf09e4b5c90f91fd545b99a1c312723fdb37e2f2", +"TT_cs_test_autolock.py::test_dryrun_locks_at_word_entry": "035812373e6541eaad1e52f406e24e50fa9034532b0486370c44095bdfdeceb1", +"TT_cs_test_lock.py::test_hold_to_lock": "30fbada26a6b65fc93e80264161d7c7ab364486d7a592a73884cd0d3f14b73bd", +"TT_cs_test_passphrase_tt.py::test_cycle_through_last_character": "22b3048333e5934bd0a67b134348ebaca340b70c5e31bad189baf6432fc72185", +"TT_cs_test_passphrase_tt.py::test_passphrase_click_same_button_many_times": "7324808147e00227cf04d9dc12ed0e3c564884e662730e6e2a47fcbbe5230e6f", +"TT_cs_test_passphrase_tt.py::test_passphrase_delete": "b4b1dd4acdadc86838a9ced80b9229b09c4e6797be7462ec3364f45e93d72c3b", +"TT_cs_test_passphrase_tt.py::test_passphrase_delete_all": "8da6e4ce7f6c7385882618386b6fb1d12185e6c7e492f7f00cbd5fee832c4534", +"TT_cs_test_passphrase_tt.py::test_passphrase_dollar_sign_deletion": "3130fe042eacc23cef4b3e89d14dca82ff9c9d5a7daf9bcc85f81d3ef607cec8", +"TT_cs_test_passphrase_tt.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-78765865": "4fe1e9b4349befe4aae68732ddde8f40656b83dc6ea29365dbad44cf3e2de145", +"TT_cs_test_passphrase_tt.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "33f008ad1464283de59d5dc83dd7f907ed6d7883a0d8545e1582802ab6b195c1", +"TT_cs_test_passphrase_tt.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "5d53ef10a348d911dfcc6947220c7bb24d43a97964a2ba234e1dff59a99f21fe", +"TT_cs_test_passphrase_tt.py::test_passphrase_input[dadadadadadadadadadadadadadadadadadadadadadada-1cc97541": "4f490bda3e8bda4966f28568c1ff5f117f2f2bfd70828b58270af70fdc7a309d", +"TT_cs_test_passphrase_tt.py::test_passphrase_input[dadadadadadadadadadadadadadadadadadadadadadada-ca475dad": "a8294cc58443e53c7d6d6ca233172fc094cbf78609b9fbeea871c0ed70749151", +"TT_cs_test_passphrase_tt.py::test_passphrase_input_over_50_chars": "4f490bda3e8bda4966f28568c1ff5f117f2f2bfd70828b58270af70fdc7a309d", +"TT_cs_test_passphrase_tt.py::test_passphrase_long_spaces_deletion": "e0171af5679da647289b18249c298c6100a52a941a5ac709101fec816ea97794", +"TT_cs_test_passphrase_tt.py::test_passphrase_loop_all_characters": "9f886f564e271b378749be90ae24350d8c69662bad13cf212b6a932d4b6a0701", +"TT_cs_test_passphrase_tt.py::test_passphrase_prompt_disappears": "22e077eaf2049c5e7d222e76dab81a5162ab7dcdbea24087a98a8b4d37d4521b", +"TT_cs_test_pin.py::test_pin_cancel": "c916aba175ee1cb01325941ddd7f627eac337b8acb17f9a89f420d9906bb1f09", +"TT_cs_test_pin.py::test_pin_change": "c8b70954f7dc9c8379d46d43133832c50ee3a9fbd5a2c079701e65e14bbd0895", +"TT_cs_test_pin.py::test_pin_delete_hold": "03052bf73c22c9a0dbaab484d6ba7c4cc326f9c44248940d1d220a870a73786a", +"TT_cs_test_pin.py::test_pin_empty_cannot_send": "a9d0a022e3ce8f5a20b16fd8035c84f8df15734db5d190e1b2357e956cbb8835", +"TT_cs_test_pin.py::test_pin_incorrect": "a6a80a18a2eb01ffa3aa6c0b43ff7be5a3e83019310eb56ecd4676aa982072c0", +"TT_cs_test_pin.py::test_pin_long": "a668a0178f187235375597127951708bb756d04db954f850564d13a0b6eebdf2", +"TT_cs_test_pin.py::test_pin_long_delete": "987d0048982f47d1772f3559cdfe373a0ca742af94b85d242ff9b6e21f557c03", +"TT_cs_test_pin.py::test_pin_longer_than_max": "28f4dcfafcfc19117becbc34133b0ff77590bb431da0fd775534e4d5a457517e", +"TT_cs_test_pin.py::test_pin_same_as_wipe_code": "7065fb2be1948e4081f66609e31c165face36f150e8ab565a815e6c23fa72a87", +"TT_cs_test_pin.py::test_pin_setup": "58edf1a026aace62cafce1cf70b6b936635874ae8e285890c480bc131c73d294", +"TT_cs_test_pin.py::test_pin_setup_mismatch": "69511d1a1f5b21871ab62ed08f7ea9e99977ae2aa75dbedd2e7d2bc3351fc40d", +"TT_cs_test_pin.py::test_pin_short": "a9d0a022e3ce8f5a20b16fd8035c84f8df15734db5d190e1b2357e956cbb8835", +"TT_cs_test_pin.py::test_wipe_code_same_as_pin": "1bbf26eff58927d03d4ca6f2685da3a9fa50a9801de3752211682d267f2d5f52", +"TT_cs_test_pin.py::test_wipe_code_setup": "35257bf290bf4b9f73e52d428581808db0701566918b983581f632e5f9ea9086", +"TT_cs_test_recovery.py::test_recovery_bip39": "c98565c401efbffa8984cf9a3d4eaacc6cb7ead55cc1a3d032549cc5acce82da", +"TT_cs_test_recovery.py::test_recovery_slip39_basic": "54e8ab4706477683619744cd3121648ebd95ddad026f4db3404009bc988ce035", +"TT_cs_test_reset_bip39.py::test_reset_bip39": "ad6acfa12d6cccc6fb9d70a3623e33a524a3744d2aef710048decdf4ddeccffd", +"TT_cs_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "c558140c3f5dd871904d303e8340876a75a521e9d5833b07dfc7cb0facdeaeaa", +"TT_cs_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "33b1fc3279f220d28059cae0af50ccf1a00452e0c0af06300cf451270c3ab893", +"TT_cs_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "b1ebfbc28bcd9efb70a4463900f85dbf9723342d8d9510f57f4dd2d101bf9f69", +"TT_cs_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "4e00953ac93bda449a63aa3bec939bac7fbf9ce139f5e617eee4f54523894f76", +"TT_fr_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "256d9ce8980c3300e596332861198d3c2cad16e335bf7e451e38aa5bf060323e", +"TT_fr_test_autolock.py::test_autolock_does_not_interrupt_signing": "c420b087345c6ecb15f47744fcf467c8243405a458c22b3649dfb7da5591860a", +"TT_fr_test_autolock.py::test_autolock_interrupts_passphrase": "cece356568a69fb3849080397e829bca192fa69cafbb3de83d0b77583206f6fc", +"TT_fr_test_autolock.py::test_autolock_interrupts_signing": "475bd064b38885452adde4c2587db39080e6526c46ef123e05fa74d68a16c5bb", +"TT_fr_test_autolock.py::test_autolock_passphrase_keyboard": "6a8920aa6890a26c3a1e96657a9aac5170c2897b79d7ecb4b593a28a7a82851d", +"TT_fr_test_autolock.py::test_dryrun_enter_word_slowly": "f739e1dc3f732d3396fa445025b791f653f2851d08771e482fc05fb1a6eaa081", +"TT_fr_test_autolock.py::test_dryrun_locks_at_number_of_words": "6208f96453afc32f41413b1fbc42c06f9f66060c7d3eaa3b18681e310eb573f0", +"TT_fr_test_autolock.py::test_dryrun_locks_at_word_entry": "cb7ec07f98d4c2991b6076165a145f07599168734e8a1dc1776d9e8afcd55a52", +"TT_fr_test_lock.py::test_hold_to_lock": "10233451f3389e6789364636dabf80486fff5eb3aa8c44f5905d68b406156133", +"TT_fr_test_passphrase_tt.py::test_cycle_through_last_character": "2fed86e1db1fcebd59156672ac82c5d4b7c98e1f6134bfe6adfa22192bb5830b", +"TT_fr_test_passphrase_tt.py::test_passphrase_click_same_button_many_times": "371b81105fb3cd1eed5ecb9996779b1268b075df77dbc9aceb5cbed8e0041e55", +"TT_fr_test_passphrase_tt.py::test_passphrase_delete": "41f4a839d35b0f34f340b3c75aac364ca3047b7798e78702461e75a9a318081d", +"TT_fr_test_passphrase_tt.py::test_passphrase_delete_all": "190e51d6385658e2f564923e4645b8b71f52cac30e779cef3f315386f8a33f1b", +"TT_fr_test_passphrase_tt.py::test_passphrase_dollar_sign_deletion": "2b95b740a4946a5417f3fb1a764cbf1591cc30897cb85c11b185959148a22d3f", +"TT_fr_test_passphrase_tt.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-78765865": "71da5ce01252c5df62cfcef0f3a3a9a1207b96e95f7602de0e99a49232928621", +"TT_fr_test_passphrase_tt.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "aee787eae0550e7228781068e62fd9956a8f6472fc2441981f7ffc153d2f792f", +"TT_fr_test_passphrase_tt.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "daafe755b615d506ac78fea167ee26a6e057ae19917324959e0d777a85b6d0f1", +"TT_fr_test_passphrase_tt.py::test_passphrase_input[dadadadadadadadadadadadadadadadadadadadadadada-1cc97541": "afd7d47aa1f1b157be151d4a9de0840285102e06823f96ac1c3d319e1ac19fa2", +"TT_fr_test_passphrase_tt.py::test_passphrase_input[dadadadadadadadadadadadadadadadadadadadadadada-ca475dad": "46429fdf32c9162c307b46a058965ff37c6fc70da3b04496610e24cd90c5a567", +"TT_fr_test_passphrase_tt.py::test_passphrase_input_over_50_chars": "afd7d47aa1f1b157be151d4a9de0840285102e06823f96ac1c3d319e1ac19fa2", +"TT_fr_test_passphrase_tt.py::test_passphrase_long_spaces_deletion": "1ecdc68cbaee7e820827a3e875a1a047659de4963e3ed6804bcf1ad46c5c2cb1", +"TT_fr_test_passphrase_tt.py::test_passphrase_loop_all_characters": "bd32e968838c27d5a0abc5cfaa9ab82888fce1160456eb826078e617750374c6", +"TT_fr_test_passphrase_tt.py::test_passphrase_prompt_disappears": "1785da21eef4dee333f310becdfe3506bf60f09a6d9d4a640be4d1034825d9f4", +"TT_fr_test_pin.py::test_pin_cancel": "5864b0a211d644ce8d96b67ee419945838e88096c80d7b08b2da3d67b7c5831b", +"TT_fr_test_pin.py::test_pin_change": "2ccb19a02bd69bd605a463cecf66980e41d2dc4d5a6b9519c70f1533a9cd731c", +"TT_fr_test_pin.py::test_pin_delete_hold": "45a85ceb29efd7580a8f87882519f981fdca5a61c0f611fd694a3277c7677c13", +"TT_fr_test_pin.py::test_pin_empty_cannot_send": "b3b60c40fbdf509fa08e549eea44e8051ea0339ff694a79e738e7afadbd01d50", +"TT_fr_test_pin.py::test_pin_incorrect": "a179678e3503cd91ca851d9b875098b58c7bc8d4d8f3559c6d7be8ae9f62e8ac", +"TT_fr_test_pin.py::test_pin_long": "2a29be494902e3b596b027478d6fcd3cf18f02aeaedec101440fc5e1e4757e9b", +"TT_fr_test_pin.py::test_pin_long_delete": "7fde03ee8156cd8220aa9557e95b5231d48f01e4ac16d84abba7612ee8dc577c", +"TT_fr_test_pin.py::test_pin_longer_than_max": "662af00d33c5d4992d0a94da97cce0e4f85b23088132a8230f739877db024ccf", +"TT_fr_test_pin.py::test_pin_same_as_wipe_code": "4958e3ea7a27f3955b8f14a9f40f3439863ff4cfee81e0bea0220a6e14d70f7f", +"TT_fr_test_pin.py::test_pin_setup": "62ded5fde417f99637966912725d255706fd5312d3ebce867d5f9322e49c587b", +"TT_fr_test_pin.py::test_pin_setup_mismatch": "387ec1d17dc42bb59c73836a0a253d8914f373b17c67c43c519771537cd3a831", +"TT_fr_test_pin.py::test_pin_short": "b3b60c40fbdf509fa08e549eea44e8051ea0339ff694a79e738e7afadbd01d50", +"TT_fr_test_pin.py::test_wipe_code_same_as_pin": "036c18721cba88e64f41bf4feac9803b76298dc672f10573c44bae21fed87752", +"TT_fr_test_pin.py::test_wipe_code_setup": "0cd22be200648002bb625bda44bb89cb64a690d58c2e44eea388a32407a02bbf", +"TT_fr_test_recovery.py::test_recovery_bip39": "81045f54a1c742b0e81c13e242aca4ac93dd58dea8ef6fd518cc93b452738669", +"TT_fr_test_recovery.py::test_recovery_slip39_basic": "38da78ae86b41a27fd8a629f9e156799ea12365cf36323d2083b4c2a4503c3c2", +"TT_fr_test_reset_bip39.py::test_reset_bip39": "86a2c76fe3b58d1a7befed667f55be2bea9ade9bd1037f3d9e62eef9d3c2c06a", +"TT_fr_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "0b7a579ec27e396723a92f66372fe758f7a455348d81f6e409553f83ed2262ba", +"TT_fr_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "14d42a545c99006863f4052a795d45c5d7d7e97745d14f38e32aaff6e5ed3335", +"TT_fr_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "74c6f6df2e45ad0c24abc113cb5ea5dbc39cf20923f91e39fd82195ad24fff95", +"TT_fr_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "b043efc598ed96a0f05372b50bbdaddfb090f39213eeae2b7f7cc2d9931ac54b", "TT_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "521b6ce07207c262b832fb4e78477f89211dc02a4b6e02a6fe8d389a2be909cf", "TT_test_autolock.py::test_autolock_does_not_interrupt_signing": "e8ff223f44e97a98fbef62c2ed314bdc2d3a2f160d0396d09733847ce103494d", "TT_test_autolock.py::test_autolock_interrupts_passphrase": "15ce8ca8c46be745296ad39b2df4e4503f1b3208c3f5b3e3f48d7bd172779605", @@ -2121,26 +2295,26 @@ "TT_test_pin.py::test_pin_long": "795a3ea7d9661924c334ae32c3da99d4cc27909b905808a422a240d221af0775", "TT_test_pin.py::test_pin_long_delete": "8fc0b6b0b80505d2e701c52c23498cf23584b7f9557dde7e7b3eab2eb4b7c447", "TT_test_pin.py::test_pin_longer_than_max": "34af02823f498627dfe0e96e97a912862c5c49eb90202cf8b1fb300b673a73ca", -"TT_test_pin.py::test_pin_same_as_wipe_code": "b0abce32d6ee3f8a84fd44f90185b7ea3c4e18d642e1c66359de5e2dcea2dcf3", -"TT_test_pin.py::test_pin_setup": "d19f026060f415a7ec104a8d1de771acfee16e8fc143c526a8f1d073ca59da9f", -"TT_test_pin.py::test_pin_setup_mismatch": "663430644b26f4493fc3ecc03a2bbce5cecda7b6ccb5be5a8a4c0ace239f6dd5", +"TT_test_pin.py::test_pin_same_as_wipe_code": "fb196ac02453b894d7773276b67174c129f270642d7f83051464591f307e11c9", +"TT_test_pin.py::test_pin_setup": "2c10614e7ad703ae74bd95de372a732ed6dac524d83430fbb43cd36d867dd8e7", +"TT_test_pin.py::test_pin_setup_mismatch": "2ccaaf68e752185f567a710b9a1c24b7e0157314c7e9b8583658b320c8e6cc79", "TT_test_pin.py::test_pin_short": "197c595eab9757f72ed59642b7bea57728397ffe072a762a126d20833d0b9278", -"TT_test_pin.py::test_wipe_code_same_as_pin": "0ec9b624645bfad45c577c687fc1a630f063ce8eabd13533a9a7f8bbaf4e183a", -"TT_test_pin.py::test_wipe_code_setup": "145ac58cfb3da76dd2528ce8aec7c5481b2272975778cd70bf38f1a9b8db7f9a", +"TT_test_pin.py::test_wipe_code_same_as_pin": "f13b6559d4d73c2853218fee85dc951c41c5b4e3867ee0e989443940233f2f96", +"TT_test_pin.py::test_wipe_code_setup": "3d6a04cc7c8d3a061758a9559277a548bf4492ef59afd1d040693372d197383c", "TT_test_recovery.py::test_recovery_bip39": "65a138f634806c6483c55c6ce5365b8a7a4073a3c0c340b1826042262faa8545", "TT_test_recovery.py::test_recovery_slip39_basic": "9b0f5a7b8d2ab0fed1e5389076bc035e24dce377d275824220f1aa61e9bb4810", -"TT_test_reset_bip39.py::test_reset_bip39": "4331646c82813f3bba6869d375dd1db9155c37a13f6468e950d4504398604811", -"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "ba8c021eb2930abd18b4079fbe29724b220f3fd1ecedab9a86f5dd939563ceec", -"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "d15db902e2838520f65002442ea2501fc67220f8a3f2da18aac55fc960c7ba3a", -"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "7202b1bcc7e8ca9e92ad83592469f74d306b8b2090d644aa810bf184edefcd71", -"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "3052aff98947aa1b15dc1abbc5091d25342902c708e7356077dcdad5e6df10b6" +"TT_test_reset_bip39.py::test_reset_bip39": "19fd9f6233d72224696c547528a0079934a86cb41539b6f7149aab57b0aaec42", +"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "1d604f1766ce861e616745dcb5c89122165eb26ecc7f40039e50b8fe8c61a861", +"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "58fe6639315e549576be1bf8141ca01cea306c94b21e9e03067ba4ade6065d27", +"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "ffe9e0d38c068f2af90dfe9f63d912492b8eeeba8e4d5105d9df86e31e0dbbd6", +"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "1db5982fc3b0795517144fcb3a30da7e2a6226c223912f6d88647f6715fd872b" }, "device_tests": { "TT_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "fa3d667e1439c9c2a475fff22d94e330c14697ff8dda4bcdded775492cb40a90", "TT_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-1-bnb1egswqkszzfc2uq7-1adfb691": "ceac9a64c9aff8a4226e40f068ce412556eb582d909a9f8d401e83ca10433d77", -"TT_binance-test_get_address.py::test_binance_get_address_chunkify_details[m-44h-714h-0h-0-0-bn-59d4996f": "9836e9ae0814d1d42cf3957fc6ac6b17dc2503e5c0bb3a27fd1209ebf57c03d1", -"TT_binance-test_get_address.py::test_binance_get_address_chunkify_details[m-44h-714h-0h-0-1-bn-c9025900": "7c2943551ba1616bf057ecdeced1f603a90d60768dbd072573a60dbfdf4933b9", -"TT_binance-test_get_public_key.py::test_binance_get_public_key": "3d0517133c0cef58b6d8627527f3960f10706094d0d7113bc622453dd5cc9d8c", +"TT_binance-test_get_address.py::test_binance_get_address_chunkify_details[m-44h-714h-0h-0-0-bn-59d4996f": "9c34eb2032085e1b5cb8ff2799cb93d463bc30cd8da951ea49d30929520fff1c", +"TT_binance-test_get_address.py::test_binance_get_address_chunkify_details[m-44h-714h-0h-0-1-bn-c9025900": "1c84455646b10506f9d96d51c9acd9434c335feb6738f8cddd6d0b44229049f8", +"TT_binance-test_get_public_key.py::test_binance_get_public_key": "52b93017dc7b41514d8062e13d8cbb807adda7c38357480bbdfa01e597b910ba", "TT_binance-test_sign_tx.py::test_binance_sign_message[False-message0-expected_response0]": "28fde0e964521cf59b97a78c62d0c9655cfc8b0c8b1bdda7f75efb6a6c705a01", "TT_binance-test_sign_tx.py::test_binance_sign_message[False-message1-expected_response1]": "5c0a4acdd90581b30266f1d97b811a3ad40cc39d2059d14f0a938c9a71016729", "TT_binance-test_sign_tx.py::test_binance_sign_message[False-message2-expected_response2]": "fc22ef1bf651f8c8475674f3699a47c2ea951f9cc93ca60a4c9b2e8ab3c124cf", @@ -2259,26 +2433,26 @@ "TT_bitcoin-test_getaddress_segwit_native.py::test_show_segwit[Testnet-m-86h-1h-0h-0-0-InputScr-821a199d": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_bitcoin-test_getaddress_segwit_native.py::test_show_segwit[Testnet-m-86h-1h-0h-1-0-InputScr-9d2fa8bc": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_bitcoin-test_getaddress_segwit_native.py::test_show_segwit[Testnet-m-86h-1h-0h-1-0-InputScr-d5b7f8fc": "f9c524eae89e19091508c9fc40e18a2c2f167a15f28bfd59ff4aad7bd80fa016", -"TT_bitcoin-test_getaddress_show.py::test_show_cancel[m-44h-0h-12h-0-0-InputScriptType.SPENDADD-4eca71e0": "e9e1685bed4796972fdf044a84d24c95981bea00df672614cad8c2ab1b2c8e74", -"TT_bitcoin-test_getaddress_show.py::test_show_cancel[m-49h-0h-12h-0-0-InputScriptType.SPENDP2S-4ec777e0": "3a15c7626541011177bc88967c72c278fc229ad8ac131cf606c8dc1f48b5df2c", -"TT_bitcoin-test_getaddress_show.py::test_show_cancel[m-84h-0h-12h-0-0-InputScriptType.SPENDWIT-d6991e22": "cbbf158f803cec0218cef4482be16b1fbce5a33c27d22089bd562cb8991f697f", -"TT_bitcoin-test_getaddress_show.py::test_show_cancel[m-86h-0h-12h-0-0-InputScriptType.SPENDTAP-4c5b2b38": "0478aa33fb21365c1d114cad7bfb3c0270b20ffc3573d1eb70821101004e30dc", +"TT_bitcoin-test_getaddress_show.py::test_show_cancel[m-44h-0h-12h-0-0-InputScriptType.SPENDADD-4eca71e0": "2946c135434f43bb2a69fc13495d67e7a6a365b310694ac26f5815209401090e", +"TT_bitcoin-test_getaddress_show.py::test_show_cancel[m-49h-0h-12h-0-0-InputScriptType.SPENDP2S-4ec777e0": "0017ffcb76d1688633e9cc3424c1419a1187198958a81080f32d94ab5cdd03be", +"TT_bitcoin-test_getaddress_show.py::test_show_cancel[m-84h-0h-12h-0-0-InputScriptType.SPENDWIT-d6991e22": "97c57af543fa96fe52658596f9c9c88480eae83e4c4b95d3f5834069dd921a7d", +"TT_bitcoin-test_getaddress_show.py::test_show_cancel[m-86h-0h-12h-0-0-InputScriptType.SPENDTAP-4c5b2b38": "300a24ce1f5be3a4448bd0e840d89f9f279294f1c5693a6675d73a493badfae6", "TT_bitcoin-test_getaddress_show.py::test_show_multisig_15": "af9af600df851cfbd070139fdd4db6588e6ce4a116b45ca19a0068ce9d678f51", "TT_bitcoin-test_getaddress_show.py::test_show_multisig_3": "94d22e1a493fa8f3c3e15a65333ef0691fcab1594ee0e767773a3a90db6814b6", -"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDMULTISIG-0-3-4efd9cf3": "faa98c7c2c581c303a2b72f0a2f93bfb6c1ed504cecda4b483af17315b569e78", -"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDMULTISIG-0-3-98a7e339": "faa98c7c2c581c303a2b72f0a2f93bfb6c1ed504cecda4b483af17315b569e78", -"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDP2SHWITNESS--2cf5f03c": "96ea93c13ca429954e84685b85df2eafa7e9f25afbc8ca77a88eb909bd3f7c8d", -"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDP2SHWITNESS--5ea18367": "a93a88119c9ee255e09a87ff1e9c078a61e1e7ec61a26a4ecbccf294a70269cf", -"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDWITNESS-2-bc-e70b56ea": "46b56e80439b320688feac9bd0ceb440ff83cf5d1da51a6425aeb97aaf097393", -"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDWITNESS-2-bc-f3c4650f": "17a274a917e5ff853a429561ef4dbceb12c41520c18714f3c3f037d008e5527e", -"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-44h-0h-12h-0-0-InputScriptType.SPENDADDRESS-5c88d0fd": "2640940dac7119c208074f263482b66323ee2ab0769c2cda32b75e91bfe9f2a3", -"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-44h-0h-12h-0-0-InputScriptType.SPENDADDRESS-87490d4e": "cbe6b393e10f5e4fc0b780255b19d46f53cf60706a2f2361a658a82b0b659825", -"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-49h-0h-12h-0-0-InputScriptType.SPENDP2SHWIT-9bc227c1": "ba1c1f8dab1121f7fdba73f9a481145ff65e383763a24a1e66d25b2b4681c58f", -"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-49h-0h-12h-0-0-InputScriptType.SPENDP2SHWIT-aca0623f": "62c493f4310b90d0082281c38acc314cfd059bbbbfe18a150a5d7236cf9e56f1", -"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-84h-0h-12h-0-0-InputScriptType.SPENDWITNESS-747c079d": "8ccb2c2222cbc695f5d49543e637f03eb69d60ea654636e0e8b97a0c04a21e52", -"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-84h-0h-12h-0-0-InputScriptType.SPENDWITNESS-e4302080": "cd5a286c516dd0bd7eb107081356863504947e9498a9d6b490760265c055c870", -"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-86h-0h-12h-0-0-InputScriptType.SPENDTAPROOT-071a4a07": "a5f25a795619e2adcc3bedc07c6b12dfa605a9369705a013f276428a5a4f78d0", -"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-86h-0h-12h-0-0-InputScriptType.SPENDTAPROOT-25ee9808": "508a69027cc9b71d218f65a015d963f13fffe4bc9ad435f8fb12894ee3ae298e", +"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDMULTISIG-0-3-4efd9cf3": "7e9f330e73459f5c0fb222c84bfe1826548254a2d69426898b4ce0448f321bdb", +"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDMULTISIG-0-3-98a7e339": "7e9f330e73459f5c0fb222c84bfe1826548254a2d69426898b4ce0448f321bdb", +"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDP2SHWITNESS--2cf5f03c": "77312bf749a401a435ba1ce8f473f6ee63e3a2f0bb81870bbace0e4fbbc45462", +"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDP2SHWITNESS--5ea18367": "5c40b4634bd310c99f519e3594b387a61c0fa06ce34cd40f0fc51c4473b869b3", +"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDWITNESS-2-bc-e70b56ea": "ead66eae50cde3bdae1de8a9dc709a099b36287931453b83e1888792a559ca38", +"TT_bitcoin-test_getaddress_show.py::test_show_multisig_xpubs[InputScriptType.SPENDWITNESS-2-bc-f3c4650f": "7b7a29c8edd8d921a1399ab0c8d22f08b3f2be5ddc7785f3aabb0bce3c8381b7", +"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-44h-0h-12h-0-0-InputScriptType.SPENDADDRESS-5c88d0fd": "8f347141e2562c2a2bf131715ee1c29f60ed4657905ebdbf3859b3ceb739f0bb", +"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-44h-0h-12h-0-0-InputScriptType.SPENDADDRESS-87490d4e": "9fba0a01a666c082d8ac78cd591f0b9ad41f2b7741ce9e4b7d92c4aee0c2b393", +"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-49h-0h-12h-0-0-InputScriptType.SPENDP2SHWIT-9bc227c1": "6b0d45ca2b560375f8532f19de0e65688fc48b51d8b00df931231d88f440aa92", +"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-49h-0h-12h-0-0-InputScriptType.SPENDP2SHWIT-aca0623f": "6adccd56e448a07b2f93fd33277ff2a328890e96fe7a311b55032c11c27a6720", +"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-84h-0h-12h-0-0-InputScriptType.SPENDWITNESS-747c079d": "98dfe18e1dbc6033d959a058c3041be8b2a522fabf5c22df9d5f3e3bde983794", +"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-84h-0h-12h-0-0-InputScriptType.SPENDWITNESS-e4302080": "9ee9b3bcd5033a655ac71606e0b33df3adc8778f08bfbf70235f6bafaf82980a", +"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-86h-0h-12h-0-0-InputScriptType.SPENDTAPROOT-071a4a07": "de13b301fb173505efcab9e23a7a326d235a33291523d5c22b880ecf06b14592", +"TT_bitcoin-test_getaddress_show.py::test_show_tt[m-86h-0h-12h-0-0-InputScriptType.SPENDTAPROOT-25ee9808": "49ffd33f61429483c2fcffe5b637b5e8d0e96e3a75483f4dfa77a7d9be86bbf1", "TT_bitcoin-test_getaddress_show.py::test_show_unrecognized_path": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_bitcoin-test_getownershipproof.py::test_attack_ownership_id": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_bitcoin-test_getownershipproof.py::test_confirm_ownership_proof": "5f4e0c17cb3258af3901c7508968af36632f3e9826f6f63068b8521a4fbb36c2", @@ -2565,7 +2739,7 @@ "TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909800-89859-0248304502210097a42b-7a89e474": "77146b5285f8f5cd30307f66929d35cbc2c51fecb78cd6e0b2a491b59545b6c3", "TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909859-89800-02483045022100af3a87-80428fad": "685ae658aa7b6d7d0e450798dfa88212241f89dce943e2dc7733e9ba0f32578a", "TT_bitcoin-test_signtx_replacement.py::test_p2wpkh_payjoin[19909859-89859-02483045022100eb74ab-881c7bef": "77146b5285f8f5cd30307f66929d35cbc2c51fecb78cd6e0b2a491b59545b6c3", -"TT_bitcoin-test_signtx_replacement.py::test_tx_meld": "da018552044c9dc07dc4019dfc6b5c7cd77b7597f4c8e3b142cf84ed6933699b", +"TT_bitcoin-test_signtx_replacement.py::test_tx_meld": "0b5a6e9db8dcd45d78f3cb84a596c61f97c91513b9e896a1b196404eb7292d53", "TT_bitcoin-test_signtx_segwit.py::test_attack_change_input_address": "6ab1dc24fa8dbc1c88a9937882069b33f0d5f0766e99faa7c3cd83709839a98b", "TT_bitcoin-test_signtx_segwit.py::test_attack_mixed_inputs": "9023903d91c453cfa696777db99334016385832fa47af3756a1aed58d097ae77", "TT_bitcoin-test_signtx_segwit.py::test_send_multisig_1": "f170d79bdc23af7161682637840abd2d271cb73dfcae575f0fc8704904fcf604", @@ -2693,26 +2867,26 @@ "TT_cardano-test_address_public_key.py::test_cardano_get_address[True-parameters7-result7]": "1c4bf364e403371d1b4570a6d7a2b8f9ac231851cbf67f56f64fe350e0edadd1", "TT_cardano-test_address_public_key.py::test_cardano_get_address[True-parameters8-result8]": "a9958771b2394aa3d6488f5d28d0516fb2632997be4ef3cfe272d8468b475e70", "TT_cardano-test_address_public_key.py::test_cardano_get_address[True-parameters9-result9]": "8281c20dcf6ccadd0e0a0db53adb4641706dbcb48bf685762173f5645abfa39b", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[icarus-derivation]": "5dafbc14364b371d938a5dbf2dd1696958dd68fd2f10702dd512f5bd7df02602", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[icarus-trezor-derivation]": "a882c9e5ffbaca9ec0e7f0fcc275270268fa9ef3c9ed135e543ce39af912cbe5", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[ledger-derivation]": "d3c1b530540a57f50020e62cd9acfe8c2d64d8c1ecad74226e9419876ae73f16", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters0-result0]": "262022e068ac3dcbba07b3451e9b670187bce739b85cd87835b18c544ab66b10", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters1-result1]": "dd731124e63c9856d1b3ca05f5b11d922b33bd82db7535f7fa8b135213f648cb", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters10-result10]": "969a01934c3f20e57c1ef3bcccf14499c15621489b67f8e9831dd7365ae3c130", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters11-result11]": "1b397226957de70e91fe48344b86beed80d3c21fc5bd2b6c2b38c1ec6d6a35f3", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters12-result12]": "99898cc1734fd63bf4d7906f30087e77138857371109b4c58fcb0aa8d312476f", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters13-result13]": "bd9d8a62ed8e4bb8dfe479f6c14d026b41a8b5f8d9a65c21784306dc208f4cdf", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters14-result14]": "93a3099eba0179a1ac48e751a7140dedf77cfc572fcc90e8fa6c9f110ceb2b8c", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters15-result15]": "027943246f0638357411b1195bd35a2f0d9a976449a7efd3d8dbf26b8205410e", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters16-result16]": "6173e8a3a2165a0f54bc124bc505a7ce9aeefecb07cb0803fa22c7d9e2487d0a", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters2-result2]": "dde7f3743ec2e9bd7d441b2f6970ccda38c47242fd64440126ded6f61825f756", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters3-result3]": "eab03e524c6cfe6f04d0dcf1a7010faef85d62720f9d8722e6e137e7f05abc33", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters4-result4]": "9859e1419d1a4aa46be81171ec835e137184d834d5f57ef37e918061b485ca8e", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters5-result5]": "e48073cb63f1bcac2d7e5f8dcf59e3dc65edce7205eb0f410f70133d28779130", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters6-result6]": "0942bab8dc494e8527d4ddfb9411e43c43d5960feb16018cb45c5ee2c6f57770", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters7-result7]": "eb01dea0983af584be8ebedacc140c9e5d295204c7f43f604122abae3ffe8f82", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters8-result8]": "4ec3428cbcc79e37db158ec1769f751044b5d3936bd057215c2076ddc2487e21", -"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters9-result9]": "5598cb3d539a85ee72edbfb70583b43b236e4638662de28bfb642bdd93a6fe59", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[icarus-derivation]": "9f1c380c7c16103993990d8c67d483e96036fae1ae6aac5b5d7e39faf3c53585", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[icarus-trezor-derivation]": "49f9efb386f8d1d4664067c9295c6c9c63379e8b8e96e201083f5ccbe7d9007d", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[ledger-derivation]": "22aec7c1a44616f3f8420394c96eff63c65171eb4f147498744a36454db2ded9", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters0-result0]": "b679adce2c9f37cb9117427c43a087fbb687c0795b689a8375982be84ad91f86", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters1-result1]": "3bdc12c7cc9816788506a217abb0f8f36c3a59018f31e2dbfa732037967c0017", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters10-result10]": "1e1776dd49a26662e59364a1fe507866b8af3492980e940905f1b5459e242f97", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters11-result11]": "fd77b2c5ef23faaf49c064f82f47f0c831651d508f3be0b65a5c670444dc5742", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters12-result12]": "2d56b6f1d0251340739d5ec3c027572eac84d8ddb355af97e7f06643dddcc281", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters13-result13]": "f557c78c6269e3b2df516da21b044141a7f2b11a8c0abccddc965d07654c99f0", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters14-result14]": "1ce9a90f107f62cae480425401e19adf5660d97a04c3c0dfdf2df8c1b2fc7256", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters15-result15]": "b52f74bdb30a8a6cfd83594eed8ca77fe08bbcc89d77387bd5abef1b0e91815f", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters16-result16]": "7f3fc023865830f50f7c8bf9970c6ec8cf7db614754986ea5c9940dd95932721", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters2-result2]": "8928e2dabecb5f605bd7b9748a1db4adacf19e5fa1c2e0ab83250447f56be198", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters3-result3]": "791fae7fbcb75bfb79188249a2670c6e4751e023b20619d48babdebbc9bbb700", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters4-result4]": "0f95bec3490c7b3cbbdc2b91e21c07a40359ad04bafe08d9fe021d3444aafb5d", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters5-result5]": "c62445356a832d203800c4497c022d2d91da151a531702d045a0f20ee7a949a0", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters6-result6]": "b8b52d7d09f3d80c7bc87eeb70b825baef6c6929c951af528321bd989c269a30", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters7-result7]": "3b031e0661d2cd7f6ca871e05342ed8f40663df176b9933c6f6a2d492ceb543f", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters8-result8]": "7ae58b4b695d0d88a8f0e9aea1aeb9afc736c6ef660f6df874949063ef2337fc", +"TT_cardano-test_address_public_key.py::test_cardano_get_public_key[parameters9-result9]": "640d7ecbbb62bda60634765f379fd19b9bf97a1b09f8b15a7e33aa87d8fbc154", "TT_cardano-test_derivations.py::test_bad_session": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_cardano-test_derivations.py::test_derivation_irrelevant_on_slip39[CardanoDerivationType.ICA-3b0af713": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_cardano-test_derivations.py::test_derivation_irrelevant_on_slip39[CardanoDerivationType.ICARUS]": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", @@ -3009,12 +3183,12 @@ "TT_ethereum-test_getaddress.py::test_getaddress[parameters1-result1]": "3aa63c89a2693aa70258f3a46d9aad78d5926a8af63d6aebbc37a2644cbd80ef", "TT_ethereum-test_getaddress.py::test_getaddress[parameters2-result2]": "2a03d5555eebb9b3cc7e78e0bf79d5c8b3ff991c1afb5db5078fb8f2ec486fd4", "TT_ethereum-test_getaddress.py::test_getaddress[parameters3-result3]": "cc46d5ffd339b76744cc822e950b4f9252b993316f36fa04a6fd371424c8dff4", -"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[ETC]": "2737bbf908acddc26c12e3e32e2d25ba2f8af28020cefadc0dea035491cfbfb8", -"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[Ledger Live legacy path]": "6356dd6ec5475397ac62d4345195d3b9f91e0cdbcace6c9fd2067a1ef31fb44c", -"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[parameters0-result0]": "49746bdc089d8ffa036cdd209a0d99f6fe669d2397ce5a88bf30cbadfb0b5174", -"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[parameters1-result1]": "2e8db4c9cc0879db9a4339556c88118009b919bb5923f93c891f65870974fb17", -"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[parameters2-result2]": "5259d154279b4007f26e44e931550d2e1750bc09be98478d845da9cd30dcd03e", -"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[parameters3-result3]": "0d38df9e7553ab93ea60c080b5ab8a42b3809c637a4257d13db300805553bbe8", +"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[ETC]": "3533dfb55f7a8c08608b3ec16f8d839778eb309f7f6e652a86e4d4e4371bdd73", +"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[Ledger Live legacy path]": "470c03c9938d4cb40e1ad7120fde9cb68097f8c3e1f9602c06c08e4b794945f3", +"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[parameters0-result0]": "112a1759870f892e6302519baaa85018081f306c82223885c844948e9372bced", +"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[parameters1-result1]": "f5dfb133d6f10b463d71fdf95f70f9d9d20b3da0618fa6199c5ed138bcc22d40", +"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[parameters2-result2]": "5488d5a4b0c0c98d9945070a533f7f633a2b463a443db7959c6dcc3cff2b62b2", +"TT_ethereum-test_getaddress.py::test_getaddress_chunkify_details[parameters3-result3]": "7633b47615fa54019eb756b719dd563f5197269724e36d1f5c70f812d3b36ab7", "TT_ethereum-test_getpublickey.py::test_ethereum_getpublickey[Ledger Live legacy path]": "3b6c5cf5c6512f1491b77f895d21d2f850f774c2b9d67c1b76eaeb2892e95e6b", "TT_ethereum-test_getpublickey.py::test_ethereum_getpublickey[parameters0-result0]": "3b6c5cf5c6512f1491b77f895d21d2f850f774c2b9d67c1b76eaeb2892e95e6b", "TT_ethereum-test_getpublickey.py::test_ethereum_getpublickey[parameters1-result1]": "3b6c5cf5c6512f1491b77f895d21d2f850f774c2b9d67c1b76eaeb2892e95e6b", @@ -3168,9 +3342,9 @@ "TT_monero-test_getaddress.py::test_monero_getaddress[m-44h-128h-0h-4Ahp23WfMrMFK3wYL2hLWQFGt87-f8cc3746": "f04af61059a8edc28122ad708eaa23a5f27441a63a87041da5bde604f3c585c4", "TT_monero-test_getaddress.py::test_monero_getaddress[m-44h-128h-1h-44iAazhoAkv5a5RqLNVyh82a1n3-1cc72b15": "097567f203b5c8090204f894017ba1676ff3d42c42b79915960d3de701422fec", "TT_monero-test_getaddress.py::test_monero_getaddress[m-44h-128h-2h-47ejhmbZ4wHUhXaqA4b7PN667oP-d571924b": "d081197da687ed543a4c0bd5ecc65d720253fcc15d2975c26b251bd14ab647de", -"TT_monero-test_getaddress.py::test_monero_getaddress_chunkify_details[m-44h-128h-0h-4Ahp23WfMr-1e763aba": "0ac1a47b3ad0595e46a8aedae71446d39f1bcfa703cbe4d1681da7ea8c332a31", -"TT_monero-test_getaddress.py::test_monero_getaddress_chunkify_details[m-44h-128h-1h-44iAazhoAk-005b0fdf": "09f056cf1613388427d5f742910d8127acd7fdfc55d44e0ab8138e7a45852784", -"TT_monero-test_getaddress.py::test_monero_getaddress_chunkify_details[m-44h-128h-2h-47ejhmbZ4w-df40ccfd": "37e8936c92b8af777c1c6276a2c4cf2c556f6babf9ccc6ed03be990d065f64b8", +"TT_monero-test_getaddress.py::test_monero_getaddress_chunkify_details[m-44h-128h-0h-4Ahp23WfMr-1e763aba": "9ba8b589feb0778a49ee2b501d2e674d99d0029251d982a2da2dbdd09ec023ea", +"TT_monero-test_getaddress.py::test_monero_getaddress_chunkify_details[m-44h-128h-1h-44iAazhoAk-005b0fdf": "80415b80cacf042d881270e172076d6b8c4b3b5e05ac3ce168c4a1125d6331db", +"TT_monero-test_getaddress.py::test_monero_getaddress_chunkify_details[m-44h-128h-2h-47ejhmbZ4w-df40ccfd": "243310962621c01d081ac2fa3cdae25596ca9c3095decae0cb14eca30e697ea0", "TT_monero-test_getwatchkey.py::test_monero_getwatchkey": "132fcbf2f28d368ed826f8ea2bd096572fc9807c6cc9be8f6ada25847b0a0adb", "TT_nem-test_getaddress.py::test_nem_getaddress[False]": "a7871f1b2e8d835fd40f378b1bdfcadaede0556bd5295ab174748527db53fd82", "TT_nem-test_getaddress.py::test_nem_getaddress[True]": "f1eb22b7aee59711693245c866324c9780aacd340c54d2d99c41a69151ba267f", @@ -3185,7 +3359,7 @@ "TT_nem-test_signtx_others.py::test_nem_signtx_provision_namespace": "fc31c7e1a668c8c6db13cef90baf722fc1f7009b81d6314c489274718ab127b1", "TT_nem-test_signtx_transfers.py::test_nem_signtx_encrypted_payload": "bf8456472807daee3ada2c82e131cb58db47a6fc483dc917f9dbdd1cc2df5024", "TT_nem-test_signtx_transfers.py::test_nem_signtx_known_mosaic": "b09915631506661437de4ef2bc4831054b0c2526310cb48a1f7fd60574de102a", -"TT_nem-test_signtx_transfers.py::test_nem_signtx_known_mosaic_with_levy": "0eeb3d15ebfdd4c8d6a60d5b7e2faa19b22e812400988e29c3e68ce8758fb081", +"TT_nem-test_signtx_transfers.py::test_nem_signtx_known_mosaic_with_levy": "73769ad30f70cbd3288cf59b076fc12bb838fd5a23aefbb804e66a25a6d526b5", "TT_nem-test_signtx_transfers.py::test_nem_signtx_multiple_mosaics": "8838c998cbe837aff4d6f7f63c4f5f0dcc1cadfa9aae6d6486262a7afd691ea4", "TT_nem-test_signtx_transfers.py::test_nem_signtx_simple[False]": "d3c8d6656a581e845b54fdca883496388ffa77766892fc5b4bb9e04e1e765651", "TT_nem-test_signtx_transfers.py::test_nem_signtx_simple[True]": "912291c884e71187005c2356c87c724dbd83a706bed2f28e2a6ff0226c1d9561", @@ -3228,30 +3402,30 @@ "TT_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[2]": "aea2334f29f8f4af160e238d79cac41e1b3b33feb08b71c2ab0cd867b8e8a20b", "TT_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_dryrun": "5f3413af77cc76413fe1ec3e00e17850f8de504499b5bf9d753ed4d9351931e5", "TT_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_invalid_seed_dryrun": "2b866e19489d98b9924398080478e2126fe9769b057ea725e7ba795584adb1ad", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "1d46c84c1bbc532521f33155eff9c534bf335853e4a436ab68b167ef14addac4", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-bac-f67baa1c": "a2ed9f143418b282b3f655aa8ecad317065d64c8ebc2b912ced8d1a3eca80d76", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup-6348e7fe": "7b169c941b361c7ceb44cf5032dbdfbb4dd3bd84cfbc3a7d93819f07cd71326f", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Bip39-backup_flow_bip39]": "d770f7b6618b03bcd9639724ad1cc3e20bb2c6ce1abdd43f13f311fde0c276dc", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Advanced-backup-dcbda5cf": "371ea1afeb1a166a57c34bcb6d6454aa319d8eed68e301f315af82fe81833b4f", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Basic-backup_fl-1577de4d": "b63de052adfc67d7e9d4455e495da6b714a52aeed488676c7bc17cbfb85a2631", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "965dcb5a1cbc4c61790e3ed9cef705962d2ed24046c5a5733e64faf5c8bce543", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-bac-f67baa1c": "ebb906d66f842651cc25cda333d71ddcca0d1fdf023f9a66cf026482bfe95a8d", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup-6348e7fe": "4bae92f24e04731197ab0a7051458551ea041bcf483c8c3b740203f9fbb84564", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Bip39-backup_flow_bip39]": "389ae72b9576b5d8c317fd8fad2656db72c9397ed52014e722aa26048587518f", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Advanced-backup-dcbda5cf": "2655a385df8ae04636f9193f3aea9078963aaa00c24c56d446f56faa71ad74ad", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Basic-backup_fl-1577de4d": "20f6295e76a45bce8679780c2eb93f99e7c2cf2e59db7afa81b53ad9a997cdab", "TT_reset_recovery-test_reset_bip39_t2.py::test_already_initialized": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_reset_recovery-test_reset_bip39_t2.py::test_failed_pin": "4e68c6705ca629cd19877fc3befe68c676aa014ad37d910dafdeecbf22843517", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device": "4975f882e9ff5150b0e7f38109e7f83f2266fc4548756fa721bf30d67e702381", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "ff3d3f9bdd26970eca48de2b5ddfa56c3eb85e2c1d25f27a260e027b2af27a7a", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "5d2dd0fc48285d9d724958b9efe1ba615c1247ebf06328e11ea67f096b4da8e8", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "c1219269bdba6e35a988d3928a4d99799a5289fe7147127112e1ffe8931944a3", -"TT_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "bef49d3199d13e7f74302d4d0695321457e91c211057c795cadd13940d97213f", -"TT_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "d0f34a8b91c7604dcf853a8b73b1ba2264f30fb4b3c1c2d9a03e36710e177043", -"TT_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "a943523f2275787db3aca5265510dd028bd87137f251025ebf3c820abff6ffa0", -"TT_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "0da1b39340ae0b521de536c4d2b477663e548bdc27ea7c4b80094acedfdb9b68", -"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "a5dd9435a3a99f1ea4d6bab845065f1552d447ae5692b39211ec4a7330f2520d", -"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "1f35326ce16fe3316dfe35a1e0fe218e22852855df9bdbc008ca519849f0cf6f", +"TT_reset_recovery-test_reset_bip39_t2.py::test_failed_pin": "550ef3ed1a280dfee383119ee41329956c47ae38162721cd3ad53f00d8423937", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device": "9aa728ab73a36dbe45d91e6406e5e9543ba1627565a901ffded3fe3f4b2031d5", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "7927c0cf3eacd16033590e38382618c408d5c67b9b589161de73aa1a0bacec12", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "f04dc9251b9eea9dec562072972822617f1449f33bee4f38f4720ba9e1ddc027", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "80545ae63bc78a9b2268caf0d19e5d5c0f9073db7b5d92406c738244feed70ee", +"TT_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "20af3c7e83478b63e469cde6654119d3de7aa860363ef14aa120b97a1a8475e0", +"TT_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "ddf840544d8580d6b057047e9880267670cbe967e5e2ec9817c0c8689b265238", +"TT_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "d90e3bf0498d1f8db2ebc99dd2d516f0f7b532627c7a48ef76a96aa8c844786f", +"TT_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "7fb8ebc2b2809bec7cb5ed0cb043d2d5f154d2051936427d20503f1f9f7d66f9", +"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "68920bb747d646fe5f274a73a64935f4c79a1974c56b1ab5e87a949d1db8cc89", +"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "192a0d8f6306929e294266aa8ce91e7c805511673ed2982538005fd74c131086", "TT_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-0h-0-0-rNaqKtKrMSwpwZSzRckPf-3321e5d1": "e1714dd0d8e33bc7d38c8a8f0a24082c89846497a98fcfec5136743007da9171", "TT_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-0h-0-1-rBKz5MC2iXdoS3XgnNSYm-fd75b415": "6b8a98bb658c617c435cf541e35c1fdb055f86c983add4df01b714836239c1b8", "TT_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-1h-0-0-rJX2KwzaLJDyFhhtXKi3h-af5daf0f": "f2b9355b112c77ea457568adb7375dd8f2338f5170ba26758751ce3f8cee285b", -"TT_ripple-test_get_address.py::test_ripple_get_address_chunkify_details[m-44h-144h-0h-0-0-rNaq-38238cf8": "5eb57d3f6b2ecee825566767d7216480b425ce5eefc691e2f4c510fc1db68b83", -"TT_ripple-test_get_address.py::test_ripple_get_address_chunkify_details[m-44h-144h-0h-0-1-rBKz-dd520dc8": "7523f86df84554313e5139bafbed7155f1259ec4e201b4c6072d614f08d430f5", -"TT_ripple-test_get_address.py::test_ripple_get_address_chunkify_details[m-44h-144h-1h-0-0-rJX2-1534f649": "58242541bf8486a102aa87376f2d9a8f2fee74c6956631501155aa5a92ef742f", +"TT_ripple-test_get_address.py::test_ripple_get_address_chunkify_details[m-44h-144h-0h-0-0-rNaq-38238cf8": "e9e53172a3d76542153f25bf1a0939af194ba5c04c614fcf544ff6e170579999", +"TT_ripple-test_get_address.py::test_ripple_get_address_chunkify_details[m-44h-144h-0h-0-1-rBKz-dd520dc8": "042d4718ad2b19bdb1f15081f110a5c18bdbe86142b7c847ce67fdb56752a531", +"TT_ripple-test_get_address.py::test_ripple_get_address_chunkify_details[m-44h-144h-1h-0-0-rJX2-1534f649": "3922680a2fe77e17768cc6aec701273b4a47b4c1fb38052526f71b4916317db2", "TT_ripple-test_get_address.py::test_ripple_get_address_other": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_ripple-test_sign_tx.py::test_ripple_sign_invalid_fee": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_ripple-test_sign_tx.py::test_ripple_sign_simple_tx[False]": "bc15baef3f52f919dba24b46d828b63eb5ad15dcb0660468e9e17088e1523df4", @@ -3337,16 +3511,16 @@ "TT_stellar-test_stellar.py::test_get_address[parameters7-result7]": "da742ffb1db1bb3430f6ea64f9d50813d41b15762969ffa4172d48ed62f0aa29", "TT_stellar-test_stellar.py::test_get_address[parameters8-result8]": "e16b547bc1bcb9e11c8785fdc98514a54d53c2b2226e0778cd4b59146ba6ecb8", "TT_stellar-test_stellar.py::test_get_address[parameters9-result9]": "4d4a0d45419529aaeadf4cf7ee391f26d15acf586b7f11212ff1a7a0b09c389b", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters0-result0]": "34490052e6eee68a9e9c732686461a4fe7f956691feae677f31df6d9054be475", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters1-result1]": "353c7dfdeef8d68454e8b9026212b52fb761864b4c5ddefa17ea973cd0e73e7a", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters2-result2]": "a23d222bbd77d3b199bf73f6c60f5bfae48a45a913b62579e6934df142ff5a4e", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters3-result3]": "3c3146d203c179bacb4d389330a6b29077359e53e6ae72f443e5efef3d841cc5", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters4-result4]": "f3bada923b206539ad960d369d3a28fdd0500d106ea079714f41add5be37543b", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters5-result5]": "9d53cf2c0aa7cd569e613edb9cc710a366e7f152264cf24042009cfa65f83452", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters6-result6]": "06c44f296124b25d22690d9a8dc5f8ac7421db139dfce0dd2e2f87762e65198b", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters7-result7]": "2ae8078b3f39e1f930a1a7c0750d863a8ae9edfad2bdf32b57d63b729fb7fb1c", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters8-result8]": "be46f409ac67fbc359df19ad6d188ab31062f7d89b94011e1788fd4d5893640b", -"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters9-result9]": "a7172a64a86555942540d9eef80042a3051d4c58e1f8cf7a39e9c99987830e27", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters0-result0]": "6580e080272fb8b9f7310e4263e16e6587f67c9a32755b67003b35a6fe8ea74b", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters1-result1]": "a4ac57f6ef4679680d1fe313f3d391bd900cfd0b3574d79aef54b06cef9f000b", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters2-result2]": "8d13be81e6b5389031c219fd10eaedaf39848132cc9cf8f4579d0fafc51cf980", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters3-result3]": "9441272a1d0d32534233109331a040f34d72ce44fdc623dc55efc7bb92e73e8b", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters4-result4]": "1db8859b2a2632c5684f0a1d6d408bb0b2d16d750db6613a378fd864e5b20bf2", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters5-result5]": "a80c0fcea5b6a41468fae9ed0b9faa15abe982a9c0796dc2f5df4e04c8840bdd", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters6-result6]": "b85c825a92869561e8b5229cb4d3215ea4ad64c5bf9189348caa291468aa7a8e", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters7-result7]": "567a23da78f1b75505cb4b24ba4277ef47800c4c59418a615d84a5fdfcecd905", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters8-result8]": "4434ca30855cd31c5361153333c63ea735345a1c11dc7e50621eb28cfa3f0df0", +"TT_stellar-test_stellar.py::test_get_address_chunkify_details[parameters9-result9]": "7d2d7e2d9778505692458fada12c6f82e3b221ba0d575ed7346ced7928c254be", "TT_stellar-test_stellar.py::test_sign_tx[StellarAccountMergeOp]": "84e2e99c4c0dbba1f2e44a2f833742dc37f88013972c8ffd0d02673660eb694f", "TT_stellar-test_stellar.py::test_sign_tx[StellarAllowTrustOp-allow]": "a3de7fcd5cad8c5e4b4316fedde7dc7862eb89ca2f2a8d1a9ba8fb50a068342c", "TT_stellar-test_stellar.py::test_sign_tx[StellarAllowTrustOp-revoke]": "d75e62e491d87be5d1317e51241431bc71b5a24da21eeb31dbf5a10e929290dc", @@ -3407,7 +3581,11 @@ "TT_test_debuglink.py::test_softlock_instability": "55cb4cbeec68bd8ccee460034677cf8053f8f688d5c3559f360c38a205b34e37", "TT_test_firmware_hash.py::test_firmware_hash_emu": "2a63f0bd10ba99e223f571482d4af635653bb8a3bddc1d8400777ee5519bc605", "TT_test_firmware_hash.py::test_firmware_hash_hw": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_test_msg_applysettings.py::test_apply_homescreen_jpeg": "8c1b50eea75d624109182cb007b84cef591b4351d336bf867190d6d772874587", +"TT_test_language.py::test_change_language_errors": "89093601d6f9bf87497a498928408533eefeb72067a153451e158df86280cac3", +"TT_test_language.py::test_full_language_change": "05466d8f64a4daa51261e8b7fe4d6eeff789db3ef0a74a6ecd4be135b17b00f4", +"TT_test_language.py::test_language_stays_after_wipe": "bc8f871e761bad4d648509f449ac3698ea3b7666792ef3b70e816675e3beda5f", +"TT_test_language.py::test_translations_renders_on_screen": "37a3e557bbeb1f0e1defec02b0385c94144f03371e930ba4bf7d736c75df2bad", +"TT_test_msg_applysettings.py::test_apply_homescreen_jpeg": "0e2a71ac60add6bebc03645b6f4f6f19abdba56f1e55e799479b116e5a66aef6", "TT_test_msg_applysettings.py::test_apply_homescreen_jpeg_progressive": "4d7c67024ee17436071e5cf2f79c36453249c95314a732580623cb1f1cdbfdf3", "TT_test_msg_applysettings.py::test_apply_homescreen_jpeg_wrong_size": "4d7c67024ee17436071e5cf2f79c36453249c95314a732580623cb1f1cdbfdf3", "TT_test_msg_applysettings.py::test_apply_homescreen_toif": "4d7c67024ee17436071e5cf2f79c36453249c95314a732580623cb1f1cdbfdf3", @@ -3418,24 +3596,24 @@ "TT_test_msg_applysettings.py::test_experimental_features": "523f74db7f660c261507dfdd92285981869af72c9ba391c4dfedb3f06ccf40ad", "TT_test_msg_applysettings.py::test_label_too_long": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_test_msg_applysettings.py::test_safety_checks": "b129e0ba2e3c7ac836b3bb4b0bc8ded1c922eba054b42b434d10499fc780ea2b", -"TT_test_msg_backup_device.py::test_backup_bip39": "860d55611c229f75c4dbaac5065a92ffbc64ad7e83a7352bda093b036e095594", -"TT_test_msg_backup_device.py::test_backup_slip39_advanced[click_info]": "6867126ffa5a364c6f1360c80fc10b3b43907910fdf700f305e3ff6b3165227d", -"TT_test_msg_backup_device.py::test_backup_slip39_advanced[no_click_info]": "f34fc345799822bc890d9815c9b0522eb8e97073dafdbb0ba4071f4c7ac3623f", -"TT_test_msg_backup_device.py::test_backup_slip39_basic[click_info]": "8954ce3541ec843d5161193d4c64520869e97eedef85c4852ae7101957328af1", -"TT_test_msg_backup_device.py::test_backup_slip39_basic[no_click_info]": "821e75f63ca4589edb7934f3f2074ae58cb97ab9c0240e153d35c79ddb605ec2", +"TT_test_msg_backup_device.py::test_backup_bip39": "3b90a1c3383b3afa2c321109e1088b2e33d976a9f75db3b013456d52c85ae5d2", +"TT_test_msg_backup_device.py::test_backup_slip39_advanced[click_info]": "2738a2e8fb890f7f2f5857cd9af0d3f6eac9fbf71dc299c829b1ba6a1209b712", +"TT_test_msg_backup_device.py::test_backup_slip39_advanced[no_click_info]": "503c667f02e099102be7c9bf14e771b5e27f41eabebdf7269389c9d9b33016ae", +"TT_test_msg_backup_device.py::test_backup_slip39_basic[click_info]": "65b98eb420a3b4a5128dc686f491a55b460205db7e1e1bb2828a3d6998af5466", +"TT_test_msg_backup_device.py::test_backup_slip39_basic[no_click_info]": "a3c26090183f9b150cd0caccfa2a4ba9833de718868777cc1e6f255ddda8a94f", "TT_test_msg_backup_device.py::test_interrupt_backup_fails": "ae147498028d68aa71c7337544e4a5049c4c943897f905c6fe29e88e5c3ab056", "TT_test_msg_backup_device.py::test_no_backup_fails": "fada9d38ec099b3c6a4fd8bf994bb1f3431e40085128b4e0cd9deb8344dec53e", "TT_test_msg_backup_device.py::test_no_backup_show_entropy_fails": "001377ce61dcd189e6a9d17e20dcd71130e951dc3314b40ff26f816bd9355bdd", -"TT_test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "e3c28a6963f167c8fd79ceb3cb3949cd66dfd08f3dadb8b093f7cef2b5e9461e", -"TT_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "941595955f00b7c66776f691420e85330b8fa3a3c9fc40763112d1064b3d61a4", -"TT_test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "a98ec36a1945f91956ca63a558206d21a5bbf714dfe420f1404d1a5df634a9c3", -"TT_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "1262a187a6e1d3c3e7b6fd189942e2077900c0f396d1a3fc94336399a07b8e83", -"TT_test_msg_changepin_t2.py::test_change_failed": "ea48323d084dea7cf806b6dfa492153bbdffed9a1f08779ab19b61a7b2237407", +"TT_test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "1ca11dd6749f7640e421b540f77b7ad9317ff14c2279bd2f45742d5e4e9935f2", +"TT_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "db9ffa189464d1c0ba7d3ea2533306e25e6895355083066c2b6f9b19efb54406", +"TT_test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "e1dd298f58bd521f9be32b247ba76206bc75ffcbea6b950c3b4c505b785959e0", +"TT_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "f478a15ed2dfde070953b1f14c6054452b0ac27ef8ab5cf9c1ea233a43676a32", +"TT_test_msg_changepin_t2.py::test_change_failed": "1526ada2ec21dc5219ac42698a1d6a9314b11f79adf37465d226ffb37b603124", "TT_test_msg_changepin_t2.py::test_change_invalid_current": "e9925836465ef35c3d5364e90780626274a92a1123451b572b9ea11a9cd2180a", "TT_test_msg_changepin_t2.py::test_change_pin": "256483cab21191ee110281e9f547c3fa08968f4d49fa38896f8141f5b8eb4701", "TT_test_msg_changepin_t2.py::test_remove_pin": "6a606987a97b9d50d991288f1222adf2c819846076b2014e91909c9907a0e427", -"TT_test_msg_changepin_t2.py::test_set_failed": "962ba410d395e37ad341b818c026b134891040617e3f9368c4fcaaa9b4a27efe", -"TT_test_msg_changepin_t2.py::test_set_pin": "ea4ca29597f29be74fedfee2387f00e3433e487dad78529ccc65f00df3231644", +"TT_test_msg_changepin_t2.py::test_set_failed": "8102f364e48feb5a3bf9eca5435135ba955fca4c67b05e5fd543a662c5734d2c", +"TT_test_msg_changepin_t2.py::test_set_pin": "001a60864717683c3edfcfaaf36adcf54a3e7459307ae8198802280657b2a918", "TT_test_msg_loaddevice.py::test_load_device_1": "1d1bdf29f677492956efe2ab29ab2481bbb647ac14cef9cfb5d8f62370133b1a", "TT_test_msg_loaddevice.py::test_load_device_2": "b9c7333adbec293167ba17f4d7306422bc6d6247c22774a8b3e7b5f1d957f062", "TT_test_msg_loaddevice.py::test_load_device_slip39_advanced": "1d1bdf29f677492956efe2ab29ab2481bbb647ac14cef9cfb5d8f62370133b1a", @@ -3494,8 +3672,8 @@ "TT_test_session_id_and_passphrase.py::test_session_with_passphrase": "9b2d4b75a4c96cc3ab52056d4b4bc83a38d948bb35aaf422829b05c43ac0cf14", "TT_tezos-test_getaddress.py::test_tezos_get_address[m-44h-1729h-0h-tz1Kef7BSg6fo75jk37WkKRYSnJ-80986d6e": "e956201e6a960c53fee449e29be2a3094d10bfd7ea3956b0c356aebc90891f60", "TT_tezos-test_getaddress.py::test_tezos_get_address[m-44h-1729h-1h-tz1ekQapZCX4AXxTJhJZhroDKDY-1a82407d": "b8006f9f20dbe6b437522702ea1068b15b3320909f8d6d86eed47c700b4d9bed", -"TT_tezos-test_getaddress.py::test_tezos_get_address_chunkify_details[m-44h-1729h-0h-tz1Kef7BSg-3b56caf9": "f911f2fcc0813237d78c7c59a4237058aec8f8066116703ed4402b70ae802569", -"TT_tezos-test_getaddress.py::test_tezos_get_address_chunkify_details[m-44h-1729h-1h-tz1ekQapZC-aca49b5f": "f686330e653bbe1d12bd05b896a9f6fd60b10f9ec1bb2e56cfd8de5e9b7f2941", +"TT_tezos-test_getaddress.py::test_tezos_get_address_chunkify_details[m-44h-1729h-0h-tz1Kef7BSg-3b56caf9": "98b45e00a77e6051e682d43b1178da8ac77c1f8ca9ef3aea933c17bf5a03934f", +"TT_tezos-test_getaddress.py::test_tezos_get_address_chunkify_details[m-44h-1729h-1h-tz1ekQapZC-aca49b5f": "51808c15b35650976f9c72727d10324576551d3099289c7c86747785d5325792", "TT_tezos-test_getpublickey.py::test_tezos_get_public_key": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_tezos-test_sign_tx.py::test_tezos_kt_remove_delegation": "842b1dba8dedf7ee6a2c6ce15305dda05b249d7a1cd4ad2854a4a2dead2657d4", "TT_tezos-test_sign_tx.py::test_tezos_sign_tx_delegation": "2c89c52a396e7aa8aee9b5216307b8aa89fd613d2734cbcad6200f95a614b418", @@ -3531,7 +3709,7 @@ "TT_test_shamir_persistence.py::test_recovery_multiple_resets": "3059811fea05eaaebf5f7ef6475b6bda9a166b3d519cb803e6172992a14fa7d8", "TT_test_shamir_persistence.py::test_recovery_on_old_wallet": "219b0822432c9dbc51a6106b901b950aa97c6ac0b7dde8de4b8e951d891c2148", "TT_test_shamir_persistence.py::test_recovery_single_reset": "dcee1170a05d710d297f835b6bc15085b33113bc11e04397254f2e7ae5e284f1", -"TT_test_wipe_code.py::test_wipe_code_activate_core": "fe824c107a2c6302e3e2cc8416dbdaa8ecd06c91364524172b162ed6d3a7916a" +"TT_test_wipe_code.py::test_wipe_code_activate_core": "1817eaf41ab1299d8c8deb39c0c7d1d4eb3ea74cc599fc5b6f40494f74c567fc" } } } diff --git a/tests/ui_tests/reporting/common.py b/tests/ui_tests/reporting/common.py index 6001d333d..8b4c9770a 100644 --- a/tests/ui_tests/reporting/common.py +++ b/tests/ui_tests/reporting/common.py @@ -215,7 +215,7 @@ def _get_unique_differing_screens( current_screens_path = get_screen_path(test_name) if not current_screens_path: current_screens_path = MASTER_CACHE_DIR / "empty_current_screens" - current_screens_path.mkdir() + current_screens_path.mkdir(exist_ok=True) # Saving all the images to a common directory # They will be referenced from the HTML files diff --git a/tools/check-bitcoin-only b/tools/check-bitcoin-only index 4651bec8a..f4e15af2a 100755 --- a/tools/check-bitcoin-only +++ b/tools/check-bitcoin-only @@ -7,6 +7,8 @@ EXCEPTIONS+=( "omni" ) # OMNI is part of the bitcoin app # BIP39 or SLIP39 words that have "dash" in them EXCEPTIONS+=( "dash" ) EXCEPTIONS+=( "confirm_ethereum_tx" ) # is model-specific, so is in layout/__init__.py instead of ethereum/layout.py +EXCEPTIONS+=( "__" ) # ignoring the translations blob (section__key delimiter) +EXCEPTIONS+=( "{}" ) # ignoring the translations blob (template identifier) GREP_ARGS=() for exception in "${EXCEPTIONS[@]}"; do