From 57366543eca321bf9bfe179b745ecf4feb3ca31e Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Fri, 6 Mar 2026 19:25:12 -0500 Subject: [PATCH] fix: harden arabic terminal rtl rendering --- history.md | 8 ++++++++ transcribe.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/history.md b/history.md index 470cda1..d9e8105 100644 --- a/history.md +++ b/history.md @@ -152,3 +152,11 @@ The project now features a high-performance, Apple Silicon-optimized pipeline th - Implemented comment/blank-line support and invalid-line skipping with line-level warnings. - Merged file-based glossary entries with repeatable `--glossary-pair` CLI entries for runtime overrides. - **Outcome:** English caption correction can now use a maintained glossary file automatically, while preserving ad-hoc CLI term fixes. + +## Phase 19: Arabic Terminal Rendering Hardening +- **Goal:** Improve readability of Arabic captions in terminal environments with mixed LTR/RTL output. +- **Approach:** + - Switched Arabic output to a strict two-line format (`[AR]:` label line + dedicated RTL content line). + - Wrapped Arabic caption content in explicit RTL embedding marks for better bidirectional layout stability. + - Added punctuation normalization for Arabic display (`،`, `؛`, `؟`) to reduce LTR punctuation artifacts. +- **Outcome:** Arabic captions render more consistently in terminal output, especially when adjacent to English/French/Spanish caption lines. diff --git a/transcribe.py b/transcribe.py index 84a9bd2..827f3e5 100644 --- a/transcribe.py +++ b/transcribe.py @@ -229,6 +229,30 @@ def maybe_merge_recent_caption(recent_en_lines, current_text, now_ts, window_sec previous["ts"] = now_ts return old, merged +def normalize_arabic_punctuation(text): + """Convert common Latin punctuation to Arabic-friendly equivalents.""" + updated = text.replace(",", "،").replace(";", "؛") + # Convert question mark only when there are Arabic letters present. + if re.search(r"[\u0600-\u06FF]", updated): + updated = updated.replace("?", "؟") + return updated + +def format_caption_lines(lang_key, text): + """Format caption output lines for terminal display.""" + key = lang_key.lower() + if key == "ar": + cleaned = normalize_arabic_punctuation(text) + # Put label on separate line and force RTL embedding for the content line. + return ["[AR]:", f"\u202B{cleaned}\u202C"] + return [f"[{lang_key.upper()}]: {text}"] + +def print_caption(lang_key, text, leading_newline=False): + lines = format_caption_lines(lang_key, text) + if leading_newline and lines: + lines[0] = "\n" + lines[0] + for line in lines: + print(line) + def is_hallucination(text): """Detect common Whisper hallucinations or high repetition.""" if not text: return False @@ -517,7 +541,7 @@ def main(): recent_en_lines.clear() if original_text: - print(f"\n[{detected_lang.upper()}]: {original_text}") + print_caption(detected_lang, original_text, leading_newline=True) last_change_time = time.time() # Successfully transcribed full segment # Prepare payload @@ -588,7 +612,7 @@ def main(): # Skip if we already filled this (e.g. detected lang was 'es') if lang_key in payload: if lang_key != detected_lang: # Already printed original - print(f"[{lang_key.upper()}]: {payload[lang_key]}") + print_caption(lang_key, payload[lang_key]) continue translated_parts = [] @@ -608,7 +632,7 @@ def main(): translated_text = " ".join(part for part in translated_parts if part).strip() payload[lang_key] = translated_text - print(f"[{lang_key.upper()}]: {translated_text}") + print_caption(lang_key, translated_text) # Queue for background ingestion if enabled if args.ingest: