Conversation
…o opinion - detect_language() returns empty both for "text too short to judge" and "not a language we handle"; the gate treated both as "translate". Fall back to the field-office language only in the first case, so short captions stop reaching the platform while T3339 stays fixed. - same correction in _can_auto_send(), which ba4ba7e changed alongside the gate. - port of the 14.0 fix; the two files were byte-identical beforehand, so this is a straight copy.
|
- langdetect samples randomly and runs unseeded; the English test text misses the confidence threshold in ~0.1% of runs, which would fail CI without a product regression. Pin the seed in setUp and restore it on cleanup.
- pre-commit runs ruff-format as well as ruff check; only the latter was run locally.
Danielgergely
approved these changes
Sep 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[T3371] FIX: don't queue B2S letters when the language detector has no opinion
v18 port of the 14.0 fix. Companion PR: #2160 (14.0). Merge the two
together, so the two lines don't drift apart again.
Problem
Letters that need no translation are landing on the translation platform — reported on
14.0 for TH / UG / PH / GH / KE / TG / BF, including letters queued to be "translated"
from a language into that same language.
v18 has the same defect: T3339 was ported here in
ba4ba7e6, and this is that change'sover-correction.
Root cause
T3339 fixed a real bug: field offices were labelling letters as already-translated when
they were not, Odoo trusted the label, and untranslated letters reached sponsors. It
fixed that by making content detection the deciding input.
The problem is that
detect_language()returns an empty recordset for four differentreasons:
en/de/fr/it/esprocess_letter()could not tell them apart and read all four as "the sponsor cannotread this → translate". Only the second justifies that.
The short-text case dominates. Most B2S letters below the floor are photo captions and
gift acknowledgements that the field office already writes in the sponsor's language —
there is nothing to translate, and no amount of detection will ever say so, because
there is not enough text to detect.
The fix
_letter_language_verdict()reports whether we have an opinion at all._sponsor_can_read_letter()then decides:Both gates call the shared predicate, so they cannot drift apart.
Does this reopen T3339?
No. Letters long enough to judge are still decided by their content — including letters
whose label claims the sponsor can read them but whose content disagrees. That is
exactly the population T3339 was about, and the fallback never touches it. Before
T3339, every letter was decided by the label; after this change, only letters too
short to detect are.
Residual gap: a letter that is both untranslated and below the length floor falls
back to the label, which could be wrong. Detection cannot close this — there is nothing
to detect.
_can_auto_send()took the same change as the gate in T3339, and it decides whether aletter is emailed to the sponsor without human validation. T3339 sharply reduced
automatic sending as an untracked side effect; this change restores it.
Coordinate the deploy with whoever runs the communication queue.
Fixing only the translation gate would release short letters from the translation queue
and strand them at the auto-send check — moving the backlog rather than clearing it.
That is why both gates are in scope.
Changes
Identical to the 14.0 patch —
_detect_letter_language()and_can_auto_send()werebyte-identical between the two branches before this change, so this is a straight copy,
not a rewrite.
advanced_translation/models/langdetect.pymin_lengthinstead of a magic numbersbc_compassion/models/correspondence.py_letter_language_verdict()and_sponsor_can_read_letter(); extract_clean_letter_text(); drop the now-unused_detect_letter_language()sbc_translation/models/correspondence.pyprocess_letter()calls the shared predicatepartner_communication_compassion/models/correspondence.py_can_auto_send()calls the same predicatesbc_compassion/tests/test_letter_language_verdict.pyTesting
3 tests in
sbc_compassion/tests/test_letter_language_verdict.py. They pass on 14.0;not yet executed on 18.0 — the v18 database is the pre-populated migration data and
running the suite against it was out of scope for this port.
_sponsor_can_read_letter()itself has no unit coverage. It needs a persisted letter,because
supporter_languages_idsis related topartner_id.spoken_lang_idsand comesback NewId-wrapped on an in-memory record. Building one requires
BaseSponsorshipTest,whose
setUpClassis broken on 14.0 for every test class that uses it — worth checkingwhether the same holds here.
On 14.0 the behaviour was validated by replaying the pre-T3339, current and new
predicates over the full B2S corpus on a database copy, and by checking the gate against
an independent reimplementation through
odoo shell. Since the code is identical, thatevidence carries over.
Known issues (pre-existing, not introduced here)
langdetectis used without settingDetectorFactory.seed, so a letter near the confidence threshold can be queued on onerun and published on the next.
them, because below-threshold counts as an opinion. Deliberate — it is the safe
direction for T3339.
filtering to the five languages, so a single surviving candidate always scores 1.0.
Out of scope
search_iso639()resolves duplicates by id order —ilike+limit=1with no_orderon the model, so it always returns the lowest id, and Spanish exists threetimes. Measured impact on this ticket: none.
label-trusting bypass into the publish path.