import gradio as gr import requests from deep_translator import GoogleTranslator import re def detect_hadith_reference(text): """ Tries to find a Hadith collection name + number inside free-form pasted text (e.g. a full quote with a citation like 'Sunan Abu Dawood - Vol 5, Hadith 4586'). Returns (collection_key, number) or (None, None). NOTE: numbering schemes for Hadith collections vary across different published translations/editions - a number found in pasted text is not guaranteed to match this API's internal numbering. The caller should always show the retrieved Arabic text so the user can visually confirm it's the correct Hadith before trusting it. """ patterns = [ (re.compile(r"sahih\s+al[\s-]?bukhari", re.I), "bukhari"), (re.compile(r"\bbukhari\b", re.I), "bukhari"), (re.compile(r"sahih\s+muslim", re.I), "muslim"), (re.compile(r"sunan\s+abu\s+da[a]?wo?o?d", re.I), "abudawud"), (re.compile(r"abu\s+da[a]?wo?o?d", re.I), "abudawud"), (re.compile(r"(jami[\s']*(at|al)?[\s-]?)?tirmidhi", re.I), "tirmidhi"), (re.compile(r"sunan\s+ibn\s+majah", re.I), "ibnmajah"), (re.compile(r"ibn\s+majah", re.I), "ibnmajah"), (re.compile(r"sunan\s+an[\s-]?nasa'?i", re.I), "nasai"), (re.compile(r"nasa'?i", re.I), "nasai"), ] for pattern, key in patterns: m = pattern.search(text) if m: tail = text[m.end():m.end() + 80] num_match = re.search(r"(?:hadith\s*#?\s*|no\.?\s*|number\s*)(\d+)", tail, re.I) if not num_match: num_match = re.search(r"(\d+)", tail) if num_match: return key, num_match.group(1) return None, None def detect_quran_reference(text): """Tries to find a Quran surah:ayah reference inside free-form text.""" m = re.search(r"(?:quran|surah|ayat|ayah)[^\d]{0,20}(\d{1,3})\s*[:\-]\s*(\d{1,3})", text, re.I) if m: return f"{m.group(1)}:{m.group(2)}" return None def get_quran_verse(reference): """ Looks up verified Arabic, Urdu (Fateh Muhammad Jalandhri), and English (Saheeh International) text for a Quranic verse, instead of running it through a generic translator. Reference format: surah:ayah, e.g. 9:36 """ if not reference or not reference.strip(): raise gr.Error("Please enter a verse reference, e.g. 9:36") ref = reference.strip() try: url = f"https://api.alquran.cloud/v1/ayah/{ref}/editions/quran-uthmani,ur.jalandhry,en.sahih" response = requests.get(url, timeout=15) response.raise_for_status() data = response.json() if data.get("code") != 200: raise gr.Error(f"Couldn't find verse '{ref}'. Use the format surah:ayah, e.g. 9:36") editions = data["data"] arabic = next(e["text"] for e in editions if e["edition"]["identifier"] == "quran-uthmani") urdu = next(e["text"] for e in editions if e["edition"]["identifier"] == "ur.jalandhry") english = next(e["text"] for e in editions if e["edition"]["identifier"] == "en.sahih") surah_name = editions[0]["surah"]["englishName"] ayah_num = editions[0]["numberInSurah"] citation = f"Surah {surah_name}, Ayah {ayah_num}" return arabic, urdu, english, citation except gr.Error: raise except Exception as e: import traceback traceback.print_exc() raise gr.Error(f"Lookup failed: {e}") def translate_to_urdu(text): if not text or not text.strip(): raise gr.Error("Please enter some English text first.") verified_block = "" # Check for an embedded Hadith citation. collection_key, hadith_num = detect_hadith_reference(text) if collection_key and hadith_num: try: label = next( (k for k, v in HADITH_COLLECTIONS.items() if v == collection_key), collection_key, ) arabic, urdu, english, citation = get_hadith(label, hadith_num) if urdu: verified_block += ( f"\u2705 [Verified Hadith \u2014 {citation}]\n" f"(cross-check numbering with your source)\n" f"{urdu}\n\n" ) except Exception: pass # fall through, verified match just won't be included # Check for an embedded Quran citation. quran_ref = detect_quran_reference(text) if quran_ref: try: arabic, urdu, english, citation = get_quran_verse(quran_ref) if urdu: verified_block += f"\u2705 [Verified Quran \u2014 {citation}]\n{urdu}\n\n" except Exception: pass # Always translate the full pasted text, line by line, regardless of # whether a citation was also found above. try: lines = [line for line in text.split("\n") if line.strip()] translated_lines = [] translator = GoogleTranslator(source="en", target="ur") for line in lines: output = translator.translate(line) translated_lines.append(output) full_translation = "\n".join(translated_lines) except Exception as e: import traceback traceback.print_exc() raise gr.Error(f"Translation failed: {e}") if verified_block: return verified_block + "\u2014\u2014\u2014\n\n" + full_translation return full_translation # ── Hadith lookup (new) ────────────────────────────────────────────────────── # Uses fawazahmed0/hadith-api - free, no API key, no gating, comprehensive # coverage of the major collections (Bukhari, Muslim, Abu Dawud, Tirmidhi, # Ibn Majah), each with a genuine Urdu edition, served via jsDelivr CDN. # Same architecture/author as fawazahmed0/quran-api. This mirrors the Quran # tab exactly: look up the verified translation by reference instead of # running the Hadith text through a generic translator. HADITH_API_BASE = "https://cdn.jsdelivr.net/gh/fawazahmed0/hadith-api@1/editions" HADITH_COLLECTIONS = { "Sahih al-Bukhari": "bukhari", "Sahih Muslim": "muslim", "Sunan Abu Dawud": "abudawud", "Jami at-Tirmidhi": "tirmidhi", "Sunan Ibn Majah": "ibnmajah", "Sunan an-Nasa'i": "nasai", } def get_hadith(collection_label, hadith_number): """ Looks up verified Arabic, Urdu, and English translations for a Hadith by collection + number, instead of running it through a generic translator. """ if not hadith_number or not str(hadith_number).strip(): raise gr.Error("Please enter a Hadith number, e.g. 1") collection_key = HADITH_COLLECTIONS.get(collection_label, "bukhari") number = str(hadith_number).strip() try: ar_url = f"{HADITH_API_BASE}/ara-{collection_key}/{number}.json" ur_url = f"{HADITH_API_BASE}/urd-{collection_key}/{number}.json" en_url = f"{HADITH_API_BASE}/eng-{collection_key}/{number}.json" ar_resp = requests.get(ar_url, timeout=15) ur_resp = requests.get(ur_url, timeout=15) en_resp = requests.get(en_url, timeout=15) if ar_resp.status_code != 200 or ur_resp.status_code != 200: raise gr.Error( f"Couldn't find Hadith #{number} in {collection_label}. " f"Check the Hadith number and try again." ) ar_data = ar_resp.json() ur_data = ur_resp.json() en_data = en_resp.json() if en_resp.status_code == 200 else {} ar_hadiths = ar_data.get("hadiths", {}) ur_hadiths = ur_data.get("hadiths", {}) en_hadiths = en_data.get("hadiths", {}) # The single-hadith endpoint returns "hadiths" as either a dict or a # one-item list depending on edition - handle both defensively. if isinstance(ar_hadiths, list): ar_hadiths = ar_hadiths[0] if ar_hadiths else {} if isinstance(ur_hadiths, list): ur_hadiths = ur_hadiths[0] if ur_hadiths else {} if isinstance(en_hadiths, list): en_hadiths = en_hadiths[0] if en_hadiths else {} arabic = ar_hadiths.get("text", "") urdu = ur_hadiths.get("text", "") english = en_hadiths.get("text", "") if not arabic and not urdu: raise gr.Error( f"Couldn't find Hadith #{number} in {collection_label}. " f"Check the Hadith number and try again." ) citation = f"{collection_label}, Hadith #{number}" return arabic, urdu, english, citation except gr.Error: raise except Exception as e: import traceback traceback.print_exc() raise gr.Error(f"Lookup failed: {e}") css = """ #header { text-align: center; padding: 24px 0 8px; } #header h1 { font-size: 32px; font-weight: 700; background: linear-gradient(135deg, #6366f1, #06b6d4); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 4px; } #header p { color: #888; font-size: 14px; } #run-btn { background: linear-gradient(135deg, #6366f1, #06b6d4) !important; color: white !important; font-weight: 600 !important; border: none !important; } #urdu-output textarea { font-size: 20px !important; direction: rtl; text-align: right; } """ with gr.Blocks(title="Peace Network Translator") as demo: gr.HTML( """ """ ) with gr.Tabs(): with gr.Tab("General Text"): gr.Markdown( "Agar aapke text mein Hadith ya Quran verse ka citation ho " "(jaise 'Sunan Abu Dawood, Hadith 4586' ya 'Quran 9:36'), " "yeh automatically verified Urdu translation dhoondh ke dega — " "generic AI translate nahi karega us hisse ko. " "\u26a0\ufe0f Numbering alag editions mein alag ho sakti hai — " "hamesha Arabic text se match confirm kar lein." ) with gr.Row(): inp = gr.Textbox( label="English text", placeholder="Type or paste English text here...", lines=8, ) out = gr.Textbox( label="اردو ترجمہ (Urdu translation)", lines=8, elem_id="urdu-output", ) btn = gr.Button("🔄 Translate to Urdu", variant="primary", elem_id="run-btn") btn.click(translate_to_urdu, inputs=inp, outputs=out) with gr.Tab("Quran Verse (Verified)"): gr.Markdown( "Verse reference daaliye (surah:ayah format, jaise `9:36`). " "Yeh generic AI translation nahi hai — Fateh Muhammad Jalandhri " "(Urdu) aur Saheeh International (English) ki verified, " "established translation database se seedha nikalta hai." ) ref_input = gr.Textbox(label="Verse reference (surah:ayah)", placeholder="e.g. 9:36") quran_btn = gr.Button("📖 Get Verse", variant="primary", elem_id="run-btn") arabic_out = gr.Textbox(label="Arabic (Uthmani)", lines=3) urdu_out = gr.Textbox(label="اردو ترجمہ (جالندھری)", lines=3, elem_id="urdu-output") english_out = gr.Textbox(label="English (Saheeh International)", lines=3) citation_out = gr.Textbox(label="Reference") quran_btn.click( get_quran_verse, inputs=ref_input, outputs=[arabic_out, urdu_out, english_out, citation_out], ) with gr.Tab("Hadith (Verified)"): gr.Markdown( "Collection chuniye aur Hadith number daaliye. " "Yeh bhi generic AI translation nahi hai — established, " "verified Urdu aur English Hadith translation database se " "seedha nikalta hai, jaise Quran tab karta hai." ) with gr.Row(): hadith_collection = gr.Dropdown( label="Collection", choices=list(HADITH_COLLECTIONS.keys()), value="Sahih al-Bukhari", ) hadith_number_input = gr.Textbox(label="Hadith number", placeholder="e.g. 1") hadith_btn = gr.Button("📜 Get Hadith", variant="primary", elem_id="run-btn") hadith_arabic_out = gr.Textbox(label="Arabic", lines=4) hadith_urdu_out = gr.Textbox(label="اردو ترجمہ", lines=4, elem_id="urdu-output") hadith_english_out = gr.Textbox(label="English", lines=4) hadith_citation_out = gr.Textbox(label="Reference") hadith_btn.click( get_hadith, inputs=[hadith_collection, hadith_number_input], outputs=[hadith_arabic_out, hadith_urdu_out, hadith_english_out, hadith_citation_out], ) demo.queue().launch(css=css)