iraqi-dialect-bad-words 1.3.0 → 2.0.0

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.
package/README.ar.md ADDED
@@ -0,0 +1,352 @@
1
+ # iraqi-dialect-bad-words
2
+
3
+ [English](README.md) | **العربية**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/iraqi-dialect-bad-words.svg)](https://www.npmjs.com/package/iraqi-dialect-bad-words)
6
+ [![CI](https://github.com/karam-fikre/iraqi-dialect-bad-words/actions/workflows/ci.yml/badge.svg)](https://github.com/karam-fikre/iraqi-dialect-bad-words/actions/workflows/ci.yml)
7
+ [![license](https://img.shields.io/npm/l/iraqi-dialect-bad-words.svg)](LICENSE)
8
+
9
+ فلتر للكلمات البذيئة في اللهجة العراقية. يكتشف الكلمات البذيئة في النصوص التي يكتبها المستخدمون ويخفيها.
10
+
11
+ يعمل في **React Native / Expo** و **React** ‏(Vite و Next.js و CRA وغيرها) و **Vue** ‏(Vite و Nuxt) و **Node.js**، ويأتي مع أنواع TypeScript، وبلا أي اعتماديات.
12
+
13
+ ## التثبيت
14
+
15
+ ```bash
16
+ npm install iraqi-dialect-bad-words
17
+ # أو
18
+ yarn add iraqi-dialect-bad-words
19
+ # أو
20
+ npx expo install iraqi-dialect-bad-words
21
+ ```
22
+
23
+ ## الاستخدام
24
+
25
+ ```ts
26
+ import IraqiBadWordsFilter from 'iraqi-dialect-bad-words';
27
+ // CommonJS: const { IraqiBadWordsFilter } = require('iraqi-dialect-bad-words');
28
+
29
+ const filter = new IraqiBadWordsFilter();
30
+
31
+ filter.isProfane('مرحبا بك'); // false
32
+ filter.isProfane('هذا كلام عارة'); // true
33
+
34
+ filter.clean('هذا كلام عارة'); // 'هذا كلام ****'
35
+ filter.clean('هذا كلام عارة', '#'); // 'هذا كلام ####'
36
+ filter.clean('هذا كلام عارة', ''); // 'هذا كلام '
37
+ ```
38
+
39
+ ### مثال React Native
40
+
41
+ ```tsx
42
+ import { useState } from 'react';
43
+ import { TextInput, Text } from 'react-native';
44
+ import IraqiBadWordsFilter from 'iraqi-dialect-bad-words';
45
+
46
+ const filter = new IraqiBadWordsFilter(); // أنشئه مرة واحدة، خارج المكوّن
47
+
48
+ export function CommentInput() {
49
+ const [text, setText] = useState('');
50
+
51
+ return (
52
+ <>
53
+ <TextInput value={text} onChangeText={setText} />
54
+ {filter.isProfane(text) && <Text>يرجى الالتزام بالاحترام.</Text>}
55
+ </>
56
+ );
57
+ }
58
+ ```
59
+
60
+ ### مثال Vue
61
+
62
+ أنشئ الفلتر مرة واحدة في ملف خاص به واستورده حيث تحتاجه. لا تضعه داخل `ref()` أو `reactive()`: الفلتر لا يتغير، وتغليفه بكائنات تفاعلية يبطّئ كل فحص فقط.
63
+
64
+ ```ts
65
+ // src/profanity.ts
66
+ import IraqiBadWordsFilter from 'iraqi-dialect-bad-words';
67
+
68
+ export const filter = new IraqiBadWordsFilter();
69
+ ```
70
+
71
+ ```vue
72
+ <script setup lang="ts">
73
+ import { ref, computed } from 'vue';
74
+ import { filter } from '@/profanity';
75
+
76
+ const text = ref('');
77
+ const isProfane = computed(() => filter.isProfane(text.value));
78
+ </script>
79
+
80
+ <template>
81
+ <textarea v-model="text" dir="auto" />
82
+ <p v-if="isProfane">يرجى الالتزام بالاحترام.</p>
83
+ <button :disabled="isProfane">إرسال</button>
84
+ </template>
85
+ ```
86
+
87
+ لتمييز الكلمات البذيئة، اعرض النص قطعًا مقسّمة عند `start` و `end` لكل تطابق من [`inspect()`](#inspecttext-string-options-profanitymatch)، وليس باستخدام `v-html`، حتى لا يستطيع نص المستخدم حقن HTML.
88
+
89
+ في أي إطار عمل، افحص النص على الخادم أيضًا (مسار خادم في Nuxt أو أي خادم Node يستطيع استخدام الحزمة نفسها): الفحص في المتصفح يمكن تجاوزه باستدعاء الـ API مباشرة.
90
+
91
+ ## الواجهة البرمجية (API)
92
+
93
+ ### `new IraqiBadWordsFilter(options?)`
94
+
95
+ ينشئ فلترًا بقائمة الكلمات المدمجة.
96
+
97
+ ```ts
98
+ new IraqiBadWordsFilter(); // الكلمات البذيئة فقط
99
+ new IraqiBadWordsFilter({ includeInsults: true }); // والشتائم الأخف أيضًا
100
+ new IraqiBadWordsFilter({ includePhrases: true }); // والعبارات البذيئة أيضًا
101
+ new IraqiBadWordsFilter({ stripSuffixes: true }); // والصيغ بلواحق غير مدرجة أيضًا
102
+ new IraqiBadWordsFilter({ dialects: ['mosul'] }); // وصيغ الموصل ر → غ أيضًا
103
+ new IraqiBadWordsFilter({ customWords: ['word1', 'word2'] });
104
+ new IraqiBadWordsFilter({ customWords: [{ word: 'word1', severity: 'mild' }] });
105
+ new IraqiBadWordsFilter({ useDefaultList: false, customWords: ['word1'] }); // كلماتك فقط
106
+ new IraqiBadWordsFilter({ safeWords: ['word3'] }); // كلمات سليمة لا تُعلَّم
107
+ new IraqiBadWordsFilter(['word1', 'word2']); // مثل customWords
108
+ ```
109
+
110
+ | الخيار | الافتراضي | الوصف |
111
+ |---|---|---|
112
+ | `customWords` | `[]` | كلمات إضافية للفلترة، فوق القائمة المدمجة. يمكن أن تأتي من ملف JSON خاص بك (انظر أدناه). الكلمة `'strong'` ما لم تُعطَ بصيغة `{ word, severity }` (انظر [درجة الشدة](#درجة-الشدة)). |
113
+ | `useDefaultList` | `true` | القيمة `false` تستبعد [`words.json`](src/words.json) المدمج، فلا تُفلتر إلا كلماتك. يبقى `includeInsults` و `includePhrases` يضيفان قائمتيهما، وتبقى الجذور الآمنة المدمجة. |
114
+ | `safeWords` | `[]` | جذور آمنة إضافية، فوق [`safe-stems.json`](src/safe-stems.json) المدمج: كلمات سليمة كانت ستُطابَق بعد حذف سابقة أو لاحقة، مثل `بعير` المدمجة (`ب` + `عير`). استخدمه للكلمات السليمة التي يعلّمها تطبيقك خطأً، خاصة تلك التي تسببها كلمات `customWords` الخاصة بك. صيغها بالسوابق واللواحق آمنة أيضًا (`وبعيرك`). يمكن أن تأتي من ملف JSON خاص بك (انظر أدناه). |
115
+ | `includeInsults` | `false` | يفلتر أيضًا الشتائم الأخف مثل كلب، حمار، قندرة، نعال. معطّل افتراضيًا لأن كثيرًا منها كلمات يومية، فتفعيله يعلّم جملًا مثل `عندي كلب`. |
116
+ | `includePhrases` | `false` | يفلتر أيضًا العبارات في [`phrases.json`](src/phrases.json): كلمات سليمة تصبح بذيئة معًا، مثل `تعال العب بيه`. معطّل افتراضيًا لأن الكلمات نفسها قد تُقصد بمعناها الحرفي. |
117
+ | `customPhrases` | `[]` | عبارات إضافية للفلترة، سواء كان `includePhrases` مفعّلًا أم لا. يمكن أن تأتي من ملف JSON خاص بك. العبارة `'strong'` ما لم تُعطَ بصيغة `{ phrase, severity }`. |
118
+ | `stripSuffixes` | `false` | يطابق أيضًا الكلمات بلاحقة ضمير (ي ك ج ه ها نا كم هم) غير مكتوبة في القائمة، مثل `طيزهم` ← `طيز`. معطّل افتراضيًا لأنه يوسّع المطابقة: الكلمة السليمة التي تنتهي كأنها كلمة بذيئة مع لاحقة تُعلَّم أيضًا. |
119
+ | `dialects` | `[]` | لهجات محلية تُطابَق أيضًا. `'mosul'`: تُلفظ ر غينًا، فكل مدخل فيه ر يطابق أيضًا صيغته بالغين (`شرموطة` ← `شغموطة`)، وتبقى صيغ الراء تُطابَق. والجذور الآمنة تحصل على صيغة الغين أيضًا. |
120
+
121
+ #### استخدام ملفات كلماتك الخاصة
122
+
123
+ احفظ الكلمات الإضافية لتطبيقك، والكلمات السليمة التي لا تريد تعليمها، في ملفات JSON: مصفوفات من كلمات مفردة، بنفس صيغة [`src/safe-stems.json`](src/safe-stems.json). والكلمة التي تحتاج [درجة شدة](#درجة-الشدة) خاصة بها يمكن أن تكون كائنًا:
124
+
125
+ ```json
126
+ ["زفت", { "word": "نذل", "severity": "mild" }]
127
+ ```
128
+
129
+ استوردها ومرّرها في `customWords` و `safeWords`. تبقى القوائم المدمجة مفعّلة، وتُضاف كلماتك إليها:
130
+
131
+ ```ts
132
+ import IraqiBadWordsFilter from 'iraqi-dialect-bad-words';
133
+ import myWords from './my-words.json'; // يحتاج TypeScript إلى "resolveJsonModule": true
134
+ import mySafeWords from './my-safe-words.json';
135
+ // CommonJS: const myWords = require('./my-words.json');
136
+
137
+ const filter = new IraqiBadWordsFilter({ customWords: myWords, safeWords: mySafeWords });
138
+ ```
139
+
140
+ تمر كلماتك بنفس توحيد الكتابة الذي تمر به القوائم المدمجة، فلا تحتاج إلى إدراج الصيغ بالتشكيل أو ة/ه أو الحروف الممطوطة أو السوابق. تُحذف المسافات حول الكلمة وتُتجاهل المدخلات الفارغة. يبلّغ `inspect()` عن التطابق من ملف كلماتك بـ `source: 'custom'`، وعن التطابق الذي ألغته كلماتك الآمنة بـ `suppressedBy` مساويًا لتلك الكلمة الآمنة.
141
+
142
+ الكلمة الآمنة لا تلغي إلا تطابقًا احتاج إلى حذف سابقة أو لاحقة، مثل الجذور الآمنة المدمجة. لمنع تعليم كلمة مدرجة في القائمة نفسها، استخدم `removeWords()`.
143
+
144
+ تحزم React Native و Expo و Vite و Next.js ملفات JSON داخل تطبيقك، فيعمل هذا بالطريقة نفسها في كل منها. الفلتر لا يقرأ ملفات أثناء التشغيل. إذا كانت قوائمك تأتي من خادم، فاجلبها ومرّر المصفوفات إلى `addWords()` و `addSafeWords()`.
145
+
146
+ ### `isProfane(text: string): boolean`
147
+
148
+ يرجع `true` إذا كانت أي كلمة في النص موجودة في القائمة.
149
+
150
+ ### `clean(text: string, placeholder = '*'): string`
151
+
152
+ يستبدل كل كلمة بذيئة بالرمز البديل، مكررًا بطول الكلمة. تبقى المسافات وعلامات الترقيم كما هي.
153
+
154
+ يمكن أن يكون البديل دالة أيضًا: تستلم كل تطابق من [`inspect()`](#inspecttext-string-options-profanitymatch) وترجع النص الذي يوضع مكانه. مثلًا، أبقِ الحرف الأول، أو أخفِ الكلمات الأشد فقط:
155
+
156
+ ```ts
157
+ filter.clean('انت عارة', (match) => {
158
+ const [first, ...rest] = Array.from(match.token); // يعدّ Array.from الحرف 𝐠 أو الإيموجي حرفًا واحدًا
159
+ return first + '*'.repeat(rest.length);
160
+ });
161
+ // 'انت ع***'
162
+
163
+ filter.clean(text, (match) => (match.severity === 'mild' ? match.token : '***'));
164
+ ```
165
+
166
+ ### `inspect(text: string, options?): ProfanityMatch[]`
167
+
168
+ يسرد كل كلمة بذيئة في النص ويشرح سبب مطابقتها. استخدمه لتمييز الكلمات، أو للتعامل مع الكلمات بحسب شدتها (مثلًا حظر `'severe'` والاكتفاء بالتحذير من `'mild'`)، أو ليراجع شخص التطابق قبل أن يحظره تطبيقك.
169
+
170
+ ```ts
171
+ filter.inspect('روح للكواد!');
172
+ // [{ token: 'للكواد', start: 4, end: 10, normalized: 'لكواد', entry: 'كواد',
173
+ // source: 'words', severity: 'strong', prefix: 'ل', suffix: '', rules: ['stretched', 'prefix'] }]
174
+ ```
175
+
176
+ | الحقل | الوصف |
177
+ |---|---|
178
+ | `token` | الكلمة كما كُتبت تمامًا. |
179
+ | `start`، `end` | المواقع في النص **الأصلي**، بحيث `text.slice(start, end) === token`. تُعدّ بوحدات سلاسل JavaScript ‏(UTF-16)؛ حوّلها إذا كانت منصة أخرى تعدّ الحروف بطريقة مختلفة. |
180
+ | `normalized` | الكلمة بعد توحيد الكتابة (والعربيزي يُوحَّد أيضًا، فتظهر `Qa7ba` بصيغة `gahba`). للكلمة التي فيها گ، القراءة التي طابقت (ق أو ك). |
181
+ | `entry` | المدخل الذي طابقته في القائمة، بالإملاء المكتوب في القائمة. لصيغة موصلية، الإملاء المدرج بالراء. |
182
+ | `source` | `'words'` أو `'insults'` أو `'phrases'` أو `'custom'`. الكلمة الموجودة في أكثر من قائمة تحتفظ بالأولى: المدمجة، ثم الشتائم، ثم الخاصة. |
183
+ | `severity` | `'mild'` أو `'strong'` أو `'severe'` (انظر أدناه). |
184
+ | `prefix` | السابقة الملتصقة التي حُذفت قبل المطابقة (بعد توحيد الكتابة، فتظهر `لل` بصيغة `ل`)، أو `''` للتطابق المباشر. |
185
+ | `suffix` | اللاحقة التي حُذفت قبل المطابقة (مع `stripSuffixes` فقط)، أو `''`. |
186
+ | `rules` | القواعد التي احتاجتها الكلمة لتطابق المدخل (انظر أدناه). فارغة إذا كانت مكتوبة تمامًا مثل المدخل. |
187
+ | `suppressedBy` | مع `includeSuppressed` فقط: الجذر الآمن الذي ألغى هذا التطابق. |
188
+
189
+ مرّر `{ includeSuppressed: true }` لترى أيضًا الكلمات التي طابقت لكن ألغاها جذر آمن. هذه ليست بذيئة (يتجاهلها `isProfane` و `clean`)، لكنها تبيّن لماذا *لم* تُعلَّم الكلمة:
190
+
191
+ ```ts
192
+ filter.inspect('وبعيرك', { includeSuppressed: true });
193
+ // [{ token: 'وبعيرك', …, entry: 'عيرك', prefix: 'وب', rules: ['prefix'], suppressedBy: 'بعير' }]
194
+ ```
195
+
196
+ يسرد `rules` القواعد التي أحدثت فرقًا بين الكلمة المكتوبة والمدخل فقط، فيبيّن التقرير مباشرة لماذا طابق شيء ما:
197
+
198
+ | القاعدة | مثال |
199
+ |---|---|
200
+ | `unicode-forms` | `ﻋﻴﺮ`، `gahba`، `𝐠𝐚𝐡𝐛𝐚` (أشكال العرض، والحروف كاملة العرض والرياضية) |
201
+ | `diacritics` | `عَارَة`، `عـــارة` |
202
+ | `invisible` | `عا‌رة` (حرف بعرض صفري داخلها) |
203
+ | `spelling` | `عاره` (ة/ه، ى/ي، أ/ا) |
204
+ | `keyboard` | `عیر`، `کس`، `پورنو` (حروف فارسية/كردية) |
205
+ | `che-as-kaf` | `چلب` ← كلب |
206
+ | `lookalike` | `gаhba` (حرف `а` سيريلي في كلمة لاتينية) |
207
+ | `arabic-digits` | `ga٧ba` |
208
+ | `stretched` | `عاااارة`، `للكواد` |
209
+ | `glued-digits` | `عارة3` |
210
+ | `arabizi` | `qa7ba` ← `gahba` |
211
+ | `gaf-as-qaf` / `gaf-as-kaf` | `گحبة` ← قحبة، `گسها` ← كسها |
212
+ | `prefix` / `suffix` | `بطيزك`، `ياعارة`، `طيزهم` (مع `stripSuffixes`) |
213
+ | `mosul` | `شغموطة` ← شرموطة (مع `dialects: ['mosul']`) |
214
+ | `phrase` | `تعال العب بيه` (مع `includePhrases`) |
215
+ | `split` | `ع*ي*ر`، `ع ي ر` (القطع تُجمع من جديد) |
216
+ | `repeated` | `عيرعير`، `gahbagahba` (مدخل مكتوب عدة مرات بلا مسافة) |
217
+ | `symbols` | `g@hba`، `$harmoota` (‏@ بمعنى a، و $ بمعنى s) |
218
+
219
+ #### درجة الشدة
220
+
221
+ لكل تطابق `severity`، فيستطيع التطبيق أن يحظر بعض الكلمات ويكتفي بالتحذير من غيرها:
222
+
223
+ | الدرجة | ماذا تشمل | أمثلة |
224
+ |---|---|---|
225
+ | `'mild'` | شتائم لها معنى يومي: كل ما في [`insults.json`](src/insults.json) | `كلب`، `حمار`، `قندرة` |
226
+ | `'strong'` | كلمات بذيئة وشتائم ليس لها معنى سليم | `كواد`، `سرسري`، `يلعن ابوك` |
227
+ | `'severe'` | كلمات جنسية وصريحة | `كس`، `شرموطة`، `تعال العب بيه` |
228
+
229
+ كلماتك وعباراتك الخاصة `'strong'` ما لم تعطها درجة (`{ word: 'نذل', severity: 'mild' }`). والكلمة الموجودة أصلًا في قائمة مدمجة تحتفظ بدرجة تلك القائمة.
230
+
231
+ ```ts
232
+ const matches = filter.inspect(comment);
233
+ if (matches.some((match) => match.severity === 'severe')) rejectComment();
234
+ else if (matches.length) flagForReview();
235
+ ```
236
+
237
+ إرفاق ناتج `inspect` في [بلاغ “Clean word flagged”](https://github.com/karam-fikre/iraqi-dialect-bad-words/issues/new?template=false-positive.yml) يبيّن بالضبط أي مدخل طابق وأي قواعد احتاج؛ ونموذج البلاغ فيه حقل لذلك.
238
+
239
+ ### `addWords(words: (string | CustomWord)[]): void`
240
+
241
+ يضيف كلمات إلى هذا الفلتر، مثل خيار `customWords`.
242
+
243
+ ### `addSafeWords(words: string[]): void` / `removeSafeWords(words: string[]): void`
244
+
245
+ يضيف جذورًا آمنة إلى هذا الفلتر، مثل خيار `safeWords`، أو يحذفها، بما فيها المدمجة (مع صيغها الموصلية عند تفعيل اللهجة).
246
+
247
+ ### `removeWords(words: string[]): void`
248
+
249
+ يحذف كلمات من هذا الفلتر، مثلًا كلمة مدمجة لا تريد تعليمها في تطبيقك.
250
+
251
+ ### `addPhrases(phrases: (string | CustomPhrase)[]): void` / `removePhrases(phrases: string[]): void`
252
+
253
+ يضيف عبارات إلى هذا الفلتر، مثل خيار `customPhrases`، أو يحذفها، بما فيها المدمجة.
254
+
255
+ العبارة هي كلماتها مفصولة بمسافات. ويمكن للكلمة أن تسرد بدائل مفصولة بـ `|`، فيغطي مدخل واحد كل التركيبات:
256
+
257
+ ```json
258
+ [
259
+ "قوم|كوم بيه|بينو"
260
+ ]
261
+ ```
262
+
263
+ يطابق `قوم بيه` و `قوم بينو` و `كوم بيه` و `كوم بينو`. تطابق العبارة عندما تأتي كلماتها واحدة بعد الأخرى وبينها مسافات أو علامات ترقيم فقط (`تعال، العب بيه!`). كل كلمة يُوحَّد إملاؤها مثل أي كلمة أخرى، فتطابق `گوم` البديل `قوم` من دون إدراجها. ويمكن للكلمة الأولى أن تحمل سابقة ملتصقة (`وتعال العب بيه`) أو دال الاستمرار العراقية `د` (`دقوم بيه`). الصيغ الأخرى لا تُحذف ولا تُخمَّن، فأدرج كل صيغة تريدها بديلًا.
264
+
265
+ تُبلَّغ العبارة كاملة تطابقًا واحدًا بالقاعدة `phrase`، ويكون `entry` هو السطر كما أُدرج، ويخفيها `clean()` كلها. يأخذ `removePhrases()` السطر كما أُدرج؛ ولا يهم ترتيب بدائله.
266
+
267
+ ### قوائم الكلمات: `iraqi-dialect-bad-words/lists`
268
+
269
+ القوائم المدمجة مُصدَّرة أيضًا وحدها، للأدوات التي لا تستطيع استخدام الفلتر (استعلام قاعدة بيانات، أو خادم بلغة أخرى):
270
+
271
+ ```ts
272
+ import { words, insults, phrases, safeStems } from 'iraqi-dialect-bad-words/lists';
273
+
274
+ words.severe; // ['عارة', 'كس', …]
275
+ words.strong; // ['كواد', 'كواويد', …]
276
+ insults; // ['كلب', 'حمار', …]
277
+ ```
278
+
279
+ ‏`words` و `phrases` مجمّعة بحسب درجة الشدة؛ و `insults` كلها `'mild'`. المدخلات كما أُدرجت، ولا يمكن تعديلها. مطابقتها بمقارنة نصية بسيطة تلتقط أقل بكثير مما يلتقطه الفلتر: فهي لا توحّد الإملاء ولا تحذف السوابق ولا توحّد العربيزي ولا تضيف صيغ الموصل.
280
+
281
+ ## ماذا يلتقط
282
+
283
+ تتم المطابقة على الكلمة كاملة، بعد توحيد كتابة النص وقائمة الكلمات معًا، فكل هذه تطابق المدخل نفسه:
284
+
285
+ | الاختلاف | مثال |
286
+ |---|---|
287
+ | التشكيل | `عَارَة` |
288
+ | الحروف الممطوطة | `عاااارة` |
289
+ | التطويل | `عـــارة` |
290
+ | ة / ه، ى / ي، أ / إ / آ / ا | `عاره` |
291
+ | حروف لوحة المفاتيح الفارسية/الكردية ی ک ە پ ڤ | `عیر`، `کس`، `پورنو` |
292
+ | گ تُقرأ ق (عراقيًا) وك (كما يكتبها الكرد) | `گحبة` = `قحبة`، `گس` = `كس` |
293
+ | چ تُقرأ ك، لأن العراقي يلفظ الكاف چ في كلمات كثيرة | `چلب` = `كلب` |
294
+ | حروف غير مرئية مخبأة داخل الكلمة (بعرض صفري، علامات اتجاه، شرطة ناعمة) | `عا‌رة` |
295
+ | حروف مفصولة برموز، أو بمسافات بين حروف مفردة | `ع*ي*ر`، `ع.ي.ر`، `ع ي ر`، `عي * ر`، `شرم.وطة`، `g-a-h-b-a` |
296
+ | أشكال العرض العربية، والحروف كاملة العرض والرياضية | `ﻋﻴﺮ`، `gahba`، `𝐠𝐚𝐡𝐛𝐚` |
297
+ | حروف يونانية أو سيريلية تشبه اللاتينية داخل كلمة لاتينية | `gаhba` (حرف `а` سيريلي) |
298
+ | السوابق الملتصقة و ف ب ل ك ال، و يا / ويا | `والعارة`، `للعارة`، `ياعارة`، `يالعارة`، `وياعارة` |
299
+ | لواحق الضمائر (مع `stripSuffixes`) | `طيزهم`، `وطيزكم` |
300
+ | الكلمة نفسها مكتوبة عدة مرات بلا مسافة | `عيرعير`، `gahbagahba` |
301
+ | علامات الترقيم الملاصقة للكلمة | `عارة!` `عارة،` |
302
+ | أرقام ملتصقة بالكلمة العربية أو داخلها | `عارة3`، `عا٣رة` |
303
+ | العربيزي (حروف وأرقام لاتينية، غربية أو هندية)، مع اختلافات الإملاء | `ga7ba` = `ga٧ba` = `qahba` = `gahba`، `kosomak` = `kusumak`، `3ayri` = `ayri`، `6eezak` = `teezak` (‏6 = ط)، `9awad` = `8awad` = `gawad` (‏9 و 8 = ق) |
304
+ | ‏@ و $ بدل حروف في العربيزي | `g@hba`، `$harmoota` |
305
+
306
+ لأن المطابقة على الكلمة كاملة، فالكلمة السليمة التي تحتوي كلمة بذيئة داخلها لا تُعلَّم.
307
+
308
+ تُكتشف الكلمات المقسّمة بجمع القطع المتجاورة عندما تكون الفجوة بينها قصيرة، وعلى سطر واحد، وإما بلا مسافات (`عي*ر`)، أو بين حروف مفردة (`ع ي ر`)، أو فيها رمز بين قطع من حرف أو حرفين (`عي * ر`). الجمل العادية مثل `انا و انت` أو `د. علي` تبقى منفصلة، والكلمة المجموعة يجب أن تطابق مدخلًا تمامًا. والجذر الآمن المقسّم يبقى آمنًا: `ب ع ي ر` هي بعير.
309
+
310
+ ## القيود المعروفة
311
+
312
+ - العبارات تطابق كما أُدرجت فقط، كلمة بكلمة: كلمة إضافية بينها (`تعال هنا العب بيه`) أو صيغة غير مدرجة (`بيهم`) لا تُطابَق.
313
+ - الكلمات المختلفة الملتصقة بلا مسافة لا تُكتشف. أما الكلمة نفسها مكتوبة عدة مرات (`عيرعير`) فتُكتشف.
314
+ - الحرف المستبدل برمز (`ع*ر`، `ك*بة`) لا يُكتشف: الرمز قد يعني أي حرف، و `ع*ر` قد تكون عمر.
315
+ - القطع الأطول المفصولة بمسافات حول الرمز (`شرم * وطة`) لا تُجمع، حتى لا تُجمع الجمل العادية أيضًا.
316
+ - لا توجد قائمة كلمات كاملة. استخدم هذه الحزمة طبقة واحدة من طبقات الإشراف على المحتوى، لا الطبقة الوحيدة.
317
+
318
+ ## المساهمة
319
+
320
+ قائمة الكلمات هي قلب هذه الحزمة، وأهل اللهجة هم أفضل من يحكم عليها.
321
+
322
+ - **كلمة ناقصة:** افتح [بلاغ “Suggest a word”](https://github.com/karam-fikre/iraqi-dialect-bad-words/issues/new?template=add-word.yml)، أو أضفها إلى [`src/words.json`](src/words.json) في طلب دمج (pull request).
323
+ - **كلمة سليمة تُعلَّم خطأً:** افتح [بلاغ “Clean word flagged”](https://github.com/karam-fikre/iraqi-dialect-bad-words/issues/new?template=false-positive.yml).
324
+ إذا كانت تُعلَّم بسبب سابقة فقط (مثل `بعير` = `ب` + كلمة بذيئة)، فالحل إضافتها إلى [`src/safe-stems.json`](src/safe-stems.json).
325
+ الجذر الآمن المكتوب بـ گ يحمي الكلمات المكتوبة بـ گ فقط، فيمكن أن تكون `گلب` (قلب بالإملاء العراقي) آمنة بينما `كلب` ليست كذلك.
326
+
327
+ - **جملة يُحكم عليها خطأً:** أضفها إلى [`test/fixtures/sentences.json`](test/fixtures/sentences.json) مع الحكم الذي يتوقعه ابن اللهجة (`"profane": true` أو `false`، مع `"options"` إذا احتاجت `includeInsults` أو `includePhrases` أو `stripSuffixes`). كل جملة هناك تعمل اختبارًا، فلا يمكن لتغييرات القائمة لاحقًا أن تقلب حكمها بصمت.
328
+ - **نص سليم يُعلَّم:** أضفه إلى [`test/fixtures/clean-text.txt`](test/fixtures/clean-text.txt)، الذي يجب ألا ينتج أي تطابق أبدًا (مع `stripSuffixes` و `includePhrases` ولهجة الموصل أو من دونها)، ثم أصلح القوائم حتى تنجح الاختبارات من جديد.
329
+
330
+ في [`src/words.json`](src/words.json)، الكلمات الجنسية والصريحة توضع تحت `"severe"` وباقي الكلمات البذيئة تحت `"strong"`. والكلمات التي لها أيضًا معنى عادي شائع (مثل كلب) مكانها [`src/insults.json`](src/insults.json)، وهي اختيارية وكلها `'mild'`، وليس `words.json`. والعبارات المكونة من كلمات سليمة تصبح بذيئة معًا مكانها [`src/phrases.json`](src/phrases.json)، وهي اختيارية أيضًا.
331
+
332
+ ### التطوير
333
+
334
+ ```bash
335
+ npm install
336
+ npm test # تشغيل الاختبارات، ومنها فحوص قوائم الكلمات وفحص النص السليم
337
+ npm run typecheck # فحص الأنواع
338
+ npm run build # بناء dist/
339
+ npm run size # التأكد من أن الملفات المبنية ضمن حدود حجمها
340
+ npm run smoke # تحميل الحزمة المبنية بـ Node فقط، كما يفعل CI على Node 14-18
341
+ npm run check -- article.txt --phrases # سرد كل تطابق في ملف نصي
342
+ ```
343
+
344
+ يشغّل `npm run check` الفلتر على ملف نصي (أو على stdin، مثل `pbpaste | npm run check`) ويطبع كل تطابق مع سطره وعموده، والمدخل الذي طابقه، ومصدره وقواعده، والنص المحيط به. الخيارات تفعّل الإعدادات: `--insults` و `--phrases` و `--suffixes` و `--mosul`، و `--clean` لطباعة النص بعد الإخفاء أيضًا. تشغيله على مقالات تعرف أنها سليمة هو أسرع طريقة لإيجاد التعليم الخاطئ: كل تطابق إما جذر آمن ناقص، أو كلمة مكانها قائمة اختيارية.
345
+
346
+ يفحص `npm test` القوائم نفسها أيضًا: لا يجوز لمدخل أن يتوحّد إلى الكلمة نفسها كمدخل آخر (`گحبة` و `قحبة`، `qahba` و `gahba`)، ولا أن يكون مدخلًا يلتقطه الفلتر من دونه (`الكس` هي `ال` + `كس`)، وكل جذر آمن يجب أن يلغي تطابقًا. عندما يفشل مدخل جديد، تذكر الرسالة المدخل الذي يكرّره والقواعد التي تلتقطه أصلًا.
347
+
348
+ هذا الملف ترجمة لـ [README.md](README.md). عند تغيير أحدهما، غيّر الآخر أيضًا.
349
+
350
+ ## الترخيص
351
+
352
+ [MIT](LICENSE)