Suggestions: “Matched by” and “Recognised” carry English literals, and the labels are in the scheduler's language #9

Closed
opened 2026-10-01 10:47:56 +00:00 by tiagoagueda · 1 comment
Owner

Found on 2026-09-29 in a read-only review of postulo-imap at cc09c6381, following a suggestion's context from sync() to the Suggestions page and running a pass for an owner whose language is Portuguese.

Related to postulo/postulo#335.

Under each suggestion Postulo prints the context the plugin stored, as key: value. The plugin translates the three keys when the pass runs and stores the result, and the values of two of them are never translatable at all: how the message was matched is one of five English literals, and what was recognised is the rule file's key. A person reading in any other language sees English words on every suggestion, and after a scheduled pass the labels are English too.

  • Where:
    • src/postulo_imap/sync.py:199-205: the context dictionary. str(_("Matched by")): found.how if found else str(_("nothing yet")) and str(_("Recognised")): f"{verdict.kind} ({verdict.language}): {verdict.matched}".
    • src/postulo_imap/matching.py:98, :112, :124, :135, :143: Match.how is "thread", "contact", "domain", "company name" or "role", none of them through gettext.
    • src/postulo_imap/classifying.py:40-46: verdict.kind is a key of OUTCOMES (acknowledged, rejected, interview, assessment, offer), an identifier used as a word.
    • src/postulo_imap/sync.py:194: str(_("A message with no subject")) is stored as the suggestion's summary in the same way.
    • In postulo: templates/applications/suggestion_list.html:61-62 prints {{ key }}: {{ value }} as stored; plugins/syncing.py:80 calls plugin.sync(...) with no language activated, and the scheduler is a management command, where the active language is LANGUAGE_CODE (en-gb).
    • tests/test_imap.py:555 pins the literal: assert "domain" in " ".join(acknowledged.context.values()).
  • What happens: run on the snapshot with the plugin's catalogues on the locale path, for an owner whose profile language is pt-pt.
    • A scheduled pass (nothing activated, get_language() is en-gb) stored {'From': …, 'Matched by': 'domain', 'Recognised': 'rejected (pt): lamentamos informar'} and, for an unmatched message, 'Matched by': 'nothing yet'. The person reads those English lines under every suggestion, on a page that is otherwise Portuguese.
    • The same pass under pt-pt, which is what Sync now gives, stored {'De': …, 'Associado por': 'domain', 'Reconhecido': 'rejected (pt): lamentamos informar'}: translated labels, English values.
    • So the same mailbox produces suggestions labelled in two languages depending on who started the pass, and no language gets a translated value.
  • Why it matters: these two lines are how the person judges whether to accept a suggestion: on what evidence it was attached to an application, and which phrase was taken for a rejection or an offer. The plugin ships 68 catalogues, and none of them can say "domain" or "rejected".
  • Fix:
    • Give the five ways of matching and the five kinds translatable labels in the plugin's catalogue (a mapping from the identifier to a gettext_lazy string, in matching.py and classifying.py), and store str() of those. Keep Match.how and Verdict.kind as identifiers for the code and the tests.
    • The language of what is stored is the core's to set: sync_connection should run each sync inside translation.override(<the owner's language>), as notify() does since postulo/postulo#223 (closed), which fixed the recipient's language for notifications only. That half is reported separately in this review against postulo, together with SyncReport.summary(); language_for is not on postulo.plugins.api, so the plugin cannot do it itself without reaching past the surface. Until the core does it, the labels of a scheduled pass stay in LANGUAGE_CODE.
    • Regenerate the catalogues with postulo-messages extract.

Done when

  • tests/test_imap.py has a test that runs a pass inside translation.override("pt-pt") over one message matched by its domain and one left unmatched, and asserts that no value in either suggestion's context contains domain, rejected or acknowledged, and that Match.how is still "domain". It fails today on the values.
  • tests/test_imap.py:555 asserts the identifier on the Match, or the translated label, not an English word inside the stored context.
