Timeline, offers, search and tables: translated labels are lower-cased inside sentences in Python, the pattern #168 retired #392
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#392
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, searching the Python code for.lower()applied to a translated label and reading the results against the German catalogue. Related to #168 (closed), which removedget_kind_display|lowerfrom a CV's heading and added a template lint against the pattern (tests/test_template_lint.py:409-431); this issue lists the places where the same thing is still done in Python, which that lint does not read.Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atcore/search.py:433and:442(the sent-document hits) andapplications/views.py:1435(the comparison page); the other locations have not moved.A translated choice label or column name is passed through
.lower()and dropped into a sentence. As #168 said, that is English typography applied to every language: it flattens an acronym (CV) in English, French and Portuguese, and in German, where every noun is capitalised, it is a spelling mistake that no catalogue can undo. Two of these strings are stored in the timeline and sent to notifiers, so the mistake is written into a person's record; two more are the accessible names of the controls on every table.applications/services.py:520insettle_interview:_("Nobody showed up for the %(kind)s") % {"kind": str(kind).lower()}, stored as a timeline event's summary. The cancelled branch just above (:510-511) uses the label as written.applications/models.py:1011-1012inOffer.terms:str(self.get_period_display()).lower(), thenf"{figure} {self.currency} {period}", joined outside any translatable pattern.termsreaches the offer card (templates/applications/application_detail.html:225), the comparison page (applications/views.py:1395), the offer's timeline summary (_offer_summary,applications/services.py:629-631) and theoffer_recordedpayload (_tell_offer,applications/services.py:612-626). The comment attemplates/applications/application_detail.html:77-79records that #168 removed exactly this for the listing's salary period on the same page.core/search.py:428and:437(the sent-document hits):sent.get_kind_display().lower()inside "in the %(kind)s you sent to %(company)s on %(when)s" and "in the %(kind)s rendered on %(when)s".core/tables.py:150(Header.filter_label):_("Filter by %(column)s") % {"column": str(self.column.label).lower()}, thearia-labelof the header filters (templates/cotton/table/filter.html:35-87).core/tables.py:542,:545and:547(Table.sort_hint): the same.lower()in "Stop sorting by …", "Sort by …, highest first" and "Sort by …, lowest first", rendered as the label of the sort icon attemplates/cotton/table/head.html:139.InterviewKind), or a separate in-sentence label withpgettextthat translators case and inflect themselves.Offer.terms: build it from a translatable pattern such as_("%(figure)s %(currency)s %(period)s")with the period's in-sentence form, so translators control order and case.core/tables.py: drop the.lower(). #314 and #317 may reword these names; whichever lands second carries the rule.tests/test_template_lint.pyto Python: rejectget_\w+_display()and.labellower-cased undersrc/postulo.tests/test_interviews.py:174andtests/test_offers.py:94pin today's English wording and will need updating.plugins/postal_rules/__init__.py:179-182(_lower) does the same to an address part's label; #306 is reworking that module and should either remove it or be the one named exception.Done when
tests/test_interviews.pysettles an interview as a no-show with German active and asserts that the stored summary keeps the catalogue's capitalisation; a test intests/test_offers.pyasserts the same forOffer.terms. Both fail today.tests/test_search.pyfinds "in the CV you sent to" in the subtitle of a sent CV's hit.tests/test_tables.pyrenders a table with German active and asserts that the filter and sort names carry the column's label as the catalogue wrote it.tests/test_template_lint.pyscanssrc/postulo/**/*.pyfor a translated label lower-cased into a sentence; it fails on today's code and passes after.