Settings → Connections: sync reports, push errors and the private-address refusal are English only, with capture wording #384
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#384
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, following each message a connection shows back to its owner.Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atplugins/http.py:36-46(check_destination) and:49-54(_refused, used by_guardat:77),plugins/public_addresses.py:53-67(the four reasons inpublic_addresses_for, which #248 moved out ofplugins/fetching.py) andplugins/views.py:460(the Sync now flash); the other locations are unchanged.Several sentences a person reads on Settings → Connections never pass through gettext, so they are English whatever language the page is in, and one of them is also wrong for the place it appears.
Related to #335, which makes
sync_connectionrun in its owner's language and time zone, so that what a plugin words during a scheduled run (its notes included) is no longer in the instance's language. This issue adds what #335 does not reach: the counters of the report are not translatable in any language, and two more families of messages on the same page are not either.1. The sync report
SyncReport.summary()words its counters with f-strings and joins the plugin's notes to them.sync_connectionstores the result inConnection.last_summary, and the Connections page prints it inside a translated sentence.plugins/base.py:815-828,SyncReport.summary():f"{self.pushed} pushed",f"{self.pulled} pulled",f"{self.removed} removed",f"{self.skipped} skipped"and"nothing to do".plugins/syncing.py:92,sync_connection:connection.last_summary = report.summary()[:500];run_syncs(:110-137) calls it with notranslation.override, which is #335's subject.templates/connections/list.html:47-49:Last run {{ when }}: {{ report }}.plugins/views.py:459,ConnectionSyncNowView: the flash "Synced: %(summary)s.".tests/test_syncs.py:93-95,:140,:151and:220-221pin the English words.2. The private-address refusal for connections
check_destinationand_refusedappend the same English sentence, written twice, to whateverUnsafeURLsaid.plugins/http.py:34-44(check_destination) and:47-52(_refused, used by_guardat:75):f"{exc} Connections may only reach private or local addresses when the operator sets POSTULO_CONNECTIONS_ALLOW_PRIVATE=true.", with no gettext.plugins/fetching.py:121-133(public_addresses_for): the four reasons it is appended to, all worded for capturing a posting.plugins/webhook/__init__.py:184-186(form error) and:217-220(Test result), and wherever a plugin'sDestinationRefusedbecomes a connection'slast_error.3. Web Push messages
plugins/browser/webpush.pyraises plain English strings that the browser notifier shows as they are.Where:
plugins/browser/webpush.py:191-205,parse_subscription: "The subscription is not something a browser wrote.", "A push address has to be HTTPS." and "The subscription's keys are not the browser's.", shown as a form error byplugins/browser/__init__.py:130-134(validate) and as a Test result by:190-194(test).plugins/browser/webpush.py:251-254,push:f"The push service answered {response.status_code}.", shown as a Test result byplugins/browser/__init__.py:225and, re-raised fromsend(:185), kept as the connection's last error. The comment above it says so: "this string is shown on the connection and stored inlast_error".What happens: a person reading in Portuguese who tests a browser connection while the push service is failing reads "The push service answered 503." under a Portuguese heading. (The 404/410 message at
:242-246is replaced by a translated sentence in bothsendandtest, so it is not part of this.)Why it matters: Postulo promises that every user-facing string is translatable. These are the lines that tell a person whether their address book and calendar are in step, why a webhook was refused and why a push did not arrive, on a page every connected person sees.
Fix:
pushed,pulled,removed,skippedandnotes, or the counters besidelast_summary), and word the counters at display time withngettextin the reader's language, with a translated "nothing to do".ConnectionSyncNowViewwords its flash the same way. The notes stay as the plugin wrote them, in the owner's language once #335 lands.UnsafeURLa kind (or raise distinct subclasses frompublic_addresses_for) socheck_destinationand_refusedadd the operator sentence only for a private address, and give connections their own wording for that case instead of the capture one.webpush.pymessages through gettext, in the browser plugin's own catalogue (plugins/browser/locale/), not the core's.Done when
tests/test_syncs.pyrenders the Connections list underdeafter a sync that pushed and pulled, and asserts the counters are not the English words; and asserts the Sync now flash is worded in the request's language.tests/security/test_outbound.pyasserts that a destination refused for an unresolvable host does not mentionPOSTULO_CONNECTIONS_ALLOW_PRIVATE, and that one refused for a private address mentions it and does not mention pasting a posting.tests/test_browser_notifier.pyasserts, under a language the browser plugin ships, that the error for anhttp://push address and the Test result for a push service answering 503 are not the English sentences.tests/test_translations.pysees the new msgids in every catalogue, the browser plugin's included.