Found on 2026-09-29 in a read-only review of postulo-imap at cc09c6381, following a suggestion's context from `sync()` to the Suggestions page and running a pass for an owner whose language is Portuguese. Related to postulo/postulo#335. Under each suggestion Postulo prints the context the plugin stored, as `key: value`. The plugin translates the three keys when the pass runs and stores the result, and the values of two of them are never translatable at all: how the message was matched is one of five English literals, and what was recognised is the rule file's key. A person reading in any other language sees English words on every suggestion, and after a scheduled pass the labels are English too. - **Where:** - `src/postulo_imap/sync.py:199-205`: the context dictionary. `str(_("Matched by")): found.how if found else str(_("nothing yet"))` and `str(_("Recognised")): f"{verdict.kind} ({verdict.language}): {verdict.matched}"`. - `src/postulo_imap/matching.py:98`, `:112`, `:124`, `:135`, `:143`: `Match.how` is `"thread"`, `"contact"`, `"domain"`, `"company name"` or `"role"`, none of them through gettext. - `src/postulo_imap/classifying.py:40-46`: `verdict.kind` is a key of `OUTCOMES` (`acknowledged`, `rejected`, `interview`, `assessment`, `offer`), an identifier used as a word. - `src/postulo_imap/sync.py:194`: `str(_("A message with no subject"))` is stored as the suggestion's summary in the same way. - In postulo: `templates/applications/suggestion_list.html:61-62` prints `{{ key }}: {{ value }}` as stored; `plugins/syncing.py:80` calls `plugin.sync(...)` with no language activated, and the scheduler is a management command, where the active language is `LANGUAGE_CODE` (`en-gb`). - `tests/test_imap.py:555` pins the literal: `assert "domain" in " ".join(acknowledged.context.values())`. - **What happens:** run on the snapshot with the plugin's catalogues on the locale path, for an owner whose profile language is `pt-pt`. - A scheduled pass (nothing activated, `get_language()` is `en-gb`) stored `{'From': …, 'Matched by': 'domain', 'Recognised': 'rejected (pt): lamentamos informar'}` and, for an unmatched message, `'Matched by': 'nothing yet'`. The person reads those English lines under every suggestion, on a page that is otherwise Portuguese. - The same pass under `pt-pt`, which is what *Sync now* gives, stored `{'De': …, 'Associado por': 'domain', 'Reconhecido': 'rejected (pt): lamentamos informar'}`: translated labels, English values. - So the same mailbox produces suggestions labelled in two languages depending on who started the pass, and no language gets a translated value. - **Why it matters:** these two lines are how the person judges whether to accept a suggestion: on what evidence it was attached to an application, and which phrase was taken for a rejection or an offer. The plugin ships 68 catalogues, and none of them can say "domain" or "rejected". - **Fix:** - Give the five ways of matching and the five kinds translatable labels in the plugin's catalogue (a mapping from the identifier to a `gettext_lazy` string, in `matching.py` and `classifying.py`), and store `str()` of those. Keep `Match.how` and `Verdict.kind` as identifiers for the code and the tests. - The language of what is stored is the core's to set: `sync_connection` should run each sync inside `translation.override(<the owner's language>)`, as `notify()` does since postulo/postulo#223 (closed), which fixed the recipient's language for notifications only. That half is reported separately in this review against postulo, together with `SyncReport.summary()`; `language_for` is not on `postulo.plugins.api`, so the plugin cannot do it itself without reaching past the surface. Until the core does it, the labels of a scheduled pass stay in `LANGUAGE_CODE`. - Regenerate the catalogues with `postulo-messages extract`. ## Done when - `tests/test_imap.py` has a test that runs a pass inside `translation.override("pt-pt")` over one message matched by its domain and one left unmatched, and asserts that no value in either suggestion's context contains `domain`, `rejected` or `acknowledged`, and that `Match.how` is still `"domain"`. It fails today on the values. - `tests/test_imap.py:555` asserts the identifier on the `Match`, or the translated label, not an English word inside the stored context.
tiagoagueda added this to the 0.6.0 milestone 2026-10-01 10:47:56 +00:00
Author
Owner

Fixed by pull request #25.

Fixed by pull request #25.
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-imap#9
No description provided.