arabcheck 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arabcheck/__init__.py +5 -0
- arabcheck/__main__.py +5 -0
- arabcheck/cli.py +59 -0
- arabcheck/core.py +49 -0
- arabcheck/patterns.py +27 -0
- arabcheck-0.1.0.dist-info/METADATA +247 -0
- arabcheck-0.1.0.dist-info/RECORD +11 -0
- arabcheck-0.1.0.dist-info/WHEEL +5 -0
- arabcheck-0.1.0.dist-info/entry_points.txt +2 -0
- arabcheck-0.1.0.dist-info/licenses/LICENSE +21 -0
- arabcheck-0.1.0.dist-info/top_level.txt +1 -0
arabcheck/__init__.py
ADDED
arabcheck/__main__.py
ADDED
arabcheck/cli.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""واجهة سطر الأوامر."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from . import __version__
|
|
8
|
+
from .core import ArabCheck
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def read_input(args, parser):
|
|
12
|
+
if args.file:
|
|
13
|
+
try:
|
|
14
|
+
return args.file.read_text(encoding="utf-8")
|
|
15
|
+
except FileNotFoundError:
|
|
16
|
+
parser.error(f"الملف غير موجود: {args.file}")
|
|
17
|
+
if args.text:
|
|
18
|
+
return args.text
|
|
19
|
+
if not sys.stdin.isatty():
|
|
20
|
+
return sys.stdin.read()
|
|
21
|
+
parser.error("لازم تمرّر نصاً، أو --file، أو stdin.")
|
|
22
|
+
return ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_parser():
|
|
26
|
+
p = argparse.ArgumentParser(prog="arabcheck", description="ArabCheck — تنظيف وفحص النصوص العربية.")
|
|
27
|
+
p.add_argument("text", nargs="?", help="النص")
|
|
28
|
+
p.add_argument("-f", "--file", type=Path, help="قراءة من ملف")
|
|
29
|
+
p.add_argument("-c", "--clean", action="store_true")
|
|
30
|
+
p.add_argument("-n", "--normalize", action="store_true")
|
|
31
|
+
p.add_argument("-a", "--audit", action="store_true")
|
|
32
|
+
p.add_argument("-j", "--json", action="store_true")
|
|
33
|
+
p.add_argument("-q", "--quiet", action="store_true")
|
|
34
|
+
p.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}")
|
|
35
|
+
return p
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main(argv=None) -> int:
|
|
39
|
+
parser = build_parser()
|
|
40
|
+
args = parser.parse_args(argv)
|
|
41
|
+
text = read_input(args, parser)
|
|
42
|
+
checker = ArabCheck()
|
|
43
|
+
result = checker.process(text, clean=args.clean, normalize=args.normalize, audit=args.audit)
|
|
44
|
+
if args.json:
|
|
45
|
+
payload = {**result, "meta": {
|
|
46
|
+
"cleaned": args.clean, "normalized": args.normalize,
|
|
47
|
+
"audited": args.audit, "version": __version__,
|
|
48
|
+
}}
|
|
49
|
+
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
50
|
+
else:
|
|
51
|
+
if not args.quiet:
|
|
52
|
+
print(result["result"])
|
|
53
|
+
for issue in result["issues"]:
|
|
54
|
+
print(f"⚠️ {issue['message']}", file=sys.stderr)
|
|
55
|
+
return 1 if result["issues"] else 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
sys.exit(main())
|
arabcheck/core.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""النواة الرئيسية لـ ArabCheck."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from .patterns import Patterns
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ArabCheck:
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
self.patterns = Patterns()
|
|
9
|
+
|
|
10
|
+
def strip_tashkeel(self, text: str) -> str:
|
|
11
|
+
return self.patterns.TASHKEEL.sub("", text)
|
|
12
|
+
|
|
13
|
+
def strip_tatweel(self, text: str) -> str:
|
|
14
|
+
return self.patterns.TATWEEL.sub("", text)
|
|
15
|
+
|
|
16
|
+
def clean_text(self, text: str) -> str:
|
|
17
|
+
text = self.strip_tashkeel(text)
|
|
18
|
+
text = self.strip_tatweel(text)
|
|
19
|
+
return self.patterns.WHITESPACE.sub(" ", text).strip()
|
|
20
|
+
|
|
21
|
+
def normalize(self, text: str, *, taa_marbuta: bool = False) -> str:
|
|
22
|
+
text = self.patterns.ALIF_VARIANTS.sub("ا", text)
|
|
23
|
+
text = self.patterns.YAA_VARIANTS.sub("ي", text)
|
|
24
|
+
text = self.patterns.HAMZA_WAW.sub("و", text)
|
|
25
|
+
text = self.patterns.HAMZA_YAA.sub("ي", text)
|
|
26
|
+
if taa_marbuta:
|
|
27
|
+
text = self.patterns.TAA_MARBUTA.sub("ه", text)
|
|
28
|
+
return text
|
|
29
|
+
|
|
30
|
+
def audit(self, text: str) -> list:
|
|
31
|
+
issues = []
|
|
32
|
+
for i, word in enumerate(text.split(), start=1):
|
|
33
|
+
if self.patterns.HAMZAT_QAT.match(word):
|
|
34
|
+
issues.append({
|
|
35
|
+
"type": "hamzat_qat",
|
|
36
|
+
"word": word,
|
|
37
|
+
"position": i,
|
|
38
|
+
"message": f"احتمال خطأ: '{word}' تبدأ بـ 'ال' + همزة قطع.",
|
|
39
|
+
})
|
|
40
|
+
return issues
|
|
41
|
+
|
|
42
|
+
def process(self, text, *, clean=False, normalize=False, audit=False):
|
|
43
|
+
issues = self.audit(text) if audit else []
|
|
44
|
+
result = text
|
|
45
|
+
if normalize:
|
|
46
|
+
result = self.normalize(result)
|
|
47
|
+
if clean:
|
|
48
|
+
result = self.clean_text(result)
|
|
49
|
+
return {"input": text, "result": result, "issues": issues}
|
arabcheck/patterns.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""أنماط regex المجمّعة مسبقاً."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Patterns:
|
|
8
|
+
"""أنماط regex لأداء أفضل (compiled once)."""
|
|
9
|
+
|
|
10
|
+
# التشكيل: حركات + علامات قرآنية + ألف خنجرية
|
|
11
|
+
TASHKEEL = re.compile(
|
|
12
|
+
"[\u0610-\u061A\u064B-\u065F\u0670"
|
|
13
|
+
"\u06D6-\u06DC\u06DF-\u06E8\u06EA-\u06ED]"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
TATWEEL = re.compile("\u0640")
|
|
17
|
+
WHITESPACE = re.compile(r"\s+")
|
|
18
|
+
|
|
19
|
+
# همزة قطع بعد "ال" التعريف
|
|
20
|
+
HAMZAT_QAT = re.compile(r"^ال[أإآ]")
|
|
21
|
+
|
|
22
|
+
# للتوحيد — كل واحد منفصل عشان يبقى واضح
|
|
23
|
+
ALIF_VARIANTS = re.compile(r"[إأآٱ]")
|
|
24
|
+
YAA_VARIANTS = re.compile(r"[ىي]")
|
|
25
|
+
TAA_MARBUTA = re.compile(r"ة")
|
|
26
|
+
HAMZA_WAW = re.compile(r"ؤ")
|
|
27
|
+
HAMZA_YAA = re.compile(r"ئ")
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arabcheck
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight toolkit and CLI for cleaning, normalizing, and auditing Arabic text.
|
|
5
|
+
Author-email: Mohamed Kamal Salih Mahmod <102423366+MOHAMEDKAMALSALLIH@users.noreply.github.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/MOHAMEDKAMALSALLIH/arabcheck
|
|
8
|
+
Project-URL: Repository, https://github.com/MOHAMEDKAMALSALLIH/arabcheck
|
|
9
|
+
Project-URL: Issues, https://github.com/MOHAMEDKAMALSALLIH/arabcheck/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/MOHAMEDKAMALSALLIH/arabcheck/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Releases, https://github.com/MOHAMEDKAMALSALLIH/arabcheck/releases
|
|
12
|
+
Keywords: arabic,nlp,text-cleaning,tashkeel
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Natural Language :: Arabic
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# ArabCheck 🔍
|
|
25
|
+
|
|
26
|
+
> A lightweight, open-source toolkit and CLI for cleaning, normalizing, and auditing Arabic text for AI and NLP workflows.
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Why ArabCheck?
|
|
35
|
+
|
|
36
|
+
Arabic text data often contains noise that breaks NLP pipelines:
|
|
37
|
+
|
|
38
|
+
- **Unicode variants** — `أ` / `إ` / `آ` / `ا` / `ٱ` all represent alif
|
|
39
|
+
- **Diacritics (Tashkeel)** — fatha, damma, shadda, tanween
|
|
40
|
+
- **Tatweel (Kashida)** — decorative stretching characters `ـــــ`
|
|
41
|
+
- **Quranic marks** and extra whitespace
|
|
42
|
+
|
|
43
|
+
ArabCheck makes cleaning and auditing this data a one-liner — with full transparency and configurable operations.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- 🕌 **Tashkeel removal** — Harakat + Quranic marks
|
|
50
|
+
- 🔤 **Character normalization** — Alif, Yaa, Hamza, Taa Marbuta
|
|
51
|
+
- 〰️ **Tatweel removal** — Kashida stretching characters
|
|
52
|
+
- 🧹 **Text cleaning** — Whitespace normalization
|
|
53
|
+
- 🔍 **Grammar audit** — Detect common hamzat qat/wasl errors
|
|
54
|
+
- 📂 **File + stdin support**
|
|
55
|
+
- 🤖 **JSON output** for CI/CD pipelines
|
|
56
|
+
- 🧩 **Extensible** — easy to add new rules
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/MOHAMEDKAMALSALLIH/arabcheck.git
|
|
64
|
+
cd arabcheck
|
|
65
|
+
pip install -e .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Requirements: Python 3.8+ — no external dependencies.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
Quick Start
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
arabcheck "النَّصُّ العَرَبِيُّ لِلتَّجْرِبَة" --clean
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Input:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
النَّصُّ العَرَبِيُّ لِلتَّجْرِبَة
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Output:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
النص العربي للتجربة
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
Usage Examples
|
|
93
|
+
|
|
94
|
+
1. Clean text
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
arabcheck "العَرَبِيَّةُ ـــ لُغَةٌ جَمِيلَةٌ" --clean
|
|
98
|
+
# → العربية لغة جميلة
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
2. Normalize characters
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
arabcheck "أحمد إبراهيم آمن" --normalize
|
|
105
|
+
# → احمد ابراهيم امن
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
⚠️ Warning: Normalization loses linguistic information. Use it for search, indexing, or NLP pipelines only.
|
|
109
|
+
|
|
110
|
+
3. Audit text
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
arabcheck "الأمر بالأمر" --audit
|
|
114
|
+
# ⚠️ احتمال خطأ: 'الأمر' تبدأ بـ 'ال' + همزة قطع.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
4. JSON output (for automation)
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
arabcheck "النَّصُّ" --clean --json
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"input": "النَّصُّ",
|
|
126
|
+
"result": "النص",
|
|
127
|
+
"issues": [],
|
|
128
|
+
"meta": {
|
|
129
|
+
"cleaned": true,
|
|
130
|
+
"normalized": false,
|
|
131
|
+
"audited": false,
|
|
132
|
+
"version": "0.1.0"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
5. Read from file
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
arabcheck --file dataset.txt --clean
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
6. From stdin
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
cat article.txt | arabcheck --clean
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
CLI Reference
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
usage: arabcheck [-h] [-f FILE] [-c] [-n] [-a] [-j] [-q] [-V] [text]
|
|
155
|
+
|
|
156
|
+
positional arguments:
|
|
157
|
+
text Text to process
|
|
158
|
+
|
|
159
|
+
options:
|
|
160
|
+
-h, --help Show help
|
|
161
|
+
-f, --file FILE Read text from file
|
|
162
|
+
-c, --clean Remove tashkeel, tatweel and extra whitespace
|
|
163
|
+
-n, --normalize Normalize letters (alif, yaa, etc.)
|
|
164
|
+
-a, --audit Audit common spelling issues
|
|
165
|
+
-j, --json Output as JSON
|
|
166
|
+
-q, --quiet Suppress text output
|
|
167
|
+
-V, --version Show version
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Exit Codes
|
|
171
|
+
|
|
172
|
+
Code Meaning
|
|
173
|
+
0 Success, no issues
|
|
174
|
+
1 Issues found (with --audit)
|
|
175
|
+
2 Input error
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
Use as a Library
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from arabcheck import ArabCheck
|
|
183
|
+
|
|
184
|
+
checker = ArabCheck()
|
|
185
|
+
|
|
186
|
+
# Clean
|
|
187
|
+
text = checker.clean_text("النَّصُّ العَرَبِيُّ ـــ")
|
|
188
|
+
print(text) # → النص العربي
|
|
189
|
+
|
|
190
|
+
# Normalize
|
|
191
|
+
print(checker.normalize("أحمد إبراهيم"))
|
|
192
|
+
# → احمد ابراهيم
|
|
193
|
+
|
|
194
|
+
# Audit
|
|
195
|
+
issues = checker.audit("الأمر بالأمر")
|
|
196
|
+
for issue in issues:
|
|
197
|
+
print(issue["message"])
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
Testing
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
pip install -e ".[dev]"
|
|
206
|
+
pytest
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
Roadmap
|
|
212
|
+
|
|
213
|
+
☑ Tashkeel & Tatweel removal
|
|
214
|
+
☑ Character normalization
|
|
215
|
+
☑ File + stdin support
|
|
216
|
+
☑ JSON output
|
|
217
|
+
☑ Exit codes for CI/CD
|
|
218
|
+
☑ Grammar audit (hamzat qat/wasl)
|
|
219
|
+
☐ Extended audit rules
|
|
220
|
+
☐ Dataset loading (CSV, JSONL)
|
|
221
|
+
☐ Duplicate detection
|
|
222
|
+
☐ Text quality statistics
|
|
223
|
+
☐ Publish on PyPI
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
Contributing
|
|
228
|
+
|
|
229
|
+
Contributions, issues, and feature requests are welcome! See CONTRIBUTING.md for guidelines.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
Releases
|
|
234
|
+
|
|
235
|
+
See Releases for version history.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
License
|
|
240
|
+
|
|
241
|
+
MIT — see LICENSE for details.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
<p align="center">
|
|
246
|
+
Made with ❤️ for the Arabic NLP community
|
|
247
|
+
</p>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
arabcheck/__init__.py,sha256=k3WJRkPK83D3GeDH8_Fz1CLXDoRJ9Jplj82xWqKkTNc,143
|
|
2
|
+
arabcheck/__main__.py,sha256=R8toRIEpaunLvLRpFhTKF4x0aBUmBLoqHo0IrF4wuTw,82
|
|
3
|
+
arabcheck/cli.py,sha256=E-LY4gFpBzIaEeC2CnktrKlj06Yj1fYqPxmvqI-zqVA,2066
|
|
4
|
+
arabcheck/core.py,sha256=Z5IRDPqd-1rdsjCa3JiBACa125XNDVE6kz4p0rq1zA0,1804
|
|
5
|
+
arabcheck/patterns.py,sha256=BAEsf17cMsST8FiGx1WvjsPUVJwJH846rrUX6dcmFDg,833
|
|
6
|
+
arabcheck-0.1.0.dist-info/licenses/LICENSE,sha256=Z0UBEKMkWFJy26jZ6lmBo47o-s7PD0_qbM0sdxWUsws,1077
|
|
7
|
+
arabcheck-0.1.0.dist-info/METADATA,sha256=3n3-gTHf9lYiYZ7CJZEb1QS2KaLM9jMl4EjJO6Nsmk0,5423
|
|
8
|
+
arabcheck-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
9
|
+
arabcheck-0.1.0.dist-info/entry_points.txt,sha256=SubV1UOG9_4s0SYJiqhB5BS08pMtM7em-hVXQ8hN9b4,49
|
|
10
|
+
arabcheck-0.1.0.dist-info/top_level.txt,sha256=Eve9P2VpSSbULW8YxBF42OiN9OxkSXk5ntiJA45tEqA,10
|
|
11
|
+
arabcheck-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mohamed Kamal Sallih
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
arabcheck
|