youssefreda9 commited on
Commit
3e90908
·
1 Parent(s): 2ff1e5e

FIX-48v3: Whitelist-based ه→ة (replaces broken stem-IV approach)

Browse files

The stem-IV approach failed because BERT vocab includes fragments like
الحكوم, الشرك, المدرس as IV. This made the pronoun guard block
every word.

New approach: explicit whitelist of ~80 common words with ه→ة confusion.
Zero false positives, covers the most frequent cases:
- الحكومه→الحكومة, الشركه→الشركة, المدرسه→المدرسة
- Definite + indefinite forms
- Common feminine adjectives

Also reverts pronoun suffix guard from araspell_rules.py.
Tests: 39 passing.

Files changed (1) hide show
  1. src/app.py +36 -22
src/app.py CHANGED
@@ -2490,37 +2490,51 @@ def analyze_text():
2490
  logger.error(traceback.format_exc())
2491
  timing_ms['grammar_error'] = f"{type(e).__name__}: {str(e)[:200]}"
2492
 
2493
- # ── FIX-48v2: ه→ة pass AFTER grammar (not before!) ──
2494
  # Must run AFTER grammar so grammar model can use ه for gender decisions.
2495
- # Only fixes remaining ه words that grammar didn't change.
2496
  if not _is_religious_text:
2497
  try:
2498
- from nlp.spelling.araspell_service import get_spelling_model
2499
- _hata_checker = get_spelling_model()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2500
  _hata_text = ctx.current_text
2501
  _hata_words = _hata_text.split()
2502
  _hata_changed = False
2503
  _hata_result = []
2504
- _PROTECTED_HA = {
2505
- 'الله', 'لله', 'فيه', 'عليه', 'منه', 'به', 'له', 'إليه',
2506
- 'وجه', 'نزه', 'سفه', 'فقه', 'نبه', 'شبه', 'مكره', 'تنبه',
2507
- 'اتجه', 'توجه', 'تشابه', 'وفيه', 'وعليه', 'ومنه', 'وله',
2508
- 'دراسته', 'دراستها', 'حياته', 'حياتها',
2509
- }
2510
- _CONSONANTS = set('بتثجحخدذرزسشصضطظعغفقكلمنهوي')
2511
  for _hw in _hata_words:
2512
  _hw_clean = _hw.rstrip('.،؛؟!?!')
2513
- if (len(_hw_clean) >= 4 and _hw_clean.endswith('ه')
2514
- and _hw_clean not in _PROTECTED_HA
2515
- and _hw_clean[-2] in _CONSONANTS):
2516
- _ta_cand = _hw_clean[:-1] + 'ة'
2517
- if _hata_checker.vocab_manager.is_iv(_ta_cand):
2518
- _punct_suffix = _hw[len(_hw_clean):]
2519
- logger.info(f"[HA-TA] Post-grammar ه→ة: '{_hw}'→'{_ta_cand}{_punct_suffix}'")
2520
- _hata_result.append(_ta_cand + _punct_suffix)
2521
- _hata_changed = True
2522
- continue
2523
- _hata_result.append(_hw)
2524
  if _hata_changed:
2525
  _hata_new = ' '.join(_hata_result)
2526
  ctx.mutate_text(_hata_new, OffsetMapper)
 
2490
  logger.error(traceback.format_exc())
2491
  timing_ms['grammar_error'] = f"{type(e).__name__}: {str(e)[:200]}"
2492
 
2493
+ # ── FIX-48v3: ه→ة pass AFTER grammar (whitelist-based) ──
2494
  # Must run AFTER grammar so grammar model can use ه for gender decisions.
2495
+ # Uses a whitelist of common words that are frequently written with ه instead of ة.
2496
  if not _is_religious_text:
2497
  try:
2498
+ _HATA_WHITELIST = {
2499
+ # Common nouns — definite form (with ال)
2500
+ 'الحكومه': 'الحكومة', 'المدرسه': 'المدرسة', 'الشركه': 'الشركة',
2501
+ 'الجامعه': 'الجامعة', 'المدينه': 'المدينة', 'القصه': 'القصة',
2502
+ 'المكتبه': 'المكتبة', 'الطائره': 'الطائرة', 'الوزاره': 'الوزارة',
2503
+ 'المديره': 'المديرة', 'المعلمه': 'المعلمة', 'الطالبه': 'الطالبة',
2504
+ 'القريه': 'القرية', 'الحديقه': 'الحديقة', 'المحكمه': 'المحكمة',
2505
+ 'الكنيسه': 'الكنيسة', 'المنطقه': 'المنطقة', 'الدوله': 'الدولة',
2506
+ 'السياره': 'السيارة', 'الطاوله': 'الطاولة', 'الغرفه': 'الغرفة',
2507
+ 'المحطه': 'المحطة', 'السفاره': 'السفارة', 'الوظيفه': 'الوظيفة',
2508
+ 'الصحيفه': 'الصحيفة', 'العائله': 'العائلة', 'الحياه': 'الحياة',
2509
+ 'الصلاه': 'الصلاة', 'الزكاه': 'الزكاة',
2510
+ # Common nouns — indefinite form
2511
+ 'حكومه': 'حكومة', 'مدرسه': 'مدرسة', 'شركه': 'شركة',
2512
+ 'جامعه': 'جامعة', 'مدينه': 'مدينة', 'قصه': 'قصة',
2513
+ 'مكتبه': 'مكتبة', 'طائره': 'طائرة', 'وزاره': 'وزارة',
2514
+ 'مديره': 'مديرة', 'معلمه': 'معلمة', 'طالبه': 'طالبة',
2515
+ 'قريه': 'قرية', 'حديقه': 'حديقة', 'محكمه': 'محكمة',
2516
+ 'منطقه': 'منطقة', 'دوله': 'دولة', 'سياره': 'سيارة',
2517
+ 'غرفه': 'غرفة', 'محطه': 'محطة', 'وظيفه': 'وظيفة',
2518
+ 'عائله': 'عائلة', 'حياه': 'حياة', 'صلاه': 'صلاة',
2519
+ # Common adjectives — feminine
2520
+ 'كبيره': 'كبيرة', 'صغيره': 'صغيرة', 'جميله': 'جميلة',
2521
+ 'طويله': 'طويلة', 'قصيره': 'قصيرة', 'جديده': 'جديدة',
2522
+ 'قديمه': 'قديمة', 'سريعه': 'سريعة', 'بطيئه': 'بطيئة',
2523
+ }
2524
  _hata_text = ctx.current_text
2525
  _hata_words = _hata_text.split()
2526
  _hata_changed = False
2527
  _hata_result = []
 
 
 
 
 
 
 
2528
  for _hw in _hata_words:
2529
  _hw_clean = _hw.rstrip('.،؛؟!?!')
2530
+ if _hw_clean in _HATA_WHITELIST:
2531
+ _punct_suffix = _hw[len(_hw_clean):]
2532
+ _fixed = _HATA_WHITELIST[_hw_clean]
2533
+ logger.info(f"[HA-TA] Post-grammar ه→ة: '{_hw}'→'{_fixed}{_punct_suffix}'")
2534
+ _hata_result.append(_fixed + _punct_suffix)
2535
+ _hata_changed = True
2536
+ else:
2537
+ _hata_result.append(_hw)
 
 
 
2538
  if _hata_changed:
2539
  _hata_new = ' '.join(_hata_result)
2540
  ctx.mutate_text(_hata_new, OffsetMapper)