diff --git a/crates/pdfs-gui/src/main.rs b/crates/pdfs-gui/src/main.rs
index d177c80..083466e 100644
--- a/crates/pdfs-gui/src/main.rs
+++ b/crates/pdfs-gui/src/main.rs
@@ -109,7 +109,8 @@ fn sync_line(items: &[TransferItem], jobs: &[JobItem]) -> String {
&[("title", &j.title), ("done", &done), ("total", &total)],
)
}
- Some(j) => format!("{}…", j.title),
+ // Translators: {title} names a running job whose size is not known yet.
+ Some(j) => gettext_f("{title}…", &[("title", &j.title)]),
None => String::new(),
},
[t] => {
@@ -118,10 +119,19 @@ fn sync_line(items: &[TransferItem], jobs: &[JobItem]) -> String {
TransferDirection::Upload => "↑",
};
if t.bytes_total == 0 {
- format!("{arrow} {}…", t.name)
+ // Translators: one transfer of unknown size; {arrow} is ↑ or ↓, {name} a file name.
+ gettext_f("{arrow} {name}…", &[("arrow", arrow), ("name", &t.name)])
} else {
let pct = (t.bytes_completed * 100 / t.bytes_total.max(1)).min(100);
- format!("{arrow} {} ({pct}%)", t.name)
+ // Translators: one transfer; {arrow} is ↑ or ↓, {name} a file name, {percent} how much is done.
+ gettext_f(
+ "{arrow} {name} ({percent}%)",
+ &[
+ ("arrow", arrow),
+ ("name", &t.name),
+ ("percent", &pct.to_string()),
+ ],
+ )
}
}
_ => {
diff --git a/docs/BUGS.md b/docs/BUGS.md
index a964dc0..f37c358 100644
--- a/docs/BUGS.md
+++ b/docs/BUGS.md
@@ -100,6 +100,7 @@ a live account since.
| Entry | Title |
|---|---|
+| [B247](#b247--tray-status-lines-are-not-translated) | Tray status lines are not translated |
| [B246](#b246--signing-in-with-a-locked-keyring-freezes-the-app-window) | Signing in with a locked keyring freezes the app window |
| [B245](#b245--any-child-of-the-daemon-can-answer-the-watchdog-for-it) | Any child of the daemon can answer the watchdog for it |
| [B244](#b244--photos-without-a-sidecar-or-a-dated-name-are-filed-under-the-upload-day) | Photos without a sidecar or a dated name are filed under the upload day |
@@ -349,6 +350,25 @@ a live account since.
Newest first.
+### B247 — Tray status lines are not translated
+
+**Status:** Fixed, not verified live
+**Found:** 2026-10-16, architecture audit (`FINDINGS.md` section 3, tray strings).
+**Where:** `sync_line` in `crates/pdfs-gui/src/main.rs`
+
+**Repro:** Run the tray in German or French while one file uploads. The tray's status line reads
+`↑ report.pdf (45%)`, with the percent sign as English writes it.
+
+**Cause.** Three status lines of the tray were built with `format!`, outside gettext, so no
+language could adapt them.
+
+**Fix.** They go through `gettext_f` and are translated in every catalog. German and French put
+a space before the percent sign, as they already do for the storage quota.
+
+**Test:** `msgfmt --check --statistics` on every catalog reports only translated messages.
+
+---
+
### B246 — Signing in with a locked keyring freezes the app window
**Status:** Fixed, not verified live
diff --git a/po/de.po b/po/de.po
index 2f70772..44d560a 100644
--- a/po/de.po
+++ b/po/de.po
@@ -5,7 +5,7 @@ msgid ""
msgstr ""
"Project-Id-Version: proton-drive-linux 2.1.0\n"
"Report-Msgid-Bugs-To: https://github.com/narrrl/proton-drive-linux/issues\n"
-"POT-Creation-Date: 2026-10-16 11:55+0200\n"
+"POT-Creation-Date: 2026-10-16 12:29+0200\n"
"PO-Revision-Date: 2026-09-23 12:00+0200\n"
"Last-Translator: Proton Drive for Linux contributors\n"
"Language-Team: German\n"
@@ -31,7 +31,7 @@ msgstr[1] "vor {n} Min."
#. Translators: strftime format for the time a photo was taken, shown on its tile, such as "14:05".
#: crates/pdfs-gui/src/app/dates.rs:44
#: crates/pdfs-gui/src/app/pages/activity.rs:840
-#: crates/pdfs-gui/src/app/pages/photos.rs:2774
+#: crates/pdfs-gui/src/app/pages/photos.rs:2772
msgid "%H:%M"
msgstr "%H:%M"
@@ -57,87 +57,87 @@ msgstr "%-d. %b %Y"
msgid "%A, %B %-d, %Y, %H:%M"
msgstr "%A, %-d. %B %Y, %H:%M"
-#: crates/pdfs-gui/src/app/main.rs:764
+#: crates/pdfs-gui/src/app/main.rs:767
#: crates/pdfs-gui/src/app/pages/activity_text.rs:539
#: crates/pdfs-gui/src/app/pages/browser.rs:454
#: crates/pdfs-gui/src/app/pages/browser.rs:833
-#: crates/pdfs-gui/src/app/pages/browser.rs:1238
-#: crates/pdfs-gui/src/app/pages/browser.rs:2104
-#: crates/pdfs-gui/src/app/pages/browser.rs:2181
+#: crates/pdfs-gui/src/app/pages/browser.rs:1239
+#: crates/pdfs-gui/src/app/pages/browser.rs:2105
+#: crates/pdfs-gui/src/app/pages/browser.rs:2182
#: crates/pdfs-gui/src/app/pages/locations.rs:1667
#: crates/pdfs-gui/src/app/pages/shared_by_me.rs:252
#: crates/pdfs-gui/src/app/pages/trash.rs:191 crates/pdfs-gui/src/prompt.rs:171
msgid "My files"
msgstr "Meine Dateien"
-#: crates/pdfs-gui/src/app/main.rs:767 crates/pdfs-gui/src/app/main.rs:1412
+#: crates/pdfs-gui/src/app/main.rs:770 crates/pdfs-gui/src/app/main.rs:1415
#: crates/pdfs-gui/src/app/pages/albums.rs:638
#: crates/pdfs-gui/src/app/pages/photos.rs:845
#: crates/pdfs-gui/src/app/pages/places.rs:441
msgid "Photos"
msgstr "Fotos"
-#: crates/pdfs-gui/src/app/main.rs:772
+#: crates/pdfs-gui/src/app/main.rs:775
#: crates/pdfs-gui/src/app/pages/shared.rs:168
#: crates/pdfs-gui/src/app/pages/shared.rs:484
msgid "Shared with me"
msgstr "Mit mir geteilt"
-#: crates/pdfs-gui/src/app/main.rs:777
+#: crates/pdfs-gui/src/app/main.rs:780
#: crates/pdfs-gui/src/app/pages/shared_by_me.rs:120
msgid "Shared by me"
msgstr "Von mir geteilt"
-#: crates/pdfs-gui/src/app/main.rs:780
+#: crates/pdfs-gui/src/app/main.rs:783
#: crates/pdfs-gui/src/app/pages/devices.rs:140
#: crates/pdfs-gui/src/app/pages/devices.rs:489
msgid "Computers"
msgstr "Computer"
-#: crates/pdfs-gui/src/app/main.rs:781
+#: crates/pdfs-gui/src/app/main.rs:784
#: crates/pdfs-gui/src/app/pages/trash.rs:101
msgid "Trash"
msgstr "Papierkorb"
#. Translators: sidebar entry for the page listing this computer's synced folders.
-#: crates/pdfs-gui/src/app/main.rs:783
+#: crates/pdfs-gui/src/app/main.rs:786
msgid "Sync"
msgstr "Synchronisierung"
#. Translators: a failed action's toast. {what} says what failed, such as
#. "Couldn't rename"; {detail} is the reason, usually untranslated text
#. from the mount service.
-#: crates/pdfs-gui/src/app/main.rs:1109
+#: crates/pdfs-gui/src/app/main.rs:1112
#: crates/pdfs-gui/src/app/pages/takeout.rs:714
#, rust-format
msgid "{what}: {detail}"
msgstr "{what}: {detail}"
-#: crates/pdfs-gui/src/app/main.rs:1132
+#: crates/pdfs-gui/src/app/main.rs:1135
msgid "You're offline"
msgstr "Keine Internetverbindung"
-#: crates/pdfs-gui/src/app/main.rs:1133
+#: crates/pdfs-gui/src/app/main.rs:1136
msgid "That's not there any more"
msgstr "Das ist nicht mehr vorhanden"
-#: crates/pdfs-gui/src/app/main.rs:1134
+#: crates/pdfs-gui/src/app/main.rs:1137
msgid "You don't have access to that"
msgstr "Kein Zugriff darauf"
-#: crates/pdfs-gui/src/app/main.rs:1135
+#: crates/pdfs-gui/src/app/main.rs:1138
msgid "Something changed this first"
msgstr "Etwas anderes hat dies zuerst geändert"
-#: crates/pdfs-gui/src/app/main.rs:1136
+#: crates/pdfs-gui/src/app/main.rs:1139
msgid "Your Proton Drive is full"
msgstr "Das Proton Drive ist voll"
-#: crates/pdfs-gui/src/app/main.rs:1223
+#: crates/pdfs-gui/src/app/main.rs:1226
msgid "Proton Drive for Linux"
msgstr "Proton Drive for Linux"
-#: crates/pdfs-gui/src/app/main.rs:1231
+#: crates/pdfs-gui/src/app/main.rs:1234
msgid ""
"Files-on-demand Proton Drive for the Linux desktop.\n"
"\n"
@@ -149,23 +149,23 @@ msgstr ""
"Inoffizieller Client – nicht mit Proton AG verbunden, von Proton AG "
"unterstützt oder befürwortet."
-#: crates/pdfs-gui/src/app/main.rs:1236
+#: crates/pdfs-gui/src/app/main.rs:1239
msgid "Source Code"
msgstr "Quellcode"
-#: crates/pdfs-gui/src/app/main.rs:1240
+#: crates/pdfs-gui/src/app/main.rs:1243
msgid "Proton Terms of Service"
msgstr "Nutzungsbedingungen von Proton"
-#: crates/pdfs-gui/src/app/main.rs:1244
+#: crates/pdfs-gui/src/app/main.rs:1247
msgid "Proton Privacy Policy"
msgstr "Datenschutzerklärung von Proton"
-#: crates/pdfs-gui/src/app/main.rs:1249
+#: crates/pdfs-gui/src/app/main.rs:1252
msgid "Disclaimer"
msgstr "Haftungsausschluss"
-#: crates/pdfs-gui/src/app/main.rs:1253
+#: crates/pdfs-gui/src/app/main.rs:1256
msgid ""
"This is an unofficial, community-built client. It is not affiliated with, "
"endorsed by, or supported by Proton AG.\n"
@@ -190,7 +190,7 @@ msgstr ""
"bereitgestellt. Daten, deren Verlust nicht hinnehmbar ist, sollten "
"zusätzlich gesichert werden."
-#: crates/pdfs-gui/src/app/main.rs:1261
+#: crates/pdfs-gui/src/app/main.rs:1264
msgid ""
"Places are named with data from GeoNames, licensed under GeoNames, lizenziert unter CC BY 4.0."
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
@@ -210,7 +210,7 @@ msgstr ""
"href=\"https://www.naturalearthdata.com/\">Natural Earth gezeichnet, die "
"gemeinfrei sind."
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the GeoNames, licensed under GeoNames, licensed under CC BY 4.0."
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
@@ -207,7 +207,7 @@ msgstr ""
"\">Natural Earth land outlines and country borders, which are in the "
"public domain."
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the GeoNames, licensed under GeoNames, con licencia CC BY 4.0."
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
@@ -208,7 +208,7 @@ msgstr ""
"fronteras de países de Natural "
"Earth, que son de dominio público."
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the GeoNames, licensed under GeoNames, sous licence CC BY 4.0."
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
@@ -209,7 +209,7 @@ msgstr ""
"frontières des pays de Natural "
"Earth, qui sont dans le domaine public."
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the GeoNames, licensed under GeoNames, con licenza CC BY 4.0."
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
@@ -207,7 +207,7 @@ msgstr ""
"confini nazionali di Natural "
"Earth, che sono di pubblico dominio."
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the GeoNames, licensed under GeoNames, gelicentieerd onder CC BY 4.0."
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
@@ -208,7 +208,7 @@ msgstr ""
"href=\"https://www.naturalearthdata.com/\">Natural Earth, die in het "
"publieke domein vallen."
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the \n"
"Language-Team: LANGUAGE \n"
@@ -34,7 +34,7 @@ msgstr[1] ""
#. Translators: strftime format for the time a photo was taken, shown on its tile, such as "14:05".
#: crates/pdfs-gui/src/app/dates.rs:44
#: crates/pdfs-gui/src/app/pages/activity.rs:840
-#: crates/pdfs-gui/src/app/pages/photos.rs:2774
+#: crates/pdfs-gui/src/app/pages/photos.rs:2772
msgid "%H:%M"
msgstr ""
@@ -60,87 +60,87 @@ msgstr ""
msgid "%A, %B %-d, %Y, %H:%M"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:764
+#: crates/pdfs-gui/src/app/main.rs:767
#: crates/pdfs-gui/src/app/pages/activity_text.rs:539
#: crates/pdfs-gui/src/app/pages/browser.rs:454
#: crates/pdfs-gui/src/app/pages/browser.rs:833
-#: crates/pdfs-gui/src/app/pages/browser.rs:1238
-#: crates/pdfs-gui/src/app/pages/browser.rs:2104
-#: crates/pdfs-gui/src/app/pages/browser.rs:2181
+#: crates/pdfs-gui/src/app/pages/browser.rs:1239
+#: crates/pdfs-gui/src/app/pages/browser.rs:2105
+#: crates/pdfs-gui/src/app/pages/browser.rs:2182
#: crates/pdfs-gui/src/app/pages/locations.rs:1667
#: crates/pdfs-gui/src/app/pages/shared_by_me.rs:252
#: crates/pdfs-gui/src/app/pages/trash.rs:191 crates/pdfs-gui/src/prompt.rs:171
msgid "My files"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:767 crates/pdfs-gui/src/app/main.rs:1412
+#: crates/pdfs-gui/src/app/main.rs:770 crates/pdfs-gui/src/app/main.rs:1415
#: crates/pdfs-gui/src/app/pages/albums.rs:638
#: crates/pdfs-gui/src/app/pages/photos.rs:845
#: crates/pdfs-gui/src/app/pages/places.rs:441
msgid "Photos"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:772
+#: crates/pdfs-gui/src/app/main.rs:775
#: crates/pdfs-gui/src/app/pages/shared.rs:168
#: crates/pdfs-gui/src/app/pages/shared.rs:484
msgid "Shared with me"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:777
+#: crates/pdfs-gui/src/app/main.rs:780
#: crates/pdfs-gui/src/app/pages/shared_by_me.rs:120
msgid "Shared by me"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:780
+#: crates/pdfs-gui/src/app/main.rs:783
#: crates/pdfs-gui/src/app/pages/devices.rs:140
#: crates/pdfs-gui/src/app/pages/devices.rs:489
msgid "Computers"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:781
+#: crates/pdfs-gui/src/app/main.rs:784
#: crates/pdfs-gui/src/app/pages/trash.rs:101
msgid "Trash"
msgstr ""
#. Translators: sidebar entry for the page listing this computer's synced folders.
-#: crates/pdfs-gui/src/app/main.rs:783
+#: crates/pdfs-gui/src/app/main.rs:786
msgid "Sync"
msgstr ""
#. Translators: a failed action's toast. {what} says what failed, such as
#. "Couldn't rename"; {detail} is the reason, usually untranslated text
#. from the mount service.
-#: crates/pdfs-gui/src/app/main.rs:1109
+#: crates/pdfs-gui/src/app/main.rs:1112
#: crates/pdfs-gui/src/app/pages/takeout.rs:714
#, rust-format
msgid "{what}: {detail}"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1132
+#: crates/pdfs-gui/src/app/main.rs:1135
msgid "You're offline"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1133
+#: crates/pdfs-gui/src/app/main.rs:1136
msgid "That's not there any more"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1134
+#: crates/pdfs-gui/src/app/main.rs:1137
msgid "You don't have access to that"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1135
+#: crates/pdfs-gui/src/app/main.rs:1138
msgid "Something changed this first"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1136
+#: crates/pdfs-gui/src/app/main.rs:1139
msgid "Your Proton Drive is full"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1223
+#: crates/pdfs-gui/src/app/main.rs:1226
msgid "Proton Drive for Linux"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1231
+#: crates/pdfs-gui/src/app/main.rs:1234
msgid ""
"Files-on-demand Proton Drive for the Linux desktop.\n"
"\n"
@@ -148,23 +148,23 @@ msgid ""
"AG."
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1236
+#: crates/pdfs-gui/src/app/main.rs:1239
msgid "Source Code"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1240
+#: crates/pdfs-gui/src/app/main.rs:1243
msgid "Proton Terms of Service"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1244
+#: crates/pdfs-gui/src/app/main.rs:1247
msgid "Proton Privacy Policy"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1249
+#: crates/pdfs-gui/src/app/main.rs:1252
msgid "Disclaimer"
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1253
+#: crates/pdfs-gui/src/app/main.rs:1256
msgid ""
"This is an unofficial, community-built client. It is not affiliated with, "
"endorsed by, or supported by Proton AG.\n"
@@ -177,21 +177,21 @@ msgid ""
"of any data you cannot afford to lose."
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1261
+#: crates/pdfs-gui/src/app/main.rs:1264
msgid ""
"Places are named with data from GeoNames, licensed under CC BY 4.0."
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
"public domain."
msgstr ""
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the GeoNames, licensed under GeoNames udostępnionych na licencji CC BY 4.0."
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
@@ -211,7 +211,7 @@ msgstr ""
"www.naturalearthdata.com/\">Natural Earth, które należą do domeny "
"publicznej."
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the GeoNames, licensed under GeoNames, licenciados sob CC BY 4.0."
-#: crates/pdfs-gui/src/app/main.rs:1269
+#: crates/pdfs-gui/src/app/main.rs:1272
msgid ""
"The places map is drawn from Natural Earth land outlines and country borders, which are in the "
@@ -207,7 +207,7 @@ msgstr ""
"fronteiras de países do Natural Earth, que são de domínio público."
-#: crates/pdfs-gui/src/app/main.rs:1277
+#: crates/pdfs-gui/src/app/main.rs:1280
msgid ""
"The street map, when turned on, comes from OpenFreeMap in the