Deleting a company or an application: the 'This also deletes' counts are never extracted, so always English #388

Closed
opened 2026-10-01 10:46:20 +00:00 by tiagoagueda · 0 comments
Owner

Found on 2026-09-29 in a read-only review of postulo at 95348908d, reading the delete views and searching every catalogue for their strings.

Checked again on 2026-10-01 against main at 0a5cad587: still present.

The company and application delete pages pass their plural texts to ngettext through variables. The extractor reads call sites and takes only literal arguments, so those texts are in no catalogue, and every language shows them in English on the page whose job (#217) is to say exactly what will be lost.

  • Where: jobs/views.py:593-601 (CompanyDeleteView.get_context_data) and applications/views.py:491-503 (ApplicationDeleteView.get_context_data), both of the form counts = ((n, "%(count)d posting", "%(count)d postings"), ...) and then ngettext(one, many, number) % {"count": number}; drawn by templates/partials/confirm_delete.html:20-26. The extractor is core/messages_tool.py (CALLS at line 111 onwards), which takes a message only from a literal argument at the call.
  • What happens: none of "%(count)d posting", "%(count)d application", "%(count)d contact", "%(count)d entry on its timeline", "%(count)d interview" or "%(count)d reminder" appears as a msgid in any locale/*/LC_MESSAGES/django.po. A German user deleting a company reads "Dies löscht außerdem:" followed by "3 postings", "5 applications", "2 contacts"; deleting an application lists "12 entries on its timeline", "2 interviews", "1 reminder". PostingDeleteView (jobs/views.py:909-942) writes its literals inside the call, and its "entry in its history" strings are in the catalogues.
  • Why it matters: the one sentence that warns somebody what a delete takes with it is half in a language they may not read, and test_the_catalogues_are_what_a_fresh_extraction_writes cannot notice, because a fresh extraction does not see these strings either.
  • Fix: write each ngettext call with its two literals inline, as PostingDeleteView does, and run scripts/messages.py extract. To stop it recurring, have the extractor (or a test) report any gettext-family call in src/ whose message argument is not a string literal; today these two are the only ngettext calls with variable arguments.

Done when

  • tests/test_translations.py has a test that walks src/postulo with ast and fails on any call named in messages_tool.CALLS whose message or plural argument is not a string constant.
  • A test renders the company delete page in a compiled non-English language (the compiled fixture in tests/test_translations.py) and asserts the English "postings" is absent.
Found on 2026-09-29 in a read-only review of postulo at 95348908d, reading the delete views and searching every catalogue for their strings. Checked again on 2026-10-01 against `main` at `0a5cad587`: still present. The company and application delete pages pass their plural texts to `ngettext` through variables. The extractor reads call sites and takes only literal arguments, so those texts are in no catalogue, and every language shows them in English on the page whose job (#217) is to say exactly what will be lost. - **Where:** `jobs/views.py:593-601` (`CompanyDeleteView.get_context_data`) and `applications/views.py:491-503` (`ApplicationDeleteView.get_context_data`), both of the form `counts = ((n, "%(count)d posting", "%(count)d postings"), ...)` and then `ngettext(one, many, number) % {"count": number}`; drawn by `templates/partials/confirm_delete.html:20-26`. The extractor is `core/messages_tool.py` (`CALLS` at line 111 onwards), which takes a message only from a literal argument at the call. - **What happens:** none of `"%(count)d posting"`, `"%(count)d application"`, `"%(count)d contact"`, `"%(count)d entry on its timeline"`, `"%(count)d interview"` or `"%(count)d reminder"` appears as a msgid in any `locale/*/LC_MESSAGES/django.po`. A German user deleting a company reads "Dies löscht außerdem:" followed by "3 postings", "5 applications", "2 contacts"; deleting an application lists "12 entries on its timeline", "2 interviews", "1 reminder". `PostingDeleteView` (`jobs/views.py:909-942`) writes its literals inside the call, and its "entry in its history" strings are in the catalogues. - **Why it matters:** the one sentence that warns somebody what a delete takes with it is half in a language they may not read, and `test_the_catalogues_are_what_a_fresh_extraction_writes` cannot notice, because a fresh extraction does not see these strings either. - **Fix:** write each `ngettext` call with its two literals inline, as `PostingDeleteView` does, and run `scripts/messages.py extract`. To stop it recurring, have the extractor (or a test) report any gettext-family call in `src/` whose message argument is not a string literal; today these two are the only `ngettext` calls with variable arguments. ## Done when - `tests/test_translations.py` has a test that walks `src/postulo` with `ast` and fails on any call named in `messages_tool.CALLS` whose message or plural argument is not a string constant. - A test renders the company delete page in a compiled non-English language (the `compiled` fixture in `tests/test_translations.py`) and asserts the English "postings" is absent.
tiagoagueda added this to the 0.5.0 milestone 2026-10-01 10:46:20 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#388
No description provided.