Commit ·
3e90908
1
Parent(s): 2ff1e5e
FIX-48v3: Whitelist-based ه→ة (replaces broken stem-IV approach)
Browse filesThe 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.
- 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-
|
| 2494 |
# Must run AFTER grammar so grammar model can use ه for gender decisions.
|
| 2495 |
-
#
|
| 2496 |
if not _is_religious_text:
|
| 2497 |
try:
|
| 2498 |
-
|
| 2499 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 2514 |
-
|
| 2515 |
-
|
| 2516 |
-
|
| 2517 |
-
|
| 2518 |
-
|
| 2519 |
-
|
| 2520 |
-
|
| 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)
|