fix(core): use translation strings instead of literals
What changed, and why it matters
This commit replaces hard-coded English text strings in the Bluetooth pairing user interface with references to the device's translation system. It does not change what the device does, only how the text is loaded, so it can be translated into other languages later. There is no security bug being fixed here.
No security action required. Treat as a normal localization/code-quality change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors UI literals into TranslatedString (TR) keys for the Eckhart, Bolt, Caesar, and Delizia layouts. It adds new translation keys such as ble__pairing_match, ble__pairing_title, thp__continue_on_host, thp__pair_name, and thp__pair_new_device, updates generated Rust/MicroPython bindings, and adjusts show_pairing_device_name to accept a parameterized description. The previous literal ’ is your Trezor’s name.’ is replaced by interpolation of TR::thp__pair_name. No cryptographic, authorization, or input-validation logic is modified.
Changed components
core/src/apps/management/ble/pair_new_device.pycore/embed/rust/src/ui/layout_eckhart/ui_firmware.rscore/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rscore/embed/rust/src/ui/api/firmware_micropython.rscore/embed/rust/src/translations/generated/translated_string.rscore/translations/en.jsoncore/translations/order.jsoncore/translations/signatures.jsonInspect captured patch +106 / −16
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index f011594a..5e70b5ad 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -187,6 +187,8 @@ static void _librust_qstrs(void) {
MP_QSTR_ble__manage_paired;
MP_QSTR_ble__pair_new;
MP_QSTR_ble__pair_title;
+ MP_QSTR_ble__pairing_match;
+ MP_QSTR_ble__pairing_title;
MP_QSTR_ble__unpair_all;
MP_QSTR_ble__unpair_current;
MP_QSTR_ble__unpair_title;
@@ -843,9 +845,12 @@ static void _librust_qstrs(void) {
MP_QSTR_thp__connect;
MP_QSTR_thp__connect_app;
MP_QSTR_thp__connect_title;
+ MP_QSTR_thp__continue_on_host;
MP_QSTR_thp__nfc_text;
MP_QSTR_thp__pair;
MP_QSTR_thp__pair_app;
+ MP_QSTR_thp__pair_name;
+ MP_QSTR_thp__pair_new_device;
MP_QSTR_thp__pair_title;
MP_QSTR_thp__qr_title;
MP_QSTR_time_ms;
diff --git a/core/embed/rust/src/translations/generated/translated_string.rs b/core/embed/rust/src/translations/generated/translated_string.rs
index e13792b0..6b88b433 100644
--- a/core/embed/rust/src/translations/generated/translated_string.rs
+++ b/core/embed/rust/src/translations/generated/translated_string.rs
@@ -1537,6 +1537,11 @@ pub enum TranslatedString {
thp__nfc_text = 1145, // {"Bolt": "", "Caesar": "", "Delizia": "", "Eckhart": "Keep your Trezor near your phone to complete the setup."}
thp__pair_title = 1146, // {"Bolt": "", "Caesar": "", "Delizia": "", "Eckhart": "Before you continue"}
thp__qr_title = 1147, // {"Bolt": "", "Caesar": "", "Delizia": "", "Eckhart": "Scan QR code to pair"}
+ ble__pairing_match = 1148, // "Pairing code match?"
+ ble__pairing_title = 1149, // "Bluetooth pairing"
+ thp__continue_on_host = 1150, // {"Bolt": "", "Caesar": "", "Delizia": "", "Eckhart": "Continue on host"}
+ thp__pair_name = 1151, // {"Bolt": "", "Caesar": "", "Delizia": "", "Eckhart": "{0} is your Trezor's name."}
+ thp__pair_new_device = 1152, // {"Bolt": "", "Caesar": "", "Delizia": "", "Eckhart": "Pair with new device"}
}
impl TranslatedString {
@@ -3490,6 +3495,32 @@ impl TranslatedString {
(Self::thp__qr_title, ""),
#[cfg(feature = "layout_eckhart")]
(Self::thp__qr_title, "Scan QR code to pair"),
+ (Self::ble__pairing_match, "Pairing code match?"),
+ (Self::ble__pairing_title, "Bluetooth pairing"),
+ #[cfg(feature = "layout_bolt")]
+ (Self::thp__continue_on_host, ""),
+ #[cfg(feature = "layout_caesar")]
+ (Self::thp__continue_on_host, ""),
+ #[cfg(feature = "layout_delizia")]
+ (Self::thp__continue_on_host, ""),
+ #[cfg(feature = "layout_eckhart")]
+ (Self::thp__continue_on_host, "Continue on host"),
+ #[cfg(feature = "layout_bolt")]
+ (Self::thp__pair_name, ""),
+ #[cfg(feature = "layout_caesar")]
+ (Self::thp__pair_name, ""),
+ #[cfg(feature = "layout_delizia")]
+ (Self::thp__pair_name, ""),
+ #[cfg(feature = "layout_eckhart")]
+ (Self::thp__pair_name, "{0} is your Trezor's name."),
+ #[cfg(feature = "layout_bolt")]
+ (Self::thp__pair_new_device, ""),
+ #[cfg(feature = "layout_caesar")]
+ (Self::thp__pair_new_device, ""),
+ #[cfg(feature = "layout_delizia")]
+ (Self::thp__pair_new_device, ""),
+ #[cfg(feature = "layout_eckhart")]
+ (Self::thp__pair_new_device, "Pair with new device"),
];
#[cfg(feature = "micropython")]
@@ -3580,6 +3611,8 @@ impl TranslatedString {
(Qstr::MP_QSTR_ble__manage_paired, Self::ble__manage_paired),
(Qstr::MP_QSTR_ble__pair_new, Self::ble__pair_new),
(Qstr::MP_QSTR_ble__pair_title, Self::ble__pair_title),
+ (Qstr::MP_QSTR_ble__pairing_match, Self::ble__pairing_match),
+ (Qstr::MP_QSTR_ble__pairing_title, Self::ble__pairing_title),
(Qstr::MP_QSTR_ble__unpair_all, Self::ble__unpair_all),
(Qstr::MP_QSTR_ble__unpair_current, Self::ble__unpair_current),
(Qstr::MP_QSTR_ble__unpair_title, Self::ble__unpair_title),
@@ -4859,9 +4892,12 @@ impl TranslatedString {
(Qstr::MP_QSTR_thp__connect, Self::thp__connect),
(Qstr::MP_QSTR_thp__connect_app, Self::thp__connect_app),
(Qstr::MP_QSTR_thp__connect_title, Self::thp__connect_title),
+ (Qstr::MP_QSTR_thp__continue_on_host, Self::thp__continue_on_host),
(Qstr::MP_QSTR_thp__nfc_text, Self::thp__nfc_text),
(Qstr::MP_QSTR_thp__pair, Self::thp__pair),
(Qstr::MP_QSTR_thp__pair_app, Self::thp__pair_app),
+ (Qstr::MP_QSTR_thp__pair_name, Self::thp__pair_name),
+ (Qstr::MP_QSTR_thp__pair_new_device, Self::thp__pair_new_device),
(Qstr::MP_QSTR_thp__pair_title, Self::thp__pair_title),
(Qstr::MP_QSTR_thp__qr_title, Self::thp__qr_title),
(Qstr::MP_QSTR_tutorial__continue, Self::tutorial__continue),
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index d4a88cf2..639ede92 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -984,8 +984,9 @@ extern "C" fn new_show_pairing_device_name(
kwargs: *mut Map,
) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
+ let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let device_name: TString = kwargs.get(Qstr::MP_QSTR_device_name)?.try_into()?;
- let layout = ModelUI::show_pairing_device_name(device_name)?;
+ let layout = ModelUI::show_pairing_device_name(description, device_name)?;
let layout_obj = LayoutObj::new_root(layout)?;
Ok(layout_obj.into())
};
@@ -1930,6 +1931,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// def show_pairing_device_name(
/// *,
+ /// description: str,
/// device_name: str,
/// ) -> LayoutObj[UiResult]:
/// """Pairing device: first screen (device name).
diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
index 6a6746ee..a7b6977d 100644
--- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs
@@ -951,6 +951,7 @@ impl FirmwareUI for UIBolt {
}
fn show_pairing_device_name(
+ _description: StrBuffer,
_device_name: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelUI>, Error>(Error::NotImplementedError)
diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
index 0f4b8432..982e0b0d 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -1148,6 +1148,7 @@ impl FirmwareUI for UICaesar {
}
fn show_pairing_device_name(
+ _description: StrBuffer,
_device_name: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelUI>, Error>(Error::NotImplementedError)
diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
index 03fdc62c..842b0cf9 100644
--- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -1033,6 +1033,7 @@ impl FirmwareUI for UIDelizia {
}
fn show_pairing_device_name(
+ _description: StrBuffer,
_device_name: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelUI>, Error>(Error::NotImplementedError)
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
index ed376c70..746cc7d6 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
@@ -258,7 +258,7 @@ impl DeviceMenuScreen {
let is_connected = connected_idx.is_some_and(|idx| idx < paired_devices.len());
let connected_subtext: Option<TString<'static>> =
- is_connected.then_some("1 device connected".into());
+ is_connected.then_some(TR::words__connected.into());
let mut submenu_indices: Vec<usize, MAX_PAIRED_DEVICES> = Vec::new();
for (i, device) in paired_devices.iter().enumerate() {
diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
index 4bce48f6..e58c29a1 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs
@@ -1231,19 +1231,28 @@ impl FirmwareUI for UIEckhart {
}
fn show_pairing_device_name(
+ description: StrBuffer,
device_name: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
- let font = fonts::FONT_SATOSHI_REGULAR_38;
let text_style = theme::firmware::TEXT_REGULAR;
+ let font = text_style.text_font;
let mut ops = OpTextLayout::new(text_style);
- let text: TString = " is your Trezor's name.".into();
- ops.add_color(theme::GREEN)
- .add_text_with_font(device_name, font)
- .add_color(text_style.text_color)
- .add_text_with_font(text, font);
+ for part in interpolate::parse(description) {
+ match part {
+ interpolate::Item::Text(s) => {
+ ops.add_color(text_style.text_color);
+ ops.add_text_with_font(s, font);
+ }
+ interpolate::Item::Arg(0) => {
+ ops.add_color(theme::GREEN);
+ ops.add_text_with_font(device_name, font);
+ }
+ _ => return Err(Error::OutOfRange),
+ };
+ }
let screen = TextScreen::new(FormattedText::new(ops))
- .with_header(Header::new("Pair with new device".into()).with_close_button())
- .with_action_bar(ActionBar::new_text_only("Continue on host".into()));
+ .with_header(Header::new(TR::thp__pair_new_device.into()).with_close_button())
+ .with_action_bar(ActionBar::new_text_only(TR::thp__continue_on_host.into()));
#[cfg(feature = "ble")]
let screen = crate::ui::component::BLEHandler::new(screen, true);
let layout = RootComponent::new(screen);
diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs
index 60b594f2..34401d45 100644
--- a/core/embed/rust/src/ui/ui_firmware.rs
+++ b/core/embed/rust/src/ui/ui_firmware.rs
@@ -376,6 +376,7 @@ pub trait FirmwareUI {
) -> Result<impl LayoutMaybeTrace, Error>;
fn show_pairing_device_name(
+ description: StrBuffer,
device_name: TString<'static>,
) -> Result<impl LayoutMaybeTrace, Error>;
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index a83d68e7..965c4b74 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -643,6 +643,7 @@ def show_device_menu(
# rust/src/ui/api/firmware_micropython.rs
def show_pairing_device_name(
*,
+ description: str,
device_name: str,
) -> LayoutObj[UiResult]:
"""Pairing device: first screen (device name).
diff --git a/core/mocks/trezortranslate_keys.pyi b/core/mocks/trezortranslate_keys.pyi
index 078ce93f..57a88135 100644
--- a/core/mocks/trezortranslate_keys.pyi
+++ b/core/mocks/trezortranslate_keys.pyi
@@ -88,6 +88,8 @@ class TR:
ble__manage_paired: str = "Manage paired devices"
ble__pair_new: str = "Pair new device"
ble__pair_title: str = "Pair & connect"
+ ble__pairing_match: str = "Pairing code match?"
+ ble__pairing_title: str = "Bluetooth pairing"
ble__unpair_all: str = "Unpair all bluetooth devices"
ble__unpair_current: str = "Unpair connected device"
ble__unpair_title: str = "Unpair"
@@ -953,9 +955,12 @@ class TR:
thp__connect: str = "Allow {0} to connect with this Trezor?"
thp__connect_app: str = "Allow {0} on {1} to connect with this Trezor?"
thp__connect_title: str = "Connection dialog"
+ thp__continue_on_host: str = "Continue on host"
thp__nfc_text: str = "Keep your Trezor near your phone to complete the setup."
thp__pair: str = "Allow {0} to pair with this Trezor?"
thp__pair_app: str = "Allow {0} on {1} to pair with this Trezor?"
+ thp__pair_name: str = "{0} is your Trezor's name."
+ thp__pair_new_device: str = "Pair with new device"
thp__pair_title: str = "Before you continue"
thp__qr_title: str = "Scan QR code to pair"
tutorial__continue: str = "Continue tutorial"
diff --git a/core/src/apps/management/ble/pair_new_device.py b/core/src/apps/management/ble/pair_new_device.py
index 38979578..20780ad3 100644
--- a/core/src/apps/management/ble/pair_new_device.py
+++ b/core/src/apps/management/ble/pair_new_device.py
@@ -25,12 +25,15 @@ def _default_ble_name() -> str:
async def pair_new_device() -> None:
+ from trezor import TR
+
label = storage_device.get_label() or _default_ble_name()
ble.start_advertising(False, label)
result = None
try:
code = await interact(
trezorui_api.show_pairing_device_name(
+ description=TR.thp__pair_name,
device_name=label,
),
None,
@@ -41,8 +44,8 @@ async def pair_new_device() -> None:
try:
result = await interact(
trezorui_api.show_ble_pairing_code(
- title="Bluetooth pairing",
- description="Pairing code match?",
+ title=TR.ble__pairing_title,
+ description=TR.ble__pairing_match,
code=f"{code:0>6}",
),
None,
diff --git a/core/translations/en.json b/core/translations/en.json
index 5339fe25..f5bdb15e 100644
--- a/core/translations/en.json
+++ b/core/translations/en.json
@@ -120,6 +120,8 @@
"ble__manage_paired": "Manage paired devices",
"ble__pair_new": "Pair new device",
"ble__pair_title": "Pair & connect",
+ "ble__pairing_match": "Pairing code match?",
+ "ble__pairing_title": "Bluetooth pairing",
"ble__unpair_all": "Unpair all bluetooth devices",
"ble__unpair_current": "Unpair connected device",
"ble__unpair_title": "Unpair",
@@ -1210,6 +1212,12 @@
"Delizia": "",
"Eckhart": "Connection dialog"
},
+ "thp__continue_on_host": {
+ "Bolt": "",
+ "Caesar": "",
+ "Delizia": "",
+ "Eckhart": "Continue on host"
+ },
"thp__nfc_text": {
"Bolt": "",
"Caesar": "",
@@ -1228,6 +1236,18 @@
"Delizia": "",
"Eckhart": "Allow {0} on {1} to pair with this Trezor?"
},
+ "thp__pair_name": {
+ "Bolt": "",
+ "Caesar": "",
+ "Delizia": "",
+ "Eckhart": "{0} is your Trezor's name."
+ },
+ "thp__pair_new_device": {
+ "Bolt": "",
+ "Caesar": "",
+ "Delizia": "",
+ "Eckhart": "Pair with new device"
+ },
"thp__pair_title": {
"Bolt": "",
"Caesar": "",
diff --git a/core/translations/order.json b/core/translations/order.json
index 26b6ff6c..a2034730 100644
--- a/core/translations/order.json
+++ b/core/translations/order.json
@@ -1146,5 +1146,10 @@
"1144": "thp__connect_title",
"1145": "thp__nfc_text",
"1146": "thp__pair_title",
- "1147": "thp__qr_title"
+ "1147": "thp__qr_title",
+ "1148": "ble__pairing_match",
+ "1149": "ble__pairing_title",
+ "1150": "thp__continue_on_host",
+ "1151": "thp__pair_name",
+ "1152": "thp__pair_new_device"
}
diff --git a/core/translations/signatures.json b/core/translations/signatures.json
index 1b61bc78..406f9dad 100644
--- a/core/translations/signatures.json
+++ b/core/translations/signatures.json
@@ -1,8 +1,8 @@
{
"current": {
- "merkle_root": "6109d557e311685c0a2655ab074e9a6251cfd9d006cda9be8286bd672eb20b06",
- "datetime": "2025-09-11T12:45:33.717063+00:00",
- "commit": "6673667e698074ee56d9ed7c08633c09ca37a190"
+ "merkle_root": "093587dc05beb27c037b3f17792d009e9ec39d67d3f9d8f7af1fa6226978c661",
+ "datetime": "2025-09-11T15:57:43.128589+00:00",
+ "commit": "6baa4f3e77ba97dbca269a1994c4cb5f0eb748d3"
},
"history": [
{
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.