From 3214d8c659a7e7189be0bf41526b9c658baa41a5 Mon Sep 17 00:00:00 2001 From: busya Date: Tue, 2 Jun 2026 09:52:32 +0300 Subject: [PATCH] fix(translate): same-language short-circuit blocked multi-language translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: _classify() immediately skipped (continue) when detected language matched ANY target language, preventing cache lookup and LLM translation for remaining targets. E.g., ru source + targets [ru, en] → only ru row created, en never processed (8978 ru vs 18 en in DB). Fix: 1. _classify(): Only short-circuit when ALL targets match detected language. Partial match → mark _same_language but continue to cache check and LLM for non-matching targets. 2. _persist_pre(): Unify two code paths into single loop over all target languages. Same-language targets use source_text as-is; cached targets use cache value; fallback to approved_translation. 3. SIM102: Merge nested 'if cl: if all(...)' → 'if cl and all(...)' to keep cyclomatic complexity ≤ 10 (INV_7). QA: All 69 translate tests pass. 7 scenario traces (A-G) verified. No regressions in approved_translation, preview edits, or single- target same-language flows. --- backend/src/plugins/translate/_batch_proc.py | 60 +++++++++----------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/backend/src/plugins/translate/_batch_proc.py b/backend/src/plugins/translate/_batch_proc.py index 75c48e2f..da8066d4 100644 --- a/backend/src/plugins/translate/_batch_proc.py +++ b/backend/src/plugins/translate/_batch_proc.py @@ -127,21 +127,27 @@ class BatchProcessingService: llm_rows, pre_rows = [], [] tls_lower = [str(t).lower() for t in tls] for row in batch_rows: - # ★ NEW: Same-language pre-filter — detected language matches target + # ★ Same-language pre-filter: only short-circuit when ALL targets + # match the detected language. If only SOME match, still process + # other targets via cache or LLM. dl = row.get("_detected_lang") if dl and str(dl).lower() not in ("und", "") and str(dl).lower() in tls_lower: + non_matching = [t for t in tls if str(t).lower() != str(dl).lower()] + if not non_matching: + # All targets are the same as detected language — no translation needed + row["_same_language"] = True + pre_rows.append(row) + continue + # Partial match: mark same-language for later use, but don't skip row["_same_language"] = True - pre_rows.append(row) - continue if row.get("approved_translation"): pre_rows.append(row) continue cl = row.get("_cached_lang_values") - if cl: - if all(lc in cl for lc in tls): - pre_rows.append(row) - continue + if cl and all(lc in cl for lc in tls): + pre_rows.append(row) + continue if preview_edits_cache: sd = row.get("source_data") or {} if sd: @@ -159,34 +165,18 @@ class BatchProcessingService: def _persist_pre(self, pre_rows, bid, run_id, tls): count = 0 for row in pre_rows: - # ★ Same-language row: source language = target → no translation needed - if row.get("_same_language"): - dl = str(row["_detected_lang"]) - source_text = row.get("source_text", "") - rec = TranslationRecord( - id=str(uuid.uuid4()), batch_id=bid, run_id=run_id, - source_sql=source_text, target_sql=source_text, - source_object_type="table_row", source_object_id=row.get("row_index"), - source_object_name=row.get("source_object_name", ""), - source_data=row.get("source_data"), source_hash=row.get("_source_hash"), - status="SUCCESS", - ) - self.db.add(rec) - # One TranslationLanguage for the detected (same) language - self.db.add(TranslationLanguage( - id=str(uuid.uuid4()), record_id=rec.id, language_code=dl, - source_language_detected=dl, translated_value=source_text, - final_value=source_text, status="translated", needs_review=False, - )) - count += 1 - continue - - # Existing: cached / approved rows cl = row.get("_cached_lang_values") detected_lang = row.get("_detected_lang", "und") or "und" + source_text = row.get("source_text", "") + is_same = row.get("_same_language") + + # target_sql: prefer approved_translation, then first cached value, then source + primary_cached = next(iter(cl.values()), "") if cl else "" + target_sql = row.get("approved_translation") or primary_cached or source_text + rec = TranslationRecord( id=str(uuid.uuid4()), batch_id=bid, run_id=run_id, - source_sql=row.get("source_text", ""), target_sql=row.get("approved_translation", ""), + source_sql=source_text, target_sql=target_sql, source_object_type="table_row", source_object_id=row.get("row_index"), source_object_name=row.get("source_object_name", ""), source_data=row.get("source_data"), source_hash=row.get("_source_hash"), @@ -194,7 +184,13 @@ class BatchProcessingService: ) self.db.add(rec) for lc in tls: - fv = cl[lc] if (cl and lc in cl) else row.get("approved_translation", "") + if is_same and str(lc).lower() == str(detected_lang).lower(): + # Same language: use source text as-is (no translation needed) + fv = source_text + elif cl and lc in cl: + fv = cl[lc] + else: + fv = row.get("approved_translation", "") self.db.add(TranslationLanguage( id=str(uuid.uuid4()), record_id=rec.id, language_code=lc, source_language_detected=detected_lang, translated_value=fv,