From b48fac36941e8e0703f3f480e870f94affc0a41a Mon Sep 17 00:00:00 2001 From: Martin Sirringhaus Date: Fri, 10 Oct 2025 15:17:08 +0200 Subject: [PATCH 1/5] Wire up translations --- CONTRIBUTING.md | 22 ++ credentialsd-ui/data/resources/ui/window.ui | 20 +- credentialsd-ui/meson.build | 8 +- credentialsd-ui/po/LINGUAS | 2 + credentialsd-ui/po/POTFILES.in | 10 +- credentialsd-ui/po/credentialsd-ui.pot | 215 +++++++++++++++++ credentialsd-ui/po/de_DE.po | 217 +++++++++++++++++ credentialsd-ui/po/en_US.po | 223 ++++++++++++++++++ credentialsd-ui/po/meson.build | 11 +- .../src/gui/view_model/gtk/application.rs | 3 +- .../src/gui/view_model/gtk/device.rs | 19 +- credentialsd-ui/src/gui/view_model/gtk/mod.rs | 57 +++-- credentialsd-ui/src/gui/view_model/mod.rs | 38 +-- credentialsd-ui/src/meson.build | 11 +- 14 files changed, 784 insertions(+), 72 deletions(-) create mode 100644 credentialsd-ui/po/credentialsd-ui.pot create mode 100644 credentialsd-ui/po/de_DE.po create mode 100644 credentialsd-ui/po/en_US.po diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ca8ab14..0250117 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -192,3 +192,25 @@ formatting and linting tools [mentioned above](#code-formatting-and-linting). You should also follow the install instructions in [`BUILDING.md`](/BUILDING.md) and execute authentication flows in a browser to ensure that everything still works as it should. + +# Translations + +credentialsd-ui is using [gettext-rs](https://github.com/gettext-rs/gettext-rs) to translate user-facing strings. + +Please wrap all user-facing messages in `gettext("my string")`-calls and add the files you add them to, to `credentialsd-ui/po/POTFILES`. + +If you introduce a new language, also add them to `credentialsd-ui/po/LINGUAS`. + +Then `cd` into your build-directory (e.g. `build/`) and run + +``` + # To update the POT template file, in case new strings have been added in the sources + meson compile credentialds-ui-pot + # and to update the individual language files + meson compile credentialds-ui-update-po +``` +to update the template, so it contains all messages to be translated. + +Meson should take care of building the translations. + +When using the development-profile to build, meson will use the locally built translations. diff --git a/credentialsd-ui/data/resources/ui/window.ui b/credentialsd-ui/data/resources/ui/window.ui index c3c1639..af10dac 100644 --- a/credentialsd-ui/data/resources/ui/window.ui +++ b/credentialsd-ui/data/resources/ui/window.ui @@ -40,13 +40,13 @@ choose_device - Choose device + Choose device vertical - Devices + Devices @@ -69,7 +69,7 @@ usb - Plug in security key + Plug in security key vertical @@ -110,7 +110,7 @@ hybrid_qr - Scan the QR code to connect your device + Scan the QR code to connect your device vertical @@ -155,13 +155,13 @@ choose_credential - Choose credential + Choose credential vertical - Choose credential + Choose credential @@ -184,13 +184,13 @@ completed - Complete + Complete vertical - Done! + Done! @@ -201,7 +201,7 @@ failed - Something went wrong. + Something went wrong. vertical @@ -214,7 +214,7 @@ - Something went wrong while retrieving a credential. Please try again later or use a different authenticator. + Something went wrong while retrieving a credential. Please try again later or use a different authenticator. diff --git a/credentialsd-ui/meson.build b/credentialsd-ui/meson.build index d2e2cb3..b8c9f7b 100644 --- a/credentialsd-ui/meson.build +++ b/credentialsd-ui/meson.build @@ -31,8 +31,6 @@ localedir = prefix / get_option('localedir') datadir = prefix / get_option('datadir') pkgdatadir = datadir / gui_executable_name iconsdir = datadir / 'icons' -podir = meson.project_source_root() / meson.current_source_dir() / 'po' -gettext_package = gui_executable_name if get_option('profile') == 'development' profile = 'Devel' @@ -71,6 +69,10 @@ if get_option('cargo_offline') == true cargo_options += ['--offline'] endif +# Localization setup +podir = meson.project_source_root() / meson.current_source_dir() / 'po' +gettext_package = gui_executable_name + subdir('data') subdir('po') subdir('src') @@ -79,4 +81,4 @@ gnome.post_install( gtk_update_icon_cache: true, glib_compile_schemas: true, update_desktop_database: true, -) \ No newline at end of file +) diff --git a/credentialsd-ui/po/LINGUAS b/credentialsd-ui/po/LINGUAS index e69de29..845d78b 100644 --- a/credentialsd-ui/po/LINGUAS +++ b/credentialsd-ui/po/LINGUAS @@ -0,0 +1,2 @@ +en_US +de_DE diff --git a/credentialsd-ui/po/POTFILES.in b/credentialsd-ui/po/POTFILES.in index 3417f90..c342f04 100644 --- a/credentialsd-ui/po/POTFILES.in +++ b/credentialsd-ui/po/POTFILES.in @@ -1,6 +1,8 @@ -data/xyz.iinuwa.CredentialManager.desktop.in.in -data/xyz.iinuwa.CredentialManager.gschema.xml.in -data/xyz.iinuwa.CredentialManager.metainfo.xml.in.in +data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in +data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in +data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in data/resources/ui/shortcuts.ui data/resources/ui/window.ui -src/application.rs +src/gui/view_model/gtk/mod.rs +src/gui/view_model/gtk/device.rs +src/gui/view_model/mod.rs diff --git a/credentialsd-ui/po/credentialsd-ui.pot b/credentialsd-ui/po/credentialsd-ui.pot new file mode 100644 index 0000000..46f60db --- /dev/null +++ b/credentialsd-ui/po/credentialsd-ui.pot @@ -0,0 +1,215 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-10-13 11:55+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#. Insert your license of choice here +#. LGPL-3.0-only +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:2 +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:8 +#: src/gui/view_model/gtk/mod.rs:372 +msgid "Credential Manager" +msgstr "" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:3 +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:9 +msgid "Write a GTK + Rust application" +msgstr "" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:9 +msgid "Gnome;GTK;" +msgstr "" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:6 +msgid "Window width" +msgstr "" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:10 +msgid "Window height" +msgstr "" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:14 +msgid "Window maximized state" +msgstr "" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:11 +msgid "" +"A boilerplate template for GTK + Rust. It uses Meson as a build system and " +"has flatpak support by default." +msgstr "" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:16 +msgid "Registering a credential" +msgstr "" + +#. developer_name tag deprecated with Appstream 1.0 +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:34 +msgid "Isaiah Inuwa" +msgstr "" + +#: data/resources/ui/shortcuts.ui:11 +msgctxt "shortcut window" +msgid "General" +msgstr "" + +#: data/resources/ui/shortcuts.ui:14 +msgctxt "shortcut window" +msgid "Show Shortcuts" +msgstr "" + +#: data/resources/ui/shortcuts.ui:20 +msgctxt "shortcut window" +msgid "Quit" +msgstr "" + +#: data/resources/ui/window.ui:6 +msgid "_Preferences" +msgstr "" + +#: data/resources/ui/window.ui:10 +msgid "_Keyboard Shortcuts" +msgstr "" + +#: data/resources/ui/window.ui:43 +msgid "Choose device" +msgstr "" + +#: data/resources/ui/window.ui:49 +msgid "Devices" +msgstr "" + +#: data/resources/ui/window.ui:72 +msgid "Plug in security key" +msgstr "" + +#: data/resources/ui/window.ui:113 +msgid "Scan the QR code to connect your device" +msgstr "" + +#: data/resources/ui/window.ui:158 data/resources/ui/window.ui:164 +msgid "Choose credential" +msgstr "" + +#: data/resources/ui/window.ui:187 +msgid "Complete" +msgstr "" + +#: data/resources/ui/window.ui:193 +msgid "Done!" +msgstr "" + +#: data/resources/ui/window.ui:204 +msgid "Something went wrong." +msgstr "" + +#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:233 +msgid "" +"Something went wrong while retrieving a credential. Please try again later " +"or use a different authenticator." +msgstr "" + +#: src/gui/view_model/gtk/mod.rs:137 +msgid "Enter your PIN." +msgstr "" + +#: src/gui/view_model/gtk/mod.rs:140 +msgid "One attempt remaining." +msgid_plural "%d attempts remaining." +msgstr[0] "" +msgstr[1] "" + +#: src/gui/view_model/gtk/mod.rs:154 +msgid "Touch your device again. One attempt remaining." +msgid_plural "Touch your device again. %d attempts remaining." +msgstr[0] "" +msgstr[1] "" + +#: src/gui/view_model/gtk/mod.rs:160 +msgid "Touch your device." +msgstr "" + +#: src/gui/view_model/gtk/mod.rs:165 +msgid "Touch your device" +msgstr "" + +#: src/gui/view_model/gtk/mod.rs:168 +msgid "Scan the QR code with your device to begin authentication." +msgstr "" + +#: src/gui/view_model/gtk/mod.rs:178 +msgid "" +"Connecting to your device. Make sure both devices are near each other and " +"have Bluetooth enabled." +msgstr "" + +#: src/gui/view_model/gtk/mod.rs:186 +msgid "Device connected. Follow the instructions on your device" +msgstr "" + +#: src/gui/view_model/gtk/mod.rs:312 +msgid "Insert your security key." +msgstr "" + +#: src/gui/view_model/gtk/mod.rs:328 +msgid "Multiple devices found. Please select with which to proceed." +msgstr "" + +#: src/gui/view_model/gtk/device.rs:57 +msgid "A Bluetooth device" +msgstr "" + +#: src/gui/view_model/gtk/device.rs:58 +msgid "This device" +msgstr "" + +#: src/gui/view_model/gtk/device.rs:59 +msgid "A mobile device" +msgstr "" + +#: src/gui/view_model/gtk/device.rs:60 +msgid "Linked Device" +msgstr "" + +#: src/gui/view_model/gtk/device.rs:61 +msgid "An NFC device" +msgstr "" + +#: src/gui/view_model/gtk/device.rs:62 +msgid "A security key" +msgstr "" + +#: src/gui/view_model/mod.rs:65 +msgid "Create new credential" +msgstr "" + +#: src/gui/view_model/mod.rs:66 +msgid "Use a credential" +msgstr "" + +#: src/gui/view_model/mod.rs:173 +msgid "Failed to select credential from device." +msgstr "" + +#: src/gui/view_model/mod.rs:227 +msgid "No matching credentials found on this authenticator." +msgstr "" + +#: src/gui/view_model/mod.rs:230 +msgid "" +"No more PIN attempts allowed. Try removing your device and plugging it back " +"in." +msgstr "" + +#: src/gui/view_model/mod.rs:236 +msgid "This credential is already registered on this authenticator." +msgstr "" + +#: src/gui/view_model/mod.rs:284 +msgid "Something went wrong. Try again later or use a different authenticator." +msgstr "" diff --git a/credentialsd-ui/po/de_DE.po b/credentialsd-ui/po/de_DE.po new file mode 100644 index 0000000..92a1170 --- /dev/null +++ b/credentialsd-ui/po/de_DE.po @@ -0,0 +1,217 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2025-10-10 14:45+0200\n" +"PO-Revision-Date: 2025-10-10 14:45+0200\n" +"Last-Translator: Martin Sirringhaus \n" +"Language: de_DE\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#. Insert your license of choice here +#. LGPL-3.0-only +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:2 +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:8 +#: src/gui/view_model/gtk/mod.rs:370 +msgid "Credential Manager" +msgstr "Zugangsdatenmanager" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:3 +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:9 +msgid "Write a GTK + Rust application" +msgstr "" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:9 +msgid "Gnome;GTK;" +msgstr "Gnome;GTK;" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:6 +msgid "Window width" +msgstr "Fensterbreite" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:10 +msgid "Window height" +msgstr "Fensterhöhe" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:14 +msgid "Window maximized state" +msgstr "Fenster maximiert" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:11 +msgid "" +"A boilerplate template for GTK + Rust. It uses Meson as a build system and " +"has flatpak support by default." +msgstr "" +"Eine Vorlage für eine GTK + Rust Anwendung." + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:16 +msgid "Registering a credential" +msgstr "Zugangsdaten registrieren" + +#: data/resources/ui/shortcuts.ui:11 +msgctxt "shortcut window" +msgid "General" +msgstr "Allgemein" + +#: data/resources/ui/shortcuts.ui:14 +msgctxt "shortcut window" +msgid "Show Shortcuts" +msgstr "Zeige Kürzel" + +#: data/resources/ui/shortcuts.ui:20 +msgctxt "shortcut window" +msgid "Quit" +msgstr "Beenden" + +#: data/resources/ui/window.ui:6 +msgid "_Preferences" +msgstr "Einstellungen" + +#: data/resources/ui/window.ui:10 +msgid "_Keyboard Shortcuts" +msgstr "Tastaturkürzel" + +#: data/resources/ui/window.ui:43 +msgid "Choose device" +msgstr "Gerät auswählen" + +#: data/resources/ui/window.ui:49 +msgid "Devices" +msgstr "Geräte" + +#: data/resources/ui/window.ui:72 +msgid "Plug in security key" +msgstr "Stecken Sie Ihren Security-Token ein" + +#: data/resources/ui/window.ui:113 +msgid "Scan the QR code to connect your device" +msgstr "Scannen Sie den QR-Code, um Ihr Gerät zu verbinden" + +#: data/resources/ui/window.ui:158 data/resources/ui/window.ui:164 +msgid "Choose credential" +msgstr "Wählen Sie Zugangsdaten aus" + +#: data/resources/ui/window.ui:187 +msgid "Complete" +msgstr "Abgeschlossen" + +#: data/resources/ui/window.ui:193 +msgid "Done!" +msgstr "Fertig!" + +#: data/resources/ui/window.ui:204 +msgid "Something went wrong." +msgstr "Etwas ist schief gegangen." + +#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:233 +msgid "" +"Something went wrong while retrieving a credential. Please try again later " +"or use a different authenticator." +msgstr "" +"Beim Abrufen Ihrer Zugangsdaten ist ein Fehler aufgetreten. Versuchen Sie es " +"später wieder, oder verwenden Sie einen anderen Security-Token." + +#: src/gui/view_model/gtk/mod.rs:137 +msgid "Enter your PIN." +msgstr "Geben Sie Ihren PIN ein." + +#: src/gui/view_model/gtk/mod.rs:140 +msgid "One attempt remaining." +msgid_plural "%d attempts remaining." +msgstr[0] "Sie haben nur noch einen Versuch." +msgstr[1] "Sie haben noch %d Versuche." + +#: src/gui/view_model/gtk/mod.rs:154 +msgid "Touch your device again. One attempt remaining." +msgid_plural "Touch your device again. %d attempts remaining." +msgstr[0] "Berühren Sie Ihr Gerät. Sie haben nur noch einen Versuch." +msgstr[1] "Berühren Sie nochmal Ihr Gerät. Sie haben nur noch %d Versuche." + +#: src/gui/view_model/gtk/mod.rs:160 +msgid "Touch your device." +msgstr "Berühren Sie Ihr Gerät." + +#: src/gui/view_model/gtk/mod.rs:165 +msgid "Touch your device" +msgstr "Berühren Sie Ihr Gerät." + +#: src/gui/view_model/gtk/mod.rs:168 +msgid "Scan the QR code with your device to begin authentication." +msgstr "Scannen Sie den QR code mit ihrem Gerät um die Authentifizierung zu beginnen." + +#: src/gui/view_model/gtk/mod.rs:178 +msgid "" +"Connecting to your device. Make sure both devices are near each other and " +"have Bluetooth enabled." +msgstr "" +"Verbindung zu Ihrem Gerät wird aufgebaut. Stellen Sie sicher, dass beide Geräte nah beieinander sind und Bluetooth aktiviert haben." + +#: src/gui/view_model/gtk/mod.rs:186 +msgid "Device connected. Follow the instructions on your device" +msgstr "Verbindung hergestellt. Folgen Sie den Anweisungen auf Ihrem Gerät." + +#: src/gui/view_model/gtk/mod.rs:312 +msgid "Insert your security key." +msgstr "Stecken Sie Ihren Security-Token ein." + +#: src/gui/view_model/gtk/mod.rs:328 +msgid "Multiple devices found. Please select with which to proceed." +msgstr "Mehrere Geräte gefunden. Bitte wählen Sie einen aus, um fortzufahren." + +#: src/gui/view_model/gtk/device.rs:57 +msgid "A Bluetooth device" +msgstr "Ein Bluetooth-Gerät" + +#: src/gui/view_model/gtk/device.rs:58 +msgid "This device" +msgstr "Dieses Gerät" + +#: src/gui/view_model/gtk/device.rs:59 +msgid "A mobile device" +msgstr "Ein mobiles Gerät" + +#: src/gui/view_model/gtk/device.rs:60 +msgid "Linked Device" +msgstr "Verbundenes Gerät" + +#: src/gui/view_model/gtk/device.rs:61 +msgid "An NFC device" +msgstr "Ein NFC-Gerät" + +#: src/gui/view_model/gtk/device.rs:62 +msgid "A security key" +msgstr "Ein Security-Token" + +#: src/gui/view_model/mod.rs:65 +msgid "Create new credential" +msgstr "Neue Zugangsdaten erstellen" + +#: src/gui/view_model/mod.rs:66 +msgid "Use a credential" +msgstr "Zugangsdaten abrufen" + +#: src/gui/view_model/mod.rs:173 +msgid "Failed to select credential from device." +msgstr "Zugangsdaten vom Gerät konnten nicht ausgewählt werden." + +#: src/gui/view_model/mod.rs:227 +msgid "No matching credentials found on this authenticator." +msgstr "Keine passenden Zugangsdaten auf diesem Gerät gefunden." + +#: src/gui/view_model/mod.rs:230 +msgid "" +"No more PIN attempts allowed. Try removing your device and plugging it back " +"in." +msgstr "Keine weiteren PIN-Eingaben erlaubt. Versuchen Sie ihr Gerät aus- und wieder einzustecken." + +#: src/gui/view_model/mod.rs:236 +msgid "This credential is already registered on this authenticator." +msgstr "Diese Zugangsdaten sind bereits auf diesem Gerät registriert." + +#: src/gui/view_model/mod.rs:284 +msgid "Something went wrong. Try again later or use a different authenticator." +msgstr "" +"Es ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal, " +"oder benutzen Sie ein anderes Gerät." diff --git a/credentialsd-ui/po/en_US.po b/credentialsd-ui/po/en_US.po new file mode 100644 index 0000000..885ed9c --- /dev/null +++ b/credentialsd-ui/po/en_US.po @@ -0,0 +1,223 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-10-10 14:45+0200\n" +"PO-Revision-Date: 2025-10-10 14:45+0200\n" +"Last-Translator: Martin Sirringhaus \n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#. Insert your license of choice here +#. LGPL-3.0-only +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:2 +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:8 +#: src/gui/view_model/gtk/mod.rs:370 +msgid "Credential Manager" +msgstr "Credential Manager" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:3 +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:9 +msgid "Write a GTK + Rust application" +msgstr "Write a GTK + Rust application" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:9 +msgid "Gnome;GTK;" +msgstr "Gnome;GTK;" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:6 +msgid "Window width" +msgstr "Window width" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:10 +msgid "Window height" +msgstr "Window height" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.gschema.xml.in:14 +msgid "Window maximized state" +msgstr "Window maximized state" + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:11 +msgid "" +"A boilerplate template for GTK + Rust. It uses Meson as a build system and " +"has flatpak support by default." +msgstr "" +"A boilerplate template for GTK + Rust. It uses Meson as a build system and " +"has flatpak support by default." + +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:16 +msgid "Registering a credential" +msgstr "Registering a credential" + +#. developer_name tag deprecated with Appstream 1.0 +#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:34 +msgid "Isaiah Inuwa" +msgstr "Isaiah Inuwa" + +#: data/resources/ui/shortcuts.ui:11 +msgctxt "shortcut window" +msgid "General" +msgstr "General" + +#: data/resources/ui/shortcuts.ui:14 +msgctxt "shortcut window" +msgid "Show Shortcuts" +msgstr "Show Shortcuts" + +#: data/resources/ui/shortcuts.ui:20 +msgctxt "shortcut window" +msgid "Quit" +msgstr "Quit" + +#: data/resources/ui/window.ui:6 +msgid "_Preferences" +msgstr "_Preferences" + +#: data/resources/ui/window.ui:10 +msgid "_Keyboard Shortcuts" +msgstr "Keyboard Shortcuts" + +#: data/resources/ui/window.ui:43 +msgid "Choose device" +msgstr "Choose device" + +#: data/resources/ui/window.ui:49 +msgid "Devices" +msgstr "Devices" + +#: data/resources/ui/window.ui:72 +msgid "Plug in security key" +msgstr "Plug in security key" + +#: data/resources/ui/window.ui:113 +msgid "Scan the QR code to connect your device" +msgstr "Scan the QR code to connect your device" + +#: data/resources/ui/window.ui:158 data/resources/ui/window.ui:164 +msgid "Choose credential" +msgstr "Choose credential" + +#: data/resources/ui/window.ui:187 +msgid "Complete" +msgstr "Complete" + +#: data/resources/ui/window.ui:193 +msgid "Done!" +msgstr "Done!" + +#: data/resources/ui/window.ui:204 +msgid "Something went wrong." +msgstr "Something went wrong." + +#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:233 +msgid "" +"Something went wrong while retrieving a credential. Please try again later " +"or use a different authenticator." +msgstr "" +"Something went wrong while retrieving a credential. Please try again later " +"or use a different authenticator." + +#: src/gui/view_model/gtk/mod.rs:137 +msgid "Enter your PIN." +msgstr "Enter your PIN." + +#: src/gui/view_model/gtk/mod.rs:140 +msgid "One attempt remaining." +msgid_plural "%d attempts remaining." +msgstr[0] "One attempt remaining." +msgstr[1] "%d attempts remaining." + +#: src/gui/view_model/gtk/mod.rs:154 +msgid "Touch your device again. One attempt remaining." +msgid_plural "Touch your device again. %d attempts remaining." +msgstr[0] "Touch your device again. One attempt remaining." +msgstr[1] "Touch your device again. %d attempts remaining." + +#: src/gui/view_model/gtk/mod.rs:160 +msgid "Touch your device." +msgstr "Touch your device." + +#: src/gui/view_model/gtk/mod.rs:165 +msgid "Touch your device" +msgstr "Touch your device" + +#: src/gui/view_model/gtk/mod.rs:168 +msgid "Scan the QR code with your device to begin authentication." +msgstr "Scan the QR code with your device to begin authentication." + +#: src/gui/view_model/gtk/mod.rs:178 +msgid "" +"Connecting to your device. Make sure both devices are near each other and " +"have Bluetooth enabled." +msgstr "" +"Connecting to your device. Make sure both devices are near each other and " +"have Bluetooth enabled." + +#: src/gui/view_model/gtk/mod.rs:186 +msgid "Device connected. Follow the instructions on your device" +msgstr "Device connected. Follow the instructions on your device" + +#: src/gui/view_model/gtk/mod.rs:312 +msgid "Insert your security key." +msgstr "Insert your security key." + +#: src/gui/view_model/gtk/mod.rs:328 +msgid "Multiple devices found. Please select with which to proceed." +msgstr "Multiple devices found. Please select with which to proceed." + +#: src/gui/view_model/gtk/device.rs:57 +msgid "A Bluetooth device" +msgstr "A Bluetooth device" + +#: src/gui/view_model/gtk/device.rs:58 +msgid "This device" +msgstr "This device" + +#: src/gui/view_model/gtk/device.rs:59 +msgid "A mobile device" +msgstr "A mobile device" + +#: src/gui/view_model/gtk/device.rs:60 +msgid "Linked Device" +msgstr "Linked Device" + +#: src/gui/view_model/gtk/device.rs:61 +msgid "An NFC device" +msgstr "An NFC device" + +#: src/gui/view_model/gtk/device.rs:62 +msgid "A security key" +msgstr "A security key" + +#: src/gui/view_model/mod.rs:65 +msgid "Create new credential" +msgstr "Create new credential" + +#: src/gui/view_model/mod.rs:66 +msgid "Use a credential" +msgstr "Use a credential" + +#: src/gui/view_model/mod.rs:173 +msgid "Failed to select credential from device." +msgstr "Failed to select credential from device." + +#: src/gui/view_model/mod.rs:227 +msgid "No matching credentials found on this authenticator." +msgstr "No matching credentials found on this authenticator." + +#: src/gui/view_model/mod.rs:230 +msgid "" +"No more PIN attempts allowed. Try removing your device and plugging it back " +"in." +msgstr "" +"No more PIN attempts allowed. Try removing your device and plugging it back " +"in." + +#: src/gui/view_model/mod.rs:236 +msgid "This credential is already registered on this authenticator." +msgstr "This credential is already registered on this authenticator." + +#: src/gui/view_model/mod.rs:284 +msgid "Something went wrong. Try again later or use a different authenticator." +msgstr "Something went wrong. Try again later or use a different authenticator." diff --git a/credentialsd-ui/po/meson.build b/credentialsd-ui/po/meson.build index 57d1266..3b8d88e 100644 --- a/credentialsd-ui/po/meson.build +++ b/credentialsd-ui/po/meson.build @@ -1 +1,10 @@ -i18n.gettext(gettext_package, preset: 'glib') +i18n = import('i18n') + +# This creates build targets: 'credentialsd-ui-pot', 'credentialsd-ui-update-po', etc. +i18n.gettext(gettext_package, + args: ['--directory=' + meson.project_source_root() / 'credentialsd-ui', + '--from-code=UTF-8', + '--copyright-holder="The Credentials for Linux Project"', + '--msgid-bugs-address="https://github.com/linux-credentials/credentialsd/issues"', + ], +) diff --git a/credentialsd-ui/src/gui/view_model/gtk/application.rs b/credentialsd-ui/src/gui/view_model/gtk/application.rs index 82216cd..7ffa2b6 100644 --- a/credentialsd-ui/src/gui/view_model/gtk/application.rs +++ b/credentialsd-ui/src/gui/view_model/gtk/application.rs @@ -6,7 +6,7 @@ use gtk::subclass::prelude::*; use gtk::{gdk, gio, glib}; use super::{ViewModel, window::CredentialsUiWindow}; -use crate::config::{APP_ID, PKGDATADIR, PROFILE, VERSION}; +use crate::config::{APP_ID, LOCALEDIR, PKGDATADIR, PROFILE, VERSION}; use crate::gui::view_model::{ViewEvent, ViewUpdate}; mod imp { @@ -152,6 +152,7 @@ impl CredentialsUi { info!("Credentials UI ({})", APP_ID); info!("Version: {} ({})", VERSION, PROFILE); info!("Datadir: {}", PKGDATADIR); + info!("Localedir: {}", LOCALEDIR); ApplicationExtManual::run(self) } diff --git a/credentialsd-ui/src/gui/view_model/gtk/device.rs b/credentialsd-ui/src/gui/view_model/gtk/device.rs index ad859ef..7992f56 100644 --- a/credentialsd-ui/src/gui/view_model/gtk/device.rs +++ b/credentialsd-ui/src/gui/view_model/gtk/device.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; +use gettextrs::gettext; use glib::Object; use gtk::glib; use gtk::prelude::*; @@ -51,28 +52,28 @@ impl DeviceObject { } } -fn transport_name(transport: &Transport) -> &'static str { +fn transport_name(transport: &Transport) -> String { match transport { - Transport::Ble => "A Bluetooth device", - Transport::Internal => "This device", - Transport::HybridQr => "A mobile device", - Transport::HybridLinked => "TODO: Linked Device", - Transport::Nfc => "An NFC device", - Transport::Usb => "A security key", + Transport::Ble => gettext("A Bluetooth device"), + Transport::Internal => gettext("This device"), + Transport::HybridQr => gettext("A mobile device"), + Transport::HybridLinked => gettext("Linked Device"), + Transport::Nfc => gettext("An NFC device"), + Transport::Usb => gettext("A security key"), // Transport::PasskeyProvider => ("symbolic-link-symbolic", "ACME Password Manager"), } } impl From for DeviceObject { fn from(value: crate::gui::view_model::Device) -> Self { let name = transport_name(&value.transport); - Self::new(&value.id, &value.transport, name) + Self::new(&value.id, &value.transport, &name) } } impl From<&crate::gui::view_model::Device> for DeviceObject { fn from(value: &crate::gui::view_model::Device) -> Self { let name = transport_name(&value.transport); - Self::new(&value.id, &value.transport, name) + Self::new(&value.id, &value.transport, &name) } } diff --git a/credentialsd-ui/src/gui/view_model/gtk/mod.rs b/credentialsd-ui/src/gui/view_model/gtk/mod.rs index 966c5f6..cb7a647 100644 --- a/credentialsd-ui/src/gui/view_model/gtk/mod.rs +++ b/credentialsd-ui/src/gui/view_model/gtk/mod.rs @@ -4,7 +4,7 @@ pub mod device; mod window; use async_std::channel::{Receiver, Sender}; -use gettextrs::{LocaleCategory, gettext}; +use gettextrs::{LocaleCategory, gettext, ngettext}; use glib::clone; use gtk::gdk::Texture; use gtk::gdk_pixbuf::Pixbuf; @@ -134,34 +134,38 @@ impl ViewModel { view_model.waiting_for_device(&device) } ViewUpdate::UsbNeedsPin { attempts_left } => { - let prompt = match attempts_left { - Some(1) => { - "Enter your PIN. 1 attempt remaining.".to_string() - } - Some(attempts_left) => format!( - "Enter your PIN. {attempts_left} attempts remaining." - ), - None => "Enter your PIN.".to_string(), - }; + let mut prompt = gettext("Enter your PIN."); + if let Some(left) = attempts_left { + let localized = ngettext( + "One attempt remaining.", + "%d attempts remaining.", + left, + ); + prompt.push(' '); + prompt += &localized.replace("%d", &format!("{}", left)); + } view_model.set_prompt(prompt); view_model.set_usb_pin_entry_visible(true); } ViewUpdate::UsbNeedsUserVerification { attempts_left } => { let prompt = match attempts_left { - Some(1) => "Touch your device again. 1 attempt remaining." - .to_string(), - Some(attempts_left) => format!( - "Touch your device again. {attempts_left} attempts remaining." - ), - None => "Touch your device.".to_string(), + Some(left) => { + let localized = ngettext( + "Touch your device again. One attempt remaining.", + "Touch your device again. %d attempts remaining.", + left, + ); + localized.replace("%d", &format!("{}", left)) + } + None => gettext("Touch your device."), }; view_model.set_prompt(prompt); } ViewUpdate::UsbNeedsUserPresence => { - view_model.set_prompt("Touch your device"); + view_model.set_prompt(gettext("Touch your device")); } ViewUpdate::HybridNeedsQrCode(qr_code) => { - view_model.set_prompt("Scan the QR code with your device to begin authentication."); + view_model.set_prompt(gettext("Scan the QR code with your device to begin authentication.")); let texture = view_model.draw_qr_code(&qr_code); view_model.set_qr_code_paintable(&texture); view_model.set_qr_code_visible(true); @@ -170,17 +174,17 @@ impl ViewModel { ViewUpdate::HybridConnecting => { view_model.set_qr_code_visible(false); _ = view_model.qr_code_paintable().take(); - view_model.set_prompt( + view_model.set_prompt(gettext( "Connecting to your device. Make sure both devices are near each other and have Bluetooth enabled.", - ); + )); view_model.set_qr_spinner_visible(true); } ViewUpdate::HybridConnected => { view_model.set_qr_code_visible(false); _ = view_model.qr_code_paintable().take(); - view_model.set_prompt( + view_model.set_prompt(gettext( "Device connected. Follow the instructions on your device", - ); + )); view_model.set_qr_spinner_visible(false); } ViewUpdate::Completed => { @@ -190,6 +194,7 @@ impl ViewModel { ViewUpdate::Failed(error_msg) => { view_model.set_qr_spinner_visible(false); view_model.set_failed(true); + // These are already gettext messages view_model.set_prompt(error_msg); } ViewUpdate::Cancelled => { @@ -304,7 +309,7 @@ impl ViewModel { fn waiting_for_device(&self, device: &Device) { match device.transport { Transport::Usb => { - self.set_prompt("Insert your security key."); + self.set_prompt(gettext("Insert your security key.")); } Transport::HybridQr => { self.set_prompt(""); @@ -319,7 +324,9 @@ impl ViewModel { } fn selecting_device(&self) { - self.set_prompt("Multiple devices found. Please select with which to proceed."); + self.set_prompt(gettext( + "Multiple devices found. Please select with which to proceed.", + )); } pub async fn send_usb_device_pin(&self, pin: String) { @@ -358,6 +365,8 @@ pub fn start_gtk_app( gettextrs::setlocale(LocaleCategory::LcAll, ""); gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect("Unable to bind the text domain"); gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain"); + gettextrs::bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8") + .expect("Unable to set codeset to UTF-8"); if glib::application_name().is_none() { glib::set_application_name(&gettext("Credential Manager")); diff --git a/credentialsd-ui/src/gui/view_model/mod.rs b/credentialsd-ui/src/gui/view_model/mod.rs index 6f8ceae..8930305 100644 --- a/credentialsd-ui/src/gui/view_model/mod.rs +++ b/credentialsd-ui/src/gui/view_model/mod.rs @@ -7,6 +7,7 @@ use async_std::{ channel::{Receiver, Sender}, sync::Mutex as AsyncMutex, }; +use gettextrs::gettext; use serde::{Deserialize, Serialize}; use tracing::{error, info}; @@ -61,8 +62,8 @@ impl ViewModel { async fn update_title(&mut self) { self.title = match self.operation { - Operation::Create => "Create new credential", - Operation::Get => "Use a credential", + Operation::Create => gettext("Create new credential"), + Operation::Get => gettext("Use a credential"), } .to_string(); self.tx_update @@ -166,10 +167,11 @@ impl ViewModel { .await .is_err() { - let error_msg = "Failed to select credential from device."; - tracing::error!(error_msg); + tracing::error!("Failed to select credential from device."); self.tx_update - .send(ViewUpdate::Failed(error_msg.to_string())) + .send(ViewUpdate::Failed(gettext( + "Failed to select credential from device.", + ))) .await .unwrap(); } @@ -220,20 +222,20 @@ impl ViewModel { } // TODO: Provide more specific error messages using the wrapped Error. UsbState::Failed(err) => { - let error_msg = String::from(match err { + let error_msg = match err { Error::NoCredentials => { - "No matching credentials found on this authenticator." + gettext("No matching credentials found on this authenticator.") } - Error::PinAttemptsExhausted => { - "No more PIN attempts allowed. Try removing your device and plugging it back in." - } - Error::AuthenticatorError | Error::Internal(_) => { - "Something went wrong while retrieving a credential. Please try again later or use a different authenticator." - } - Error::CredentialExcluded => { - "This credential is already registered on this authenticator." - } - }); + Error::PinAttemptsExhausted => gettext( + "No more PIN attempts allowed. Try removing your device and plugging it back in.", + ), + Error::AuthenticatorError | Error::Internal(_) => gettext( + "Something went wrong while retrieving a credential. Please try again later or use a different authenticator.", + ), + Error::CredentialExcluded => gettext( + "This credential is already registered on this authenticator.", + ), + }; self.tx_update .send(ViewUpdate::Failed(error_msg)) .await @@ -279,7 +281,7 @@ impl ViewModel { } HybridState::Failed => { self.hybrid_qr_code_data = None; - self.tx_update.send(ViewUpdate::Failed(String::from("Something went wrong. Try again later or use a different authenticator."))).await.unwrap(); + self.tx_update.send(ViewUpdate::Failed(gettext("Something went wrong. Try again later or use a different authenticator."))).await.unwrap(); } }; } /* diff --git a/credentialsd-ui/src/meson.build b/credentialsd-ui/src/meson.build index 02e7d8c..198de49 100644 --- a/credentialsd-ui/src/meson.build +++ b/credentialsd-ui/src/meson.build @@ -8,10 +8,17 @@ if (get_option('profile') == 'development') else global_conf.set_quoted('PKGDATADIR', pkgdatadir) endif +if (get_option('profile') == 'development') + global_conf.set_quoted( + 'LOCALEDIR', + meson.project_build_root() / gui_build_dir / 'po', + ) +else + global_conf.set_quoted('LOCALEDIR', localedir) +endif global_conf.set_quoted('PROFILE', profile) global_conf.set_quoted('VERSION', version + version_suffix) global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package) -global_conf.set_quoted('LOCALEDIR', localedir) configure_file(input: 'config.rs.in', output: 'config.rs', configuration: global_conf) # Copy the config.rs output to the source directory. @@ -67,4 +74,4 @@ test( '--nocapture', ], protocol: 'exitcode', -) \ No newline at end of file +) From 735b49ec4e35a744351461cefa88af6a469632d5 Mon Sep 17 00:00:00 2001 From: Martin Sirringhaus Date: Fri, 24 Oct 2025 08:12:07 +0200 Subject: [PATCH 2/5] Do not concatenate translated strings and fix typo in CONTRIBUTING.md --- CONTRIBUTING.md | 4 +-- credentialsd-ui/po/credentialsd-ui.pot | 19 ++++++----- credentialsd-ui/po/de_DE.po | 34 +++++++++++-------- credentialsd-ui/po/en_US.po | 29 ++++++++-------- credentialsd-ui/src/gui/view_model/gtk/mod.rs | 14 ++++---- 5 files changed, 54 insertions(+), 46 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0250117..2a25e89 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -205,9 +205,9 @@ Then `cd` into your build-directory (e.g. `build/`) and run ``` # To update the POT template file, in case new strings have been added in the sources - meson compile credentialds-ui-pot + meson compile credentialsd-ui-pot # and to update the individual language files - meson compile credentialds-ui-update-po + meson compile credentialsd-ui-update-po ``` to update the template, so it contains all messages to be translated. diff --git a/credentialsd-ui/po/credentialsd-ui.pot b/credentialsd-ui/po/credentialsd-ui.pot index 46f60db..4bd2739 100644 --- a/credentialsd-ui/po/credentialsd-ui.pot +++ b/credentialsd-ui/po/credentialsd-ui.pot @@ -1,6 +1,9 @@ msgid "" msgstr "" -"POT-Creation-Date: 2025-10-13 11:55+0200\n" +"Project-Id-Version: credentialsd-ui\n" +"Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" +"issues\"\n" +"POT-Creation-Date: 2025-10-24 08:05+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: \n" @@ -114,16 +117,16 @@ msgid "" "or use a different authenticator." msgstr "" -#: src/gui/view_model/gtk/mod.rs:137 -msgid "Enter your PIN." -msgstr "" - -#: src/gui/view_model/gtk/mod.rs:140 -msgid "One attempt remaining." -msgid_plural "%d attempts remaining." +#: src/gui/view_model/gtk/mod.rs:139 +msgid "Enter your PIN. One attempt remaining." +msgid_plural "Enter your PIN. %d attempts remaining." msgstr[0] "" msgstr[1] "" +#: src/gui/view_model/gtk/mod.rs:145 +msgid "Enter your PIN." +msgstr "" + #: src/gui/view_model/gtk/mod.rs:154 msgid "Touch your device again. One attempt remaining." msgid_plural "Touch your device again. %d attempts remaining." diff --git a/credentialsd-ui/po/de_DE.po b/credentialsd-ui/po/de_DE.po index 92a1170..b07fe20 100644 --- a/credentialsd-ui/po/de_DE.po +++ b/credentialsd-ui/po/de_DE.po @@ -1,7 +1,9 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2025-10-10 14:45+0200\n" +"Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" +"issues\"\n" +"POT-Creation-Date: 2025-10-24 08:05+0200\n" "PO-Revision-Date: 2025-10-10 14:45+0200\n" "Last-Translator: Martin Sirringhaus \n" "Language: de_DE\n" @@ -14,7 +16,7 @@ msgstr "" #. LGPL-3.0-only #: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:2 #: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:8 -#: src/gui/view_model/gtk/mod.rs:370 +#: src/gui/view_model/gtk/mod.rs:372 msgid "Credential Manager" msgstr "Zugangsdatenmanager" @@ -43,8 +45,7 @@ msgstr "Fenster maximiert" msgid "" "A boilerplate template for GTK + Rust. It uses Meson as a build system and " "has flatpak support by default." -msgstr "" -"Eine Vorlage für eine GTK + Rust Anwendung." +msgstr "Eine Vorlage für eine GTK + Rust Anwendung." #: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:16 msgid "Registering a credential" @@ -113,16 +114,17 @@ msgstr "" "Beim Abrufen Ihrer Zugangsdaten ist ein Fehler aufgetreten. Versuchen Sie es " "später wieder, oder verwenden Sie einen anderen Security-Token." -#: src/gui/view_model/gtk/mod.rs:137 +#: src/gui/view_model/gtk/mod.rs:139 +#, fuzzy +msgid "Enter your PIN. One attempt remaining." +msgid_plural "Enter your PIN. %d attempts remaining." +msgstr[0] "Geben Sie Ihren PIN ein. Sie haben nur noch einen Versuch." +msgstr[1] "Geben Sie Ihren PIN ein. Sie haben noch %d Versuche." + +#: src/gui/view_model/gtk/mod.rs:145 msgid "Enter your PIN." msgstr "Geben Sie Ihren PIN ein." -#: src/gui/view_model/gtk/mod.rs:140 -msgid "One attempt remaining." -msgid_plural "%d attempts remaining." -msgstr[0] "Sie haben nur noch einen Versuch." -msgstr[1] "Sie haben noch %d Versuche." - #: src/gui/view_model/gtk/mod.rs:154 msgid "Touch your device again. One attempt remaining." msgid_plural "Touch your device again. %d attempts remaining." @@ -139,14 +141,16 @@ msgstr "Berühren Sie Ihr Gerät." #: src/gui/view_model/gtk/mod.rs:168 msgid "Scan the QR code with your device to begin authentication." -msgstr "Scannen Sie den QR code mit ihrem Gerät um die Authentifizierung zu beginnen." +msgstr "" +"Scannen Sie den QR code mit ihrem Gerät um die Authentifizierung zu beginnen." #: src/gui/view_model/gtk/mod.rs:178 msgid "" "Connecting to your device. Make sure both devices are near each other and " "have Bluetooth enabled." msgstr "" -"Verbindung zu Ihrem Gerät wird aufgebaut. Stellen Sie sicher, dass beide Geräte nah beieinander sind und Bluetooth aktiviert haben." +"Verbindung zu Ihrem Gerät wird aufgebaut. Stellen Sie sicher, dass beide " +"Geräte nah beieinander sind und Bluetooth aktiviert haben." #: src/gui/view_model/gtk/mod.rs:186 msgid "Device connected. Follow the instructions on your device" @@ -204,7 +208,9 @@ msgstr "Keine passenden Zugangsdaten auf diesem Gerät gefunden." msgid "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." -msgstr "Keine weiteren PIN-Eingaben erlaubt. Versuchen Sie ihr Gerät aus- und wieder einzustecken." +msgstr "" +"Keine weiteren PIN-Eingaben erlaubt. Versuchen Sie ihr Gerät aus- und wieder " +"einzustecken." #: src/gui/view_model/mod.rs:236 msgid "This credential is already registered on this authenticator." diff --git a/credentialsd-ui/po/en_US.po b/credentialsd-ui/po/en_US.po index 885ed9c..acf7832 100644 --- a/credentialsd-ui/po/en_US.po +++ b/credentialsd-ui/po/en_US.po @@ -1,6 +1,8 @@ msgid "" msgstr "" -"POT-Creation-Date: 2025-10-10 14:45+0200\n" +"Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" +"issues\"\n" +"POT-Creation-Date: 2025-10-24 08:05+0200\n" "PO-Revision-Date: 2025-10-10 14:45+0200\n" "Last-Translator: Martin Sirringhaus \n" "Language: en_US\n" @@ -13,7 +15,7 @@ msgstr "" #. LGPL-3.0-only #: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:2 #: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:8 -#: src/gui/view_model/gtk/mod.rs:370 +#: src/gui/view_model/gtk/mod.rs:372 msgid "Credential Manager" msgstr "Credential Manager" @@ -50,11 +52,6 @@ msgstr "" msgid "Registering a credential" msgstr "Registering a credential" -#. developer_name tag deprecated with Appstream 1.0 -#: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:34 -msgid "Isaiah Inuwa" -msgstr "Isaiah Inuwa" - #: data/resources/ui/shortcuts.ui:11 msgctxt "shortcut window" msgid "General" @@ -118,16 +115,17 @@ msgstr "" "Something went wrong while retrieving a credential. Please try again later " "or use a different authenticator." -#: src/gui/view_model/gtk/mod.rs:137 +#: src/gui/view_model/gtk/mod.rs:139 +#, fuzzy +msgid "Enter your PIN. One attempt remaining." +msgid_plural "Enter your PIN. %d attempts remaining." +msgstr[0] "Enter your PIN. One attempt remaining." +msgstr[1] "Enter your PIN. %d attempts remaining." + +#: src/gui/view_model/gtk/mod.rs:145 msgid "Enter your PIN." msgstr "Enter your PIN." -#: src/gui/view_model/gtk/mod.rs:140 -msgid "One attempt remaining." -msgid_plural "%d attempts remaining." -msgstr[0] "One attempt remaining." -msgstr[1] "%d attempts remaining." - #: src/gui/view_model/gtk/mod.rs:154 msgid "Touch your device again. One attempt remaining." msgid_plural "Touch your device again. %d attempts remaining." @@ -220,4 +218,5 @@ msgstr "This credential is already registered on this authenticator." #: src/gui/view_model/mod.rs:284 msgid "Something went wrong. Try again later or use a different authenticator." -msgstr "Something went wrong. Try again later or use a different authenticator." +msgstr "" +"Something went wrong. Try again later or use a different authenticator." diff --git a/credentialsd-ui/src/gui/view_model/gtk/mod.rs b/credentialsd-ui/src/gui/view_model/gtk/mod.rs index cb7a647..b22555f 100644 --- a/credentialsd-ui/src/gui/view_model/gtk/mod.rs +++ b/credentialsd-ui/src/gui/view_model/gtk/mod.rs @@ -134,16 +134,16 @@ impl ViewModel { view_model.waiting_for_device(&device) } ViewUpdate::UsbNeedsPin { attempts_left } => { - let mut prompt = gettext("Enter your PIN."); - if let Some(left) = attempts_left { + let prompt = if let Some(left) = attempts_left { let localized = ngettext( - "One attempt remaining.", - "%d attempts remaining.", + "Enter your PIN. One attempt remaining.", + "Enter your PIN. %d attempts remaining.", left, ); - prompt.push(' '); - prompt += &localized.replace("%d", &format!("{}", left)); - } + localized.replace("%d", &format!("{}", left)) + } else { + gettext("Enter your PIN.") + }; view_model.set_prompt(prompt); view_model.set_usb_pin_entry_visible(true); } From 69dd5799b8e760b2b10a1226f80e1cccb5821345 Mon Sep 17 00:00:00 2001 From: Martin Sirringhaus Date: Wed, 29 Oct 2025 13:01:52 +0100 Subject: [PATCH 3/5] Show more request context in the UI (fix #69) --- credentialsd-common/src/server.rs | 2 + credentialsd-ui/po/credentialsd-ui.pot | 26 ++++--- credentialsd-ui/po/de_DE.po | 32 +++++---- credentialsd-ui/po/en_US.po | 32 +++++---- credentialsd-ui/src/gui/mod.rs | 6 +- credentialsd-ui/src/gui/view_model/mod.rs | 24 +++++-- credentialsd/src/credential_service/mod.rs | 13 +++- credentialsd/src/dbus/flow_control.rs | 16 ++++- credentialsd/src/dbus/gateway.rs | 79 +++++++++++++++++++++- 9 files changed, 178 insertions(+), 52 deletions(-) diff --git a/credentialsd-common/src/server.rs b/credentialsd-common/src/server.rs index eaa9436..864e271 100644 --- a/credentialsd-common/src/server.rs +++ b/credentialsd-common/src/server.rs @@ -484,6 +484,8 @@ where pub struct ViewRequest { pub operation: Operation, pub id: RequestId, + pub rp_id: String, + pub requesting_app: String, } fn value_to_owned(value: &Value<'_>) -> OwnedValue { diff --git a/credentialsd-ui/po/credentialsd-ui.pot b/credentialsd-ui/po/credentialsd-ui.pot index 4bd2739..fea6bc3 100644 --- a/credentialsd-ui/po/credentialsd-ui.pot +++ b/credentialsd-ui/po/credentialsd-ui.pot @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: credentialsd-ui\n" "Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" "issues\"\n" -"POT-Creation-Date: 2025-10-24 08:05+0200\n" +"POT-Creation-Date: 2025-10-29 13:04+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: \n" @@ -111,7 +111,7 @@ msgstr "" msgid "Something went wrong." msgstr "" -#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:233 +#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:247 msgid "" "Something went wrong while retrieving a credential. Please try again later " "or use a different authenticator." @@ -187,32 +187,36 @@ msgstr "" msgid "A security key" msgstr "" -#: src/gui/view_model/mod.rs:65 -msgid "Create new credential" +#: src/gui/view_model/mod.rs:70 +msgid "unknown application" msgstr "" -#: src/gui/view_model/mod.rs:66 -msgid "Use a credential" +#: src/gui/view_model/mod.rs:75 +msgid "Create a new credential for %s1 via %s2" msgstr "" -#: src/gui/view_model/mod.rs:173 +#: src/gui/view_model/mod.rs:76 +msgid "Use a credential on %s1 via %s2" +msgstr "" + +#: src/gui/view_model/mod.rs:187 msgid "Failed to select credential from device." msgstr "" -#: src/gui/view_model/mod.rs:227 +#: src/gui/view_model/mod.rs:241 msgid "No matching credentials found on this authenticator." msgstr "" -#: src/gui/view_model/mod.rs:230 +#: src/gui/view_model/mod.rs:244 msgid "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." msgstr "" -#: src/gui/view_model/mod.rs:236 +#: src/gui/view_model/mod.rs:250 msgid "This credential is already registered on this authenticator." msgstr "" -#: src/gui/view_model/mod.rs:284 +#: src/gui/view_model/mod.rs:298 msgid "Something went wrong. Try again later or use a different authenticator." msgstr "" diff --git a/credentialsd-ui/po/de_DE.po b/credentialsd-ui/po/de_DE.po index b07fe20..8fcdc1b 100644 --- a/credentialsd-ui/po/de_DE.po +++ b/credentialsd-ui/po/de_DE.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" "issues\"\n" -"POT-Creation-Date: 2025-10-24 08:05+0200\n" +"POT-Creation-Date: 2025-10-29 13:04+0100\n" "PO-Revision-Date: 2025-10-10 14:45+0200\n" "Last-Translator: Martin Sirringhaus \n" "Language: de_DE\n" @@ -106,7 +106,7 @@ msgstr "Fertig!" msgid "Something went wrong." msgstr "Etwas ist schief gegangen." -#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:233 +#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:247 msgid "" "Something went wrong while retrieving a credential. Please try again later " "or use a different authenticator." @@ -188,23 +188,29 @@ msgstr "Ein NFC-Gerät" msgid "A security key" msgstr "Ein Security-Token" -#: src/gui/view_model/mod.rs:65 -msgid "Create new credential" -msgstr "Neue Zugangsdaten erstellen" +#: src/gui/view_model/mod.rs:70 +msgid "unknown application" +msgstr "unbekannter Applikation" -#: src/gui/view_model/mod.rs:66 -msgid "Use a credential" -msgstr "Zugangsdaten abrufen" +#: src/gui/view_model/mod.rs:75 +#, fuzzy +msgid "Create a new credential for %s1 via %s2" +msgstr "Neue Zugangsdaten für %s1 via %s2 erstellen" + +#: src/gui/view_model/mod.rs:76 +#, fuzzy +msgid "Use a credential on %s1 via %s2" +msgstr "Zugangsdaten für %s1 via %s2 abrufen" -#: src/gui/view_model/mod.rs:173 +#: src/gui/view_model/mod.rs:187 msgid "Failed to select credential from device." msgstr "Zugangsdaten vom Gerät konnten nicht ausgewählt werden." -#: src/gui/view_model/mod.rs:227 +#: src/gui/view_model/mod.rs:241 msgid "No matching credentials found on this authenticator." msgstr "Keine passenden Zugangsdaten auf diesem Gerät gefunden." -#: src/gui/view_model/mod.rs:230 +#: src/gui/view_model/mod.rs:244 msgid "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." @@ -212,11 +218,11 @@ msgstr "" "Keine weiteren PIN-Eingaben erlaubt. Versuchen Sie ihr Gerät aus- und wieder " "einzustecken." -#: src/gui/view_model/mod.rs:236 +#: src/gui/view_model/mod.rs:250 msgid "This credential is already registered on this authenticator." msgstr "Diese Zugangsdaten sind bereits auf diesem Gerät registriert." -#: src/gui/view_model/mod.rs:284 +#: src/gui/view_model/mod.rs:298 msgid "Something went wrong. Try again later or use a different authenticator." msgstr "" "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal, " diff --git a/credentialsd-ui/po/en_US.po b/credentialsd-ui/po/en_US.po index acf7832..70935f7 100644 --- a/credentialsd-ui/po/en_US.po +++ b/credentialsd-ui/po/en_US.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" "issues\"\n" -"POT-Creation-Date: 2025-10-24 08:05+0200\n" +"POT-Creation-Date: 2025-10-29 13:04+0100\n" "PO-Revision-Date: 2025-10-10 14:45+0200\n" "Last-Translator: Martin Sirringhaus \n" "Language: en_US\n" @@ -107,7 +107,7 @@ msgstr "Done!" msgid "Something went wrong." msgstr "Something went wrong." -#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:233 +#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:247 msgid "" "Something went wrong while retrieving a credential. Please try again later " "or use a different authenticator." @@ -188,23 +188,29 @@ msgstr "An NFC device" msgid "A security key" msgstr "A security key" -#: src/gui/view_model/mod.rs:65 -msgid "Create new credential" -msgstr "Create new credential" +#: src/gui/view_model/mod.rs:70 +msgid "unknown application" +msgstr "unknown application" -#: src/gui/view_model/mod.rs:66 -msgid "Use a credential" -msgstr "Use a credential" +#: src/gui/view_model/mod.rs:75 +#, fuzzy +msgid "Create a new credential for %s1 via %s2" +msgstr "Create a new credential for %s1 via %s2" + +#: src/gui/view_model/mod.rs:76 +#, fuzzy +msgid "Use a credential on %s1 via %s2" +msgstr "Use a credential on %s1 via %s2" -#: src/gui/view_model/mod.rs:173 +#: src/gui/view_model/mod.rs:187 msgid "Failed to select credential from device." msgstr "Failed to select credential from device." -#: src/gui/view_model/mod.rs:227 +#: src/gui/view_model/mod.rs:241 msgid "No matching credentials found on this authenticator." msgstr "No matching credentials found on this authenticator." -#: src/gui/view_model/mod.rs:230 +#: src/gui/view_model/mod.rs:244 msgid "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." @@ -212,11 +218,11 @@ msgstr "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." -#: src/gui/view_model/mod.rs:236 +#: src/gui/view_model/mod.rs:250 msgid "This credential is already registered on this authenticator." msgstr "This credential is already registered on this authenticator." -#: src/gui/view_model/mod.rs:284 +#: src/gui/view_model/mod.rs:298 msgid "Something went wrong. Try again later or use a different authenticator." msgstr "" "Something went wrong. Try again later or use a different authenticator." diff --git a/credentialsd-ui/src/gui/mod.rs b/credentialsd-ui/src/gui/mod.rs index 08ba897..d9870b1 100644 --- a/credentialsd-ui/src/gui/mod.rs +++ b/credentialsd-ui/src/gui/mod.rs @@ -27,19 +27,19 @@ fn run_gui( flow_controller: Arc>, request: ViewRequest, ) { - let operation = request.operation; let (tx_update, rx_update) = async_std::channel::unbounded::(); let (tx_event, rx_event) = async_std::channel::unbounded::(); let event_loop = async_std::task::spawn(async move { + let request_id = request.id; let mut vm = - view_model::ViewModel::new(operation, flow_controller.clone(), rx_event, tx_update); + view_model::ViewModel::new(request, flow_controller.clone(), rx_event, tx_update); vm.start_event_loop().await; tracing::debug!("Finishing user request."); // If cancellation fails, that's fine. let _ = flow_controller .lock() .await - .cancel_request(request.id) + .cancel_request(request_id) .await; }); diff --git a/credentialsd-ui/src/gui/view_model/mod.rs b/credentialsd-ui/src/gui/view_model/mod.rs index 8930305..65e43f9 100644 --- a/credentialsd-ui/src/gui/view_model/mod.rs +++ b/credentialsd-ui/src/gui/view_model/mod.rs @@ -7,6 +7,7 @@ use async_std::{ channel::{Receiver, Sender}, sync::Mutex as AsyncMutex, }; +use credentialsd_common::server::ViewRequest; use gettextrs::gettext; use serde::{Deserialize, Serialize}; use tracing::{error, info}; @@ -29,6 +30,8 @@ where rx_event: Receiver, title: String, operation: Operation, + rp_id: String, + requesting_app: String, // This includes devices like platform authenticator, USB, hybrid devices: Vec, @@ -42,7 +45,7 @@ where impl ViewModel { pub(crate) fn new( - operation: Operation, + request: ViewRequest, flow_controller: Arc>, rx_event: Receiver, tx_update: Sender, @@ -51,7 +54,9 @@ impl ViewModel { flow_controller, rx_event, tx_update, - operation, + operation: request.operation, + rp_id: request.rp_id, + requesting_app: request.requesting_app, title: String::default(), devices: Vec::new(), selected_device: None, @@ -61,11 +66,20 @@ impl ViewModel { } async fn update_title(&mut self) { - self.title = match self.operation { - Operation::Create => gettext("Create new credential"), - Operation::Get => gettext("Use a credential"), + let requesting_app = if self.requesting_app.is_empty() { + gettext("unknown application") + } else { + self.requesting_app.clone() + }; + let mut title = match self.operation { + Operation::Create => gettext("Create a new credential for %s1 via %s2"), + Operation::Get => gettext("Use a credential on %s1 via %s2"), } .to_string(); + + title = title.replace("%s1", &self.rp_id); + title = title.replace("%s2", &requesting_app); + self.title = title; self.tx_update .send(ViewUpdate::SetTitle(self.title.to_string())) .await diff --git a/credentialsd/src/credential_service/mod.rs b/credentialsd/src/credential_service/mod.rs index 6bc8a03..1e451b7 100644 --- a/credentialsd/src/credential_service/mod.rs +++ b/credentialsd/src/credential_service/mod.rs @@ -101,6 +101,7 @@ impl pub async fn init_request( &self, request: &CredentialRequest, + requesting_app: Option, tx: Sender>, ) { let request_id = { @@ -126,9 +127,15 @@ impl CredentialRequest::CreatePublicKeyCredentialRequest(_) => Operation::Create, CredentialRequest::GetPublicKeyCredentialRequest(_) => Operation::Get, }; + let rp_id = match &request { + CredentialRequest::CreatePublicKeyCredentialRequest(r) => r.relying_party.id.clone(), + CredentialRequest::GetPublicKeyCredentialRequest(r) => r.relying_party_id.clone(), + }; let view_request = ViewRequest { operation, id: request_id, + rp_id, + requesting_app: requesting_app.unwrap_or_default(), // We can't send Options, so we send an empty string instead, if we don't know the peer }; let launch_ui_response = self @@ -364,7 +371,11 @@ mod test { cred_service .lock() .await - .init_request(&request, request_tx) + .init_request( + &request, + Some(String::from("test_hybrid_sets_credential")), + request_tx, + ) .await; user.request_hybrid_credential().await; tokio::time::timeout(Duration::from_secs(5), request_rx) diff --git a/credentialsd/src/dbus/flow_control.rs b/credentialsd/src/dbus/flow_control.rs index 9bd1e7e..b4c9674 100644 --- a/credentialsd/src/dbus/flow_control.rs +++ b/credentialsd/src/dbus/flow_control.rs @@ -43,6 +43,7 @@ pub async fn start_flow_control_service< Connection, Sender<( CredentialRequest, + Option, // Application name sending the request oneshot::Sender>, )>, )> { @@ -66,8 +67,11 @@ pub async fn start_flow_control_service< let (initiator_tx, mut initiator_rx) = mpsc::channel(2); tokio::spawn(async move { let svc = svc2; - while let Some((msg, tx)) = initiator_rx.recv().await { - svc.lock().await.init_request(&msg, tx).await; + while let Some((msg, requesting_app, tx)) = initiator_rx.recv().await { + svc.lock() + .await + .init_request(&msg, requesting_app, tx) + .await; } }); Ok((conn, initiator_tx)) @@ -300,6 +304,7 @@ enum SignalState { pub trait CredentialRequestController { fn request_credential( &self, + requesting_app: Option, request: CredentialRequest, ) -> impl Future> + Send; } @@ -307,6 +312,7 @@ pub trait CredentialRequestController { pub struct CredentialRequestControllerClient { pub initiator: Sender<( CredentialRequest, + Option, // Application name sending the request oneshot::Sender>, )>, } @@ -314,10 +320,14 @@ pub struct CredentialRequestControllerClient { impl CredentialRequestController for CredentialRequestControllerClient { async fn request_credential( &self, + requesting_app: Option, request: CredentialRequest, ) -> Result { let (tx, rx) = oneshot::channel(); - self.initiator.send((request, tx)).await.unwrap(); + self.initiator + .send((request, requesting_app, tx)) + .await + .unwrap(); let response = rx.await.map_err(|_| { tracing::error!("Credential response channel closed prematurely"); WebAuthnError::NotAllowedError diff --git a/credentialsd/src/dbus/gateway.rs b/credentialsd/src/dbus/gateway.rs index 10d0bd5..1672070 100644 --- a/credentialsd/src/dbus/gateway.rs +++ b/credentialsd/src/dbus/gateway.rs @@ -11,7 +11,7 @@ use credentialsd_common::{ }, }; use tokio::sync::Mutex as AsyncMutex; -use zbus::{fdo, interface, Connection, DBusError}; +use zbus::{fdo, interface, message::Header, Connection, DBusError}; use crate::dbus::{ create_credential_request_try_into_ctap2, create_credential_response_try_from_ctap2, @@ -44,11 +44,78 @@ struct CredentialGateway { controller: Arc>, } +async fn query_connection_peer_binary( + header: Header<'_>, + connection: &Connection, +) -> Option { + // 1. Get the sender's unique bus name + let Some(sender_unique_name) = header.sender() else { + return None; + }; + + tracing::info!("Received request from sender: {}", sender_unique_name); + + // 2. Use the connection to query the D-Bus daemon for more info + let proxy = match zbus::Proxy::new( + connection, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + ) + .await + { + Ok(p) => p, + Err(e) => { + tracing::error!("Failed to establish DBus proxy to query peer info: {e:?}"); + return None; + } + }; + + // 3. Get the Process ID (PID) of the peer + let pid_result = match proxy + .call_method("GetConnectionUnixProcessID", &(sender_unique_name)) + .await + { + Ok(pid) => pid, + Err(e) => { + tracing::error!("Failed to get peer PID via DBus: {e:?}"); + return None; + } + }; + let pid: u32 = match pid_result.body().deserialize() { + Ok(pid) => pid, + Err(e) => { + tracing::error!("Retrieved peer PID is not an integer: {e:?}"); + return None; + } + }; + + // 4. Get binary path via PID from /proc file-system + // TODO: To be REALLY sure, we may want to look at /proc/PID/exe instead. It is a symlink to + // the actual binary, giving a full path instead of only the command name. + // This should in theory be "more secure", but also may disconcert novice users with no + // technical background. + let command_name = match std::fs::read_to_string(format!("/proc/{pid}/comm")) { + Ok(c) => c.trim().to_string(), + Err(e) => { + tracing::error!( + "Failed to read /proc/{pid}/comm, so we don't know the command name of peer: {e:?}" + ); + return None; + } + }; + + tracing::info!("Request is from: {command_name}"); + Some(command_name) +} + /// These are public methods that can be called by arbitrary clients to begin a credential flow. #[interface(name = "xyz.iinuwa.credentialsd.Credentials1")] impl CredentialGateway { async fn create_credential( &self, + #[zbus(header)] header: Header<'_>, + #[zbus(connection)] connection: &Connection, request: CreateCredentialRequest, ) -> Result { let (_origin, is_same_origin, _top_origin) = @@ -75,6 +142,8 @@ impl CredentialGateway CredentialGateway CredentialGateway, + #[zbus(connection)] connection: &Connection, request: GetCredentialRequest, ) -> Result { let (_origin, is_same_origin, _top_origin) = @@ -137,12 +208,14 @@ impl CredentialGateway Date: Thu, 30 Oct 2025 14:56:52 +0100 Subject: [PATCH 4/5] Add PID and binary path to the GUI and reformat it a bit. --- credentialsd-common/src/model.rs | 17 +++- credentialsd-common/src/server.rs | 4 +- credentialsd-ui/data/resources/style.css | 2 +- credentialsd-ui/data/resources/ui/window.ui | 25 +++++ credentialsd-ui/po/credentialsd-ui.pot | 84 ++++++++++------ credentialsd-ui/po/de_DE.po | 96 ++++++++++++------- credentialsd-ui/po/en_US.po | 96 ++++++++++++------- credentialsd-ui/po/meson.build | 1 + credentialsd-ui/src/gui/view_model/gtk/mod.rs | 8 +- credentialsd-ui/src/gui/view_model/mod.rs | 53 ++++++++-- credentialsd/src/credential_service/mod.rs | 10 +- credentialsd/src/dbus/flow_control.rs | 10 +- credentialsd/src/dbus/gateway.rs | 32 +++++-- 13 files changed, 299 insertions(+), 139 deletions(-) diff --git a/credentialsd-common/src/model.rs b/credentialsd-common/src/model.rs index b3cdd20..d345131 100644 --- a/credentialsd-common/src/model.rs +++ b/credentialsd-common/src/model.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::{fmt::Display, path::PathBuf}; use serde::{Deserialize, Serialize}; use zvariant::{SerializeDict, Type}; @@ -170,9 +170,22 @@ impl Transport { } } +#[derive(Debug, Default, Clone, Serialize, Deserialize, Type)] +pub struct RequestingApplication { + pub name: String, + pub path: PathBuf, + pub pid: u32, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, Type)] +pub struct RequestingParty { + pub rp_id: String, + pub origin: String, +} + #[derive(Serialize, Deserialize)] pub enum ViewUpdate { - SetTitle(String), + SetTitle((String, String)), SetDevices(Vec), SetCredentials(Vec), diff --git a/credentialsd-common/src/server.rs b/credentialsd-common/src/server.rs index 864e271..b6b3b73 100644 --- a/credentialsd-common/src/server.rs +++ b/credentialsd-common/src/server.rs @@ -9,7 +9,7 @@ use zvariant::{ Signature, Structure, StructureBuilder, Type, Value, signature::Fields, }; -use crate::model::{BackgroundEvent, Operation}; +use crate::model::{BackgroundEvent, Operation, RequestingApplication}; const TAG_VALUE_SIGNATURE: &Signature = &Signature::Structure(Fields::Static { fields: &[&Signature::U8, &Signature::Variant], @@ -485,7 +485,7 @@ pub struct ViewRequest { pub operation: Operation, pub id: RequestId, pub rp_id: String, - pub requesting_app: String, + pub requesting_app: RequestingApplication, } fn value_to_owned(value: &Value<'_>) -> OwnedValue { diff --git a/credentialsd-ui/data/resources/style.css b/credentialsd-ui/data/resources/style.css index 3c4bd47..a117719 100644 --- a/credentialsd-ui/data/resources/style.css +++ b/credentialsd-ui/data/resources/style.css @@ -1,4 +1,4 @@ .title-header{ - font-size: 36px; + font-size: 24px; font-weight: bold; } diff --git a/credentialsd-ui/data/resources/ui/window.ui b/credentialsd-ui/data/resources/ui/window.ui index af10dac..3e9503c 100644 --- a/credentialsd-ui/data/resources/ui/window.ui +++ b/credentialsd-ui/data/resources/ui/window.ui @@ -20,6 +20,7 @@ vertical + 10 @@ -34,6 +35,30 @@ + + + + + + true + true + + + + CredentialsUiWindow + + + + + + + + + + + diff --git a/credentialsd-ui/po/credentialsd-ui.pot b/credentialsd-ui/po/credentialsd-ui.pot index fea6bc3..4d43622 100644 --- a/credentialsd-ui/po/credentialsd-ui.pot +++ b/credentialsd-ui/po/credentialsd-ui.pot @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: credentialsd-ui\n" "Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" "issues\"\n" -"POT-Creation-Date: 2025-10-29 13:04+0100\n" +"POT-Creation-Date: 2025-10-30 14:43+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: \n" @@ -16,7 +16,7 @@ msgstr "" #. LGPL-3.0-only #: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:2 #: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:8 -#: src/gui/view_model/gtk/mod.rs:372 +#: src/gui/view_model/gtk/mod.rs:378 msgid "Credential Manager" msgstr "" @@ -79,87 +79,87 @@ msgstr "" msgid "_Keyboard Shortcuts" msgstr "" -#: data/resources/ui/window.ui:43 +#: data/resources/ui/window.ui:68 msgid "Choose device" msgstr "" -#: data/resources/ui/window.ui:49 +#: data/resources/ui/window.ui:74 msgid "Devices" msgstr "" -#: data/resources/ui/window.ui:72 +#: data/resources/ui/window.ui:97 msgid "Plug in security key" msgstr "" -#: data/resources/ui/window.ui:113 +#: data/resources/ui/window.ui:138 msgid "Scan the QR code to connect your device" msgstr "" -#: data/resources/ui/window.ui:158 data/resources/ui/window.ui:164 +#: data/resources/ui/window.ui:183 data/resources/ui/window.ui:189 msgid "Choose credential" msgstr "" -#: data/resources/ui/window.ui:187 +#: data/resources/ui/window.ui:212 msgid "Complete" msgstr "" -#: data/resources/ui/window.ui:193 +#: data/resources/ui/window.ui:218 msgid "Done!" msgstr "" -#: data/resources/ui/window.ui:204 +#: data/resources/ui/window.ui:229 msgid "Something went wrong." msgstr "" -#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:247 +#: data/resources/ui/window.ui:242 src/gui/view_model/mod.rs:280 msgid "" "Something went wrong while retrieving a credential. Please try again later " "or use a different authenticator." msgstr "" -#: src/gui/view_model/gtk/mod.rs:139 +#: src/gui/view_model/gtk/mod.rs:145 msgid "Enter your PIN. One attempt remaining." msgid_plural "Enter your PIN. %d attempts remaining." msgstr[0] "" msgstr[1] "" -#: src/gui/view_model/gtk/mod.rs:145 +#: src/gui/view_model/gtk/mod.rs:151 msgid "Enter your PIN." msgstr "" -#: src/gui/view_model/gtk/mod.rs:154 +#: src/gui/view_model/gtk/mod.rs:160 msgid "Touch your device again. One attempt remaining." msgid_plural "Touch your device again. %d attempts remaining." msgstr[0] "" msgstr[1] "" -#: src/gui/view_model/gtk/mod.rs:160 +#: src/gui/view_model/gtk/mod.rs:166 msgid "Touch your device." msgstr "" -#: src/gui/view_model/gtk/mod.rs:165 +#: src/gui/view_model/gtk/mod.rs:171 msgid "Touch your device" msgstr "" -#: src/gui/view_model/gtk/mod.rs:168 +#: src/gui/view_model/gtk/mod.rs:174 msgid "Scan the QR code with your device to begin authentication." msgstr "" -#: src/gui/view_model/gtk/mod.rs:178 +#: src/gui/view_model/gtk/mod.rs:184 msgid "" "Connecting to your device. Make sure both devices are near each other and " "have Bluetooth enabled." msgstr "" -#: src/gui/view_model/gtk/mod.rs:186 +#: src/gui/view_model/gtk/mod.rs:192 msgid "Device connected. Follow the instructions on your device" msgstr "" -#: src/gui/view_model/gtk/mod.rs:312 +#: src/gui/view_model/gtk/mod.rs:318 msgid "Insert your security key." msgstr "" -#: src/gui/view_model/gtk/mod.rs:328 +#: src/gui/view_model/gtk/mod.rs:334 msgid "Multiple devices found. Please select with which to proceed." msgstr "" @@ -187,36 +187,58 @@ msgstr "" msgid "A security key" msgstr "" -#: src/gui/view_model/mod.rs:70 +#: src/gui/view_model/mod.rs:75 msgid "unknown application" msgstr "" -#: src/gui/view_model/mod.rs:75 -msgid "Create a new credential for %s1 via %s2" +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#: src/gui/view_model/mod.rs:80 +msgid "Create a passkey for %s1" msgstr "" -#: src/gui/view_model/mod.rs:76 -msgid "Use a credential on %s1 via %s2" +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#: src/gui/view_model/mod.rs:84 +msgid "Use a passkey for %s1" +msgstr "" + +#. TRANSLATORS: %s1 is the "relying party" (e.g.: domain name) where the request is coming from +#. TRANSLATORS: %s2 is the application name (e.g.: firefox) where the request is coming from, must be left untouched to make the name bold +#. TRANSLATORS: %i1 is the process ID of the requesting application +#. TRANSLATORS: %s3 is the absolute path (think: /usr/bin/firefox) of the requesting application +#: src/gui/view_model/mod.rs:96 +msgid "" +"\"%s2\" (process ID: %i1, binary: %s3) is asking to create a " +"credential to sign in to \"%s1\". Only proceed if you trust this process." +msgstr "" + +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#. TRANSLATORS: %s2 is the application name (e.g.: firefox) where the request is coming from, must be left untouched to make the name bold +#. TRANSLATORS: %i1 is the process ID of the requesting application +#. TRANSLATORS: %s3 is the absolute path (think: /usr/bin/firefox) of the requesting application +#: src/gui/view_model/mod.rs:103 +msgid "" +"\"%s2\" (process ID: %i1, binary: %s3) is asking to use a credential " +"to sign in to \"%s1\". Only proceed if you trust this process." msgstr "" -#: src/gui/view_model/mod.rs:187 +#: src/gui/view_model/mod.rs:220 msgid "Failed to select credential from device." msgstr "" -#: src/gui/view_model/mod.rs:241 +#: src/gui/view_model/mod.rs:274 msgid "No matching credentials found on this authenticator." msgstr "" -#: src/gui/view_model/mod.rs:244 +#: src/gui/view_model/mod.rs:277 msgid "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." msgstr "" -#: src/gui/view_model/mod.rs:250 +#: src/gui/view_model/mod.rs:283 msgid "This credential is already registered on this authenticator." msgstr "" -#: src/gui/view_model/mod.rs:298 +#: src/gui/view_model/mod.rs:331 msgid "Something went wrong. Try again later or use a different authenticator." msgstr "" diff --git a/credentialsd-ui/po/de_DE.po b/credentialsd-ui/po/de_DE.po index 8fcdc1b..4c11cbb 100644 --- a/credentialsd-ui/po/de_DE.po +++ b/credentialsd-ui/po/de_DE.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" "issues\"\n" -"POT-Creation-Date: 2025-10-29 13:04+0100\n" +"POT-Creation-Date: 2025-10-30 14:43+0100\n" "PO-Revision-Date: 2025-10-10 14:45+0200\n" "Last-Translator: Martin Sirringhaus \n" "Language: de_DE\n" @@ -16,7 +16,7 @@ msgstr "" #. LGPL-3.0-only #: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:2 #: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:8 -#: src/gui/view_model/gtk/mod.rs:372 +#: src/gui/view_model/gtk/mod.rs:378 msgid "Credential Manager" msgstr "Zugangsdatenmanager" @@ -74,39 +74,39 @@ msgstr "Einstellungen" msgid "_Keyboard Shortcuts" msgstr "Tastaturkürzel" -#: data/resources/ui/window.ui:43 +#: data/resources/ui/window.ui:68 msgid "Choose device" msgstr "Gerät auswählen" -#: data/resources/ui/window.ui:49 +#: data/resources/ui/window.ui:74 msgid "Devices" msgstr "Geräte" -#: data/resources/ui/window.ui:72 +#: data/resources/ui/window.ui:97 msgid "Plug in security key" msgstr "Stecken Sie Ihren Security-Token ein" -#: data/resources/ui/window.ui:113 +#: data/resources/ui/window.ui:138 msgid "Scan the QR code to connect your device" msgstr "Scannen Sie den QR-Code, um Ihr Gerät zu verbinden" -#: data/resources/ui/window.ui:158 data/resources/ui/window.ui:164 +#: data/resources/ui/window.ui:183 data/resources/ui/window.ui:189 msgid "Choose credential" msgstr "Wählen Sie Zugangsdaten aus" -#: data/resources/ui/window.ui:187 +#: data/resources/ui/window.ui:212 msgid "Complete" msgstr "Abgeschlossen" -#: data/resources/ui/window.ui:193 +#: data/resources/ui/window.ui:218 msgid "Done!" msgstr "Fertig!" -#: data/resources/ui/window.ui:204 +#: data/resources/ui/window.ui:229 msgid "Something went wrong." msgstr "Etwas ist schief gegangen." -#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:247 +#: data/resources/ui/window.ui:242 src/gui/view_model/mod.rs:280 msgid "" "Something went wrong while retrieving a credential. Please try again later " "or use a different authenticator." @@ -114,37 +114,37 @@ msgstr "" "Beim Abrufen Ihrer Zugangsdaten ist ein Fehler aufgetreten. Versuchen Sie es " "später wieder, oder verwenden Sie einen anderen Security-Token." -#: src/gui/view_model/gtk/mod.rs:139 +#: src/gui/view_model/gtk/mod.rs:145 #, fuzzy msgid "Enter your PIN. One attempt remaining." msgid_plural "Enter your PIN. %d attempts remaining." msgstr[0] "Geben Sie Ihren PIN ein. Sie haben nur noch einen Versuch." msgstr[1] "Geben Sie Ihren PIN ein. Sie haben noch %d Versuche." -#: src/gui/view_model/gtk/mod.rs:145 +#: src/gui/view_model/gtk/mod.rs:151 msgid "Enter your PIN." msgstr "Geben Sie Ihren PIN ein." -#: src/gui/view_model/gtk/mod.rs:154 +#: src/gui/view_model/gtk/mod.rs:160 msgid "Touch your device again. One attempt remaining." msgid_plural "Touch your device again. %d attempts remaining." msgstr[0] "Berühren Sie Ihr Gerät. Sie haben nur noch einen Versuch." msgstr[1] "Berühren Sie nochmal Ihr Gerät. Sie haben nur noch %d Versuche." -#: src/gui/view_model/gtk/mod.rs:160 +#: src/gui/view_model/gtk/mod.rs:166 msgid "Touch your device." msgstr "Berühren Sie Ihr Gerät." -#: src/gui/view_model/gtk/mod.rs:165 +#: src/gui/view_model/gtk/mod.rs:171 msgid "Touch your device" msgstr "Berühren Sie Ihr Gerät." -#: src/gui/view_model/gtk/mod.rs:168 +#: src/gui/view_model/gtk/mod.rs:174 msgid "Scan the QR code with your device to begin authentication." msgstr "" "Scannen Sie den QR code mit ihrem Gerät um die Authentifizierung zu beginnen." -#: src/gui/view_model/gtk/mod.rs:178 +#: src/gui/view_model/gtk/mod.rs:184 msgid "" "Connecting to your device. Make sure both devices are near each other and " "have Bluetooth enabled." @@ -152,15 +152,15 @@ msgstr "" "Verbindung zu Ihrem Gerät wird aufgebaut. Stellen Sie sicher, dass beide " "Geräte nah beieinander sind und Bluetooth aktiviert haben." -#: src/gui/view_model/gtk/mod.rs:186 +#: src/gui/view_model/gtk/mod.rs:192 msgid "Device connected. Follow the instructions on your device" msgstr "Verbindung hergestellt. Folgen Sie den Anweisungen auf Ihrem Gerät." -#: src/gui/view_model/gtk/mod.rs:312 +#: src/gui/view_model/gtk/mod.rs:318 msgid "Insert your security key." msgstr "Stecken Sie Ihren Security-Token ein." -#: src/gui/view_model/gtk/mod.rs:328 +#: src/gui/view_model/gtk/mod.rs:334 msgid "Multiple devices found. Please select with which to proceed." msgstr "Mehrere Geräte gefunden. Bitte wählen Sie einen aus, um fortzufahren." @@ -188,29 +188,53 @@ msgstr "Ein NFC-Gerät" msgid "A security key" msgstr "Ein Security-Token" -#: src/gui/view_model/mod.rs:70 +#: src/gui/view_model/mod.rs:75 msgid "unknown application" msgstr "unbekannter Applikation" -#: src/gui/view_model/mod.rs:75 -#, fuzzy -msgid "Create a new credential for %s1 via %s2" -msgstr "Neue Zugangsdaten für %s1 via %s2 erstellen" - -#: src/gui/view_model/mod.rs:76 -#, fuzzy -msgid "Use a credential on %s1 via %s2" -msgstr "Zugangsdaten für %s1 via %s2 abrufen" +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#: src/gui/view_model/mod.rs:80 +msgid "Create a passkey for %s1" +msgstr "Neuen Passkey für %s1 erstellen" + +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#: src/gui/view_model/mod.rs:84 +msgid "Use a passkey for %s1" +msgstr "Passkey für %s1 abrufen" + +#. TRANSLATORS: %s1 is the "relying party" (e.g.: domain name) where the request is coming from +#. TRANSLATORS: %s2 is the application name (e.g.: firefox) where the request is coming from, must be left untouched to make the name bold +#. TRANSLATORS: %i1 is the process ID of the requesting application +#. TRANSLATORS: %s3 is the absolute path (think: /usr/bin/firefox) of the requesting application +#: src/gui/view_model/mod.rs:96 +msgid "" +"\"%s2\" (process ID: %i1, binary: %s3) is asking to create a " +"credential to register at \"%s1\". Only proceed if you trust this process." +msgstr "" +"\"%s2\" (Prozess-ID: %i1, ausführbare Datei: %s3) möchte neue Zugangsdaten erstellen, " +"um Sie bei \"%s1\" zu registrieren. Fahren Sie nur fort, wenn Sie diesem Prozess vertrauen." + +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#. TRANSLATORS: %s2 is the application name (e.g.: firefox) where the request is coming from, must be left untouched to make the name bold +#. TRANSLATORS: %i1 is the process ID of the requesting application +#. TRANSLATORS: %s3 is the absolute path (think: /usr/bin/firefox) of the requesting application +#: src/gui/view_model/mod.rs:103 +msgid "" +"\"%s2\" (process ID: %i1, binary: %s3) is asking to use a credential " +"to sign in to \"%s1\". Only proceed if you trust this process." +msgstr "" +"\"%s2\" (Prozess-ID: %i1, ausführbare Datei: %s3) möchte Zugangsdaten abrufen, um " +"Sie bei \"%s1\" anzumelden. Fahren Sie nur fort, wenn Sie diesem Prozess vertrauen." -#: src/gui/view_model/mod.rs:187 +#: src/gui/view_model/mod.rs:220 msgid "Failed to select credential from device." msgstr "Zugangsdaten vom Gerät konnten nicht ausgewählt werden." -#: src/gui/view_model/mod.rs:241 +#: src/gui/view_model/mod.rs:274 msgid "No matching credentials found on this authenticator." msgstr "Keine passenden Zugangsdaten auf diesem Gerät gefunden." -#: src/gui/view_model/mod.rs:244 +#: src/gui/view_model/mod.rs:277 msgid "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." @@ -218,11 +242,11 @@ msgstr "" "Keine weiteren PIN-Eingaben erlaubt. Versuchen Sie ihr Gerät aus- und wieder " "einzustecken." -#: src/gui/view_model/mod.rs:250 +#: src/gui/view_model/mod.rs:283 msgid "This credential is already registered on this authenticator." msgstr "Diese Zugangsdaten sind bereits auf diesem Gerät registriert." -#: src/gui/view_model/mod.rs:298 +#: src/gui/view_model/mod.rs:331 msgid "Something went wrong. Try again later or use a different authenticator." msgstr "" "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal, " diff --git a/credentialsd-ui/po/en_US.po b/credentialsd-ui/po/en_US.po index 70935f7..34fd54d 100644 --- a/credentialsd-ui/po/en_US.po +++ b/credentialsd-ui/po/en_US.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \"https://github.com/linux-credentials/credentialsd/" "issues\"\n" -"POT-Creation-Date: 2025-10-29 13:04+0100\n" +"POT-Creation-Date: 2025-10-30 14:43+0100\n" "PO-Revision-Date: 2025-10-10 14:45+0200\n" "Last-Translator: Martin Sirringhaus \n" "Language: en_US\n" @@ -15,7 +15,7 @@ msgstr "" #. LGPL-3.0-only #: data/xyz.iinuwa.credentialsd.CredentialsUi.desktop.in.in:2 #: data/xyz.iinuwa.credentialsd.CredentialsUi.metainfo.xml.in.in:8 -#: src/gui/view_model/gtk/mod.rs:372 +#: src/gui/view_model/gtk/mod.rs:378 msgid "Credential Manager" msgstr "Credential Manager" @@ -75,39 +75,39 @@ msgstr "_Preferences" msgid "_Keyboard Shortcuts" msgstr "Keyboard Shortcuts" -#: data/resources/ui/window.ui:43 +#: data/resources/ui/window.ui:68 msgid "Choose device" msgstr "Choose device" -#: data/resources/ui/window.ui:49 +#: data/resources/ui/window.ui:74 msgid "Devices" msgstr "Devices" -#: data/resources/ui/window.ui:72 +#: data/resources/ui/window.ui:97 msgid "Plug in security key" msgstr "Plug in security key" -#: data/resources/ui/window.ui:113 +#: data/resources/ui/window.ui:138 msgid "Scan the QR code to connect your device" msgstr "Scan the QR code to connect your device" -#: data/resources/ui/window.ui:158 data/resources/ui/window.ui:164 +#: data/resources/ui/window.ui:183 data/resources/ui/window.ui:189 msgid "Choose credential" msgstr "Choose credential" -#: data/resources/ui/window.ui:187 +#: data/resources/ui/window.ui:212 msgid "Complete" msgstr "Complete" -#: data/resources/ui/window.ui:193 +#: data/resources/ui/window.ui:218 msgid "Done!" msgstr "Done!" -#: data/resources/ui/window.ui:204 +#: data/resources/ui/window.ui:229 msgid "Something went wrong." msgstr "Something went wrong." -#: data/resources/ui/window.ui:217 src/gui/view_model/mod.rs:247 +#: data/resources/ui/window.ui:242 src/gui/view_model/mod.rs:280 msgid "" "Something went wrong while retrieving a credential. Please try again later " "or use a different authenticator." @@ -115,36 +115,36 @@ msgstr "" "Something went wrong while retrieving a credential. Please try again later " "or use a different authenticator." -#: src/gui/view_model/gtk/mod.rs:139 +#: src/gui/view_model/gtk/mod.rs:145 #, fuzzy msgid "Enter your PIN. One attempt remaining." msgid_plural "Enter your PIN. %d attempts remaining." msgstr[0] "Enter your PIN. One attempt remaining." msgstr[1] "Enter your PIN. %d attempts remaining." -#: src/gui/view_model/gtk/mod.rs:145 +#: src/gui/view_model/gtk/mod.rs:151 msgid "Enter your PIN." msgstr "Enter your PIN." -#: src/gui/view_model/gtk/mod.rs:154 +#: src/gui/view_model/gtk/mod.rs:160 msgid "Touch your device again. One attempt remaining." msgid_plural "Touch your device again. %d attempts remaining." msgstr[0] "Touch your device again. One attempt remaining." msgstr[1] "Touch your device again. %d attempts remaining." -#: src/gui/view_model/gtk/mod.rs:160 +#: src/gui/view_model/gtk/mod.rs:166 msgid "Touch your device." msgstr "Touch your device." -#: src/gui/view_model/gtk/mod.rs:165 +#: src/gui/view_model/gtk/mod.rs:171 msgid "Touch your device" msgstr "Touch your device" -#: src/gui/view_model/gtk/mod.rs:168 +#: src/gui/view_model/gtk/mod.rs:174 msgid "Scan the QR code with your device to begin authentication." msgstr "Scan the QR code with your device to begin authentication." -#: src/gui/view_model/gtk/mod.rs:178 +#: src/gui/view_model/gtk/mod.rs:184 msgid "" "Connecting to your device. Make sure both devices are near each other and " "have Bluetooth enabled." @@ -152,15 +152,15 @@ msgstr "" "Connecting to your device. Make sure both devices are near each other and " "have Bluetooth enabled." -#: src/gui/view_model/gtk/mod.rs:186 +#: src/gui/view_model/gtk/mod.rs:192 msgid "Device connected. Follow the instructions on your device" msgstr "Device connected. Follow the instructions on your device" -#: src/gui/view_model/gtk/mod.rs:312 +#: src/gui/view_model/gtk/mod.rs:318 msgid "Insert your security key." msgstr "Insert your security key." -#: src/gui/view_model/gtk/mod.rs:328 +#: src/gui/view_model/gtk/mod.rs:334 msgid "Multiple devices found. Please select with which to proceed." msgstr "Multiple devices found. Please select with which to proceed." @@ -188,29 +188,53 @@ msgstr "An NFC device" msgid "A security key" msgstr "A security key" -#: src/gui/view_model/mod.rs:70 +#: src/gui/view_model/mod.rs:75 msgid "unknown application" msgstr "unknown application" -#: src/gui/view_model/mod.rs:75 -#, fuzzy -msgid "Create a new credential for %s1 via %s2" -msgstr "Create a new credential for %s1 via %s2" - -#: src/gui/view_model/mod.rs:76 -#, fuzzy -msgid "Use a credential on %s1 via %s2" -msgstr "Use a credential on %s1 via %s2" +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#: src/gui/view_model/mod.rs:80 +msgid "Create a passkey for %s1" +msgstr "Create a passkey for %s1" + +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#: src/gui/view_model/mod.rs:84 +msgid "Use a passkey for %s1" +msgstr "Use a passkey for %s1" + +#. TRANSLATORS: %s1 is the "relying party" (e.g.: domain name) where the request is coming from +#. TRANSLATORS: %s2 is the application name (e.g.: firefox) where the request is coming from, must be left untouched to make the name bold +#. TRANSLATORS: %i1 is the process ID of the requesting application +#. TRANSLATORS: %s3 is the absolute path (think: /usr/bin/firefox) of the requesting application +#: src/gui/view_model/mod.rs:96 +msgid "" +"\"%s2\" (process ID: %i1, binary: %s3) is asking to create a " +"credential to register at \"%s1\". Only proceed if you trust this process." +msgstr "" +"\"%s2\" (process ID: %i1, binary: %s3) is asking to create a " +"credential to register at \"%s1\". Only proceed if you trust this process." + +#. TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from +#. TRANSLATORS: %s2 is the application name (e.g.: firefox) where the request is coming from, must be left untouched to make the name bold +#. TRANSLATORS: %i1 is the process ID of the requesting application +#. TRANSLATORS: %s3 is the absolute path (think: /usr/bin/firefox) of the requesting application +#: src/gui/view_model/mod.rs:103 +msgid "" +"\"%s2\" (process ID: %i1, binary: %s3) is asking to use a credential " +"to sign in to \"%s1\". Only proceed if you trust this process." +msgstr "" +"\"%s2\" (process ID: %i1, binary: %s3) is asking to use a credential " +"to sign in to \"%s1\". Only proceed if you trust this process." -#: src/gui/view_model/mod.rs:187 +#: src/gui/view_model/mod.rs:220 msgid "Failed to select credential from device." msgstr "Failed to select credential from device." -#: src/gui/view_model/mod.rs:241 +#: src/gui/view_model/mod.rs:274 msgid "No matching credentials found on this authenticator." msgstr "No matching credentials found on this authenticator." -#: src/gui/view_model/mod.rs:244 +#: src/gui/view_model/mod.rs:277 msgid "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." @@ -218,11 +242,11 @@ msgstr "" "No more PIN attempts allowed. Try removing your device and plugging it back " "in." -#: src/gui/view_model/mod.rs:250 +#: src/gui/view_model/mod.rs:283 msgid "This credential is already registered on this authenticator." msgstr "This credential is already registered on this authenticator." -#: src/gui/view_model/mod.rs:298 +#: src/gui/view_model/mod.rs:331 msgid "Something went wrong. Try again later or use a different authenticator." msgstr "" "Something went wrong. Try again later or use a different authenticator." diff --git a/credentialsd-ui/po/meson.build b/credentialsd-ui/po/meson.build index 3b8d88e..75cb319 100644 --- a/credentialsd-ui/po/meson.build +++ b/credentialsd-ui/po/meson.build @@ -6,5 +6,6 @@ i18n.gettext(gettext_package, '--from-code=UTF-8', '--copyright-holder="The Credentials for Linux Project"', '--msgid-bugs-address="https://github.com/linux-credentials/credentialsd/issues"', + '--add-comments=TRANSLATORS:' ], ) diff --git a/credentialsd-ui/src/gui/view_model/gtk/mod.rs b/credentialsd-ui/src/gui/view_model/gtk/mod.rs index b22555f..d39e125 100644 --- a/credentialsd-ui/src/gui/view_model/gtk/mod.rs +++ b/credentialsd-ui/src/gui/view_model/gtk/mod.rs @@ -36,6 +36,9 @@ mod imp { #[property(get, set)] pub title: RefCell, + #[property(get, set)] + pub subtitle: RefCell, + #[property(get, set)] pub devices: RefCell, @@ -122,7 +125,10 @@ impl ViewModel { // TODO: hack so I don't have to unset this in every event manually. view_model.set_usb_pin_entry_visible(false); match update { - ViewUpdate::SetTitle(title) => view_model.set_title(title), + ViewUpdate::SetTitle((title, subtitle)) => { + view_model.set_title(title); + view_model.set_subtitle(subtitle); + } ViewUpdate::SetDevices(devices) => { view_model.update_devices(&devices) } diff --git a/credentialsd-ui/src/gui/view_model/mod.rs b/credentialsd-ui/src/gui/view_model/mod.rs index 65e43f9..9d545fd 100644 --- a/credentialsd-ui/src/gui/view_model/mod.rs +++ b/credentialsd-ui/src/gui/view_model/mod.rs @@ -7,6 +7,7 @@ use async_std::{ channel::{Receiver, Sender}, sync::Mutex as AsyncMutex, }; +use credentialsd_common::model::RequestingApplication; use credentialsd_common::server::ViewRequest; use gettextrs::gettext; use serde::{Deserialize, Serialize}; @@ -29,9 +30,10 @@ where tx_update: Sender, rx_event: Receiver, title: String, + subtitle: String, operation: Operation, rp_id: String, - requesting_app: String, + requesting_app: RequestingApplication, // This includes devices like platform authenticator, USB, hybrid devices: Vec, @@ -58,6 +60,7 @@ impl ViewModel { rp_id: request.rp_id, requesting_app: request.requesting_app, title: String::default(), + subtitle: String::default(), devices: Vec::new(), selected_device: None, hybrid_qr_state: HybridState::default(), @@ -66,22 +69,52 @@ impl ViewModel { } async fn update_title(&mut self) { - let requesting_app = if self.requesting_app.is_empty() { - gettext("unknown application") - } else { - self.requesting_app.clone() + let mut requesting_app = self.requesting_app.clone(); + + if requesting_app.name.is_empty() { + requesting_app.name = gettext("unknown application"); }; let mut title = match self.operation { - Operation::Create => gettext("Create a new credential for %s1 via %s2"), - Operation::Get => gettext("Use a credential on %s1 via %s2"), + Operation::Create => { + // TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from + gettext("Create a passkey for %s1") + } + Operation::Get => { + // TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from + gettext("Use a passkey for %s1") + } } .to_string(); - title = title.replace("%s1", &self.rp_id); - title = title.replace("%s2", &requesting_app); + + let mut subtitle = match self.operation { + Operation::Create => { + // TRANSLATORS: %s1 is the "relying party" (e.g.: domain name) where the request is coming from + // TRANSLATORS: %s2 is the application name (e.g.: firefox) where the request is coming from, must be left untouched to make the name bold + // TRANSLATORS: %i1 is the process ID of the requesting application + // TRANSLATORS: %s3 is the absolute path (think: /usr/bin/firefox) of the requesting application + gettext("\"%s2\" (process ID: %i1, binary: %s3) is asking to create a credential to register at \"%s1\". Only proceed if you trust this process.") + } + Operation::Get => { + // TRANSLATORS: %s1 is the "relying party" (think: domain name) where the request is coming from + // TRANSLATORS: %s2 is the application name (e.g.: firefox) where the request is coming from, must be left untouched to make the name bold + // TRANSLATORS: %i1 is the process ID of the requesting application + // TRANSLATORS: %s3 is the absolute path (think: /usr/bin/firefox) of the requesting application + gettext("\"%s2\" (process ID: %i1, binary: %s3) is asking to use a credential to sign in to \"%s1\". Only proceed if you trust this process.") + } + } + .to_string(); + subtitle = subtitle.replace("%s1", &self.rp_id); + subtitle = subtitle.replace("%i1", &format!("{}", requesting_app.pid)); + subtitle = subtitle.replace("%s2", &requesting_app.name); + subtitle = subtitle.replace("%s3", &requesting_app.path.to_string_lossy()); self.title = title; + self.subtitle = subtitle; self.tx_update - .send(ViewUpdate::SetTitle(self.title.to_string())) + .send(ViewUpdate::SetTitle(( + self.title.to_string(), + self.subtitle.to_string(), + ))) .await .unwrap(); } diff --git a/credentialsd/src/credential_service/mod.rs b/credentialsd/src/credential_service/mod.rs index 1e451b7..9b098df 100644 --- a/credentialsd/src/credential_service/mod.rs +++ b/credentialsd/src/credential_service/mod.rs @@ -20,7 +20,7 @@ use tokio::sync::oneshot::Sender; use credentialsd_common::{ model::{ CredentialRequest, CredentialResponse, Device, Error as CredentialServiceError, Operation, - Transport, + RequestingApplication, Transport, }, server::{RequestId, ViewRequest}, }; @@ -101,7 +101,7 @@ impl pub async fn init_request( &self, request: &CredentialRequest, - requesting_app: Option, + requesting_app: Option, tx: Sender>, ) { let request_id = { @@ -371,11 +371,7 @@ mod test { cred_service .lock() .await - .init_request( - &request, - Some(String::from("test_hybrid_sets_credential")), - request_tx, - ) + .init_request(&request, None, request_tx) .await; user.request_hybrid_credential().await; tokio::time::timeout(Duration::from_secs(5), request_rx) diff --git a/credentialsd/src/dbus/flow_control.rs b/credentialsd/src/dbus/flow_control.rs index b4c9674..b7ad274 100644 --- a/credentialsd/src/dbus/flow_control.rs +++ b/credentialsd/src/dbus/flow_control.rs @@ -6,7 +6,7 @@ use std::{collections::VecDeque, fmt::Debug, sync::Arc}; use credentialsd_common::model::{ BackgroundEvent, CredentialRequest, CredentialResponse, Error as CredentialServiceError, - WebAuthnError, + RequestingApplication, WebAuthnError, }; use credentialsd_common::server::{Device, RequestId}; use futures_lite::StreamExt; @@ -43,7 +43,7 @@ pub async fn start_flow_control_service< Connection, Sender<( CredentialRequest, - Option, // Application name sending the request + Option, // Application name sending the request oneshot::Sender>, )>, )> { @@ -304,7 +304,7 @@ enum SignalState { pub trait CredentialRequestController { fn request_credential( &self, - requesting_app: Option, + requesting_app: Option, request: CredentialRequest, ) -> impl Future> + Send; } @@ -312,7 +312,7 @@ pub trait CredentialRequestController { pub struct CredentialRequestControllerClient { pub initiator: Sender<( CredentialRequest, - Option, // Application name sending the request + Option, // Application name sending the request oneshot::Sender>, )>, } @@ -320,7 +320,7 @@ pub struct CredentialRequestControllerClient { impl CredentialRequestController for CredentialRequestControllerClient { async fn request_credential( &self, - requesting_app: Option, + requesting_app: Option, request: CredentialRequest, ) -> Result { let (tx, rx) = oneshot::channel(); diff --git a/credentialsd/src/dbus/gateway.rs b/credentialsd/src/dbus/gateway.rs index 1672070..c2288e3 100644 --- a/credentialsd/src/dbus/gateway.rs +++ b/credentialsd/src/dbus/gateway.rs @@ -4,7 +4,10 @@ use std::sync::Arc; use credentialsd_common::{ - model::{CredentialRequest, CredentialResponse, GetClientCapabilitiesResponse, WebAuthnError}, + model::{ + CredentialRequest, CredentialResponse, GetClientCapabilitiesResponse, + RequestingApplication, WebAuthnError, + }, server::{ CreateCredentialRequest, CreateCredentialResponse, GetCredentialRequest, GetCredentialResponse, @@ -47,13 +50,11 @@ struct CredentialGateway { async fn query_connection_peer_binary( header: Header<'_>, connection: &Connection, -) -> Option { +) -> Option { // 1. Get the sender's unique bus name - let Some(sender_unique_name) = header.sender() else { - return None; - }; + let sender_unique_name = header.sender()?; - tracing::info!("Received request from sender: {}", sender_unique_name); + tracing::debug!("Received request from sender: {}", sender_unique_name); // 2. Use the connection to query the D-Bus daemon for more info let proxy = match zbus::Proxy::new( @@ -104,9 +105,24 @@ async fn query_connection_peer_binary( return None; } }; + tracing::debug!("Request is from: {command_name}"); + + let exe_path = match std::fs::read_link(format!("/proc/{pid}/exe")) { + Ok(p) => p, + Err(e) => { + tracing::error!( + "Failed to follow link of /proc/{pid}/exe, so we don't know the executable path of peer: {e:?}" + ); + return None; + } + }; + tracing::debug!("Request is from: {exe_path:?}"); - tracing::info!("Request is from: {command_name}"); - Some(command_name) + Some(RequestingApplication { + name: command_name, + path: exe_path, + pid, + }) } /// These are public methods that can be called by arbitrary clients to begin a credential flow. From 7ba365577e52ba22762a5d42b521c216351a6ac0 Mon Sep 17 00:00:00 2001 From: Martin Sirringhaus Date: Thu, 30 Oct 2025 15:52:55 +0100 Subject: [PATCH 5/5] Make the scrolled widgets a little bit prettier by expanding dynamically up to a given limit instead of giving them a big minimal height --- credentialsd-ui/data/resources/ui/window.ui | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/credentialsd-ui/data/resources/ui/window.ui b/credentialsd-ui/data/resources/ui/window.ui index 3e9503c..5539cba 100644 --- a/credentialsd-ui/data/resources/ui/window.ui +++ b/credentialsd-ui/data/resources/ui/window.ui @@ -76,7 +76,8 @@ - 256 + true + 256 @@ -191,7 +192,8 @@ - 256 + true + 256