iraqi-dialect-bad-words 1.4.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 +352 -0
- package/README.md +77 -16
- package/dist/chunk-B6UOPPCT.mjs +270 -0
- package/dist/index.d.mts +63 -15
- package/dist/index.d.ts +63 -15
- package/dist/index.js +323 -235
- package/dist/index.mjs +122 -291
- package/dist/lists.d.mts +19 -0
- package/dist/lists.d.ts +19 -0
- package/dist/lists.js +312 -0
- package/dist/lists.mjs +25 -0
- package/lists/package.json +6 -0
- package/package.json +17 -4
package/README.ar.md
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# iraqi-dialect-bad-words
|
|
2
|
+
|
|
3
|
+
[English](README.md) | **العربية**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/iraqi-dialect-bad-words)
|
|
6
|
+
[](https://github.com/karam-fikre/iraqi-dialect-bad-words/actions/workflows/ci.yml)
|
|
7
|
+
[](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)
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# iraqi-dialect-bad-words
|
|
2
2
|
|
|
3
|
+
**English** | [العربية](README.ar.md)
|
|
4
|
+
|
|
3
5
|
[](https://www.npmjs.com/package/iraqi-dialect-bad-words)
|
|
4
6
|
[](https://github.com/karam-fikre/iraqi-dialect-bad-words/actions/workflows/ci.yml)
|
|
5
7
|
[](LICENSE)
|
|
@@ -99,26 +101,29 @@ new IraqiBadWordsFilter({ includePhrases: true }); // also vulgar phrases
|
|
|
99
101
|
new IraqiBadWordsFilter({ stripSuffixes: true }); // also unlisted suffix forms
|
|
100
102
|
new IraqiBadWordsFilter({ dialects: ['mosul'] }); // also Mosul ر → غ forms
|
|
101
103
|
new IraqiBadWordsFilter({ customWords: ['word1', 'word2'] });
|
|
104
|
+
new IraqiBadWordsFilter({ customWords: [{ word: 'word1', severity: 'mild' }] });
|
|
105
|
+
new IraqiBadWordsFilter({ useDefaultList: false, customWords: ['word1'] }); // your words only
|
|
102
106
|
new IraqiBadWordsFilter({ safeWords: ['word3'] }); // clean words not to flag
|
|
103
107
|
new IraqiBadWordsFilter(['word1', 'word2']); // same as customWords
|
|
104
108
|
```
|
|
105
109
|
|
|
106
110
|
| Option | Default | Description |
|
|
107
111
|
|---|---|---|
|
|
108
|
-
| `customWords` | `[]` | Extra words to filter, on top of the built-in list. Can come from your own JSON file (see below). |
|
|
112
|
+
| `customWords` | `[]` | Extra words to filter, on top of the built-in list. Can come from your own JSON file (see below). A word is `'strong'` unless given as `{ word, severity }` (see [severity](#severity)). |
|
|
113
|
+
| `useDefaultList` | `true` | `false` leaves out the built-in [`words.json`](src/words.json), so only your own words are filtered. `includeInsults` and `includePhrases` still add their lists, and the built-in safe stems stay. |
|
|
109
114
|
| `safeWords` | `[]` | Extra safe stems, on top of the built-in [`safe-stems.json`](src/safe-stems.json): clean words that would otherwise match once a prefix or suffix is stripped, like the built-in `بعير` ("camel" = `ب` + `عير`). Use it for clean words your app sees flagged, especially ones your own `customWords` cause. Their prefixed and suffixed forms are safe too (`وبعيرك`). Can come from your own JSON file (see below). |
|
|
110
115
|
| `includeInsults` | `false` | Also filter milder insults like كلب, حمار, قندرة, نعال. Off by default because many of them are everyday words, so turning it on flags sentences like `عندي كلب` ("I have a dog"). |
|
|
111
116
|
| `includePhrases` | `false` | Also filter the phrases in [`phrases.json`](src/phrases.json): clean words that are vulgar together, like `تعال العب بيه`. Off by default because the same words can be meant literally ("come play with it"). |
|
|
112
|
-
| `customPhrases` | `[]` | Extra phrases to filter, whether or not `includePhrases` is on. Can come from your own JSON file. |
|
|
117
|
+
| `customPhrases` | `[]` | Extra phrases to filter, whether or not `includePhrases` is on. Can come from your own JSON file. A phrase is `'strong'` unless given as `{ phrase, severity }`. |
|
|
113
118
|
| `stripSuffixes` | `false` | Also match words with a pronoun suffix (ي ك ج ه ها نا كم هم) that the list doesn't spell out, e.g. `طيزهم` → `طيز`. Off by default because it widens matching: a clean word that ends like a bad word plus a suffix gets flagged too. |
|
|
114
119
|
| `dialects` | `[]` | Regional pronunciations to also match. `'mosul'`: ر said as غ, so every entry with ر also matches its غ form (`شرموطة` → `شغموطة`); the ر forms keep matching. Safe stems get their غ form too. |
|
|
115
120
|
|
|
116
121
|
#### Using your own word files
|
|
117
122
|
|
|
118
|
-
Keep your app's extra words, and clean words you don't want flagged, in JSON files: arrays of single words, the same format as [`src/
|
|
123
|
+
Keep your app's extra words, and clean words you don't want flagged, in JSON files: arrays of single words, the same format as [`src/safe-stems.json`](src/safe-stems.json). A word that needs its own [severity](#severity) can be an object instead:
|
|
119
124
|
|
|
120
125
|
```json
|
|
121
|
-
["زفت", "نذل"]
|
|
126
|
+
["زفت", { "word": "نذل", "severity": "mild" }]
|
|
122
127
|
```
|
|
123
128
|
|
|
124
129
|
Import them and pass them as `customWords` and `safeWords`. The built-in lists stay on, and your words are added to them:
|
|
@@ -146,14 +151,26 @@ Returns `true` if any word in the text is on the list.
|
|
|
146
151
|
|
|
147
152
|
Replaces each bad word with the placeholder, repeated to the word's length. Spaces and punctuation are kept.
|
|
148
153
|
|
|
154
|
+
The placeholder can also be a function: it gets each match from [`inspect()`](#inspecttext-string-options-profanitymatch) and returns the text to put in its place. For example, keep the first letter, or mask only the stronger words:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
filter.clean('انت عارة', (match) => {
|
|
158
|
+
const [first, ...rest] = Array.from(match.token); // Array.from counts 𝐠 or an emoji as one letter
|
|
159
|
+
return first + '*'.repeat(rest.length);
|
|
160
|
+
});
|
|
161
|
+
// 'انت ع***'
|
|
162
|
+
|
|
163
|
+
filter.clean(text, (match) => (match.severity === 'mild' ? match.token : '***'));
|
|
164
|
+
```
|
|
165
|
+
|
|
149
166
|
### `inspect(text: string, options?): ProfanityMatch[]`
|
|
150
167
|
|
|
151
|
-
Lists each bad word in the text and explains why it matched. Use it to highlight words, to treat
|
|
168
|
+
Lists each bad word in the text and explains why it matched. Use it to highlight words, to treat words differently by severity (e.g. block `'severe'` but only warn on `'mild'`), or to let someone review a match before your app blocks it.
|
|
152
169
|
|
|
153
170
|
```ts
|
|
154
171
|
filter.inspect('روح للكواد!');
|
|
155
172
|
// [{ token: 'للكواد', start: 4, end: 10, normalized: 'لكواد', entry: 'كواد',
|
|
156
|
-
// source: 'words', prefix: 'ل', suffix: '', rules: ['stretched', 'prefix'] }]
|
|
173
|
+
// source: 'words', severity: 'strong', prefix: 'ل', suffix: '', rules: ['stretched', 'prefix'] }]
|
|
157
174
|
```
|
|
158
175
|
|
|
159
176
|
| Field | Description |
|
|
@@ -163,6 +180,7 @@ filter.inspect('روح للكواد!');
|
|
|
163
180
|
| `normalized` | The word after normalization (Arabizi is folded too, so `Qa7ba` shows as `gahba`). For a word with گ, the reading that matched (ق or ك). |
|
|
164
181
|
| `entry` | The list entry it matched, spelled as in the list. For a Mosul form, the listed ر spelling. |
|
|
165
182
|
| `source` | `'words'`, `'insults'`, `'phrases'` or `'custom'`. A word on more than one list keeps the first: built-in, then insults, then custom. |
|
|
183
|
+
| `severity` | `'mild'`, `'strong'` or `'severe'` (see below). |
|
|
166
184
|
| `prefix` | The attached prefix stripped before matching (after normalization, so `لل` shows as `ل`), or `''` for a direct match. |
|
|
167
185
|
| `suffix` | The suffix stripped before matching (only with `stripSuffixes`), or `''`. |
|
|
168
186
|
| `rules` | The rules the word needed to match the entry (see below). Empty when it's spelled exactly like the entry. |
|
|
@@ -183,7 +201,8 @@ filter.inspect('وبعيرك', { includeSuppressed: true });
|
|
|
183
201
|
| `diacritics` | `عَارَة`, `عـــارة` |
|
|
184
202
|
| `invisible` | `عارة` (zero-width character inside) |
|
|
185
203
|
| `spelling` | `عاره` (ة/ه, ى/ي, أ/ا) |
|
|
186
|
-
| `keyboard` | `عیر`,
|
|
204
|
+
| `keyboard` | `عیر`, `کس`, `پورنو` (Persian/Kurdish letters) |
|
|
205
|
+
| `che-as-kaf` | `چلب` → كلب |
|
|
187
206
|
| `lookalike` | `gаhba` (Cyrillic `а` in a Latin word) |
|
|
188
207
|
| `arabic-digits` | `ga٧ba` |
|
|
189
208
|
| `stretched` | `عاااارة`, `للكواد` |
|
|
@@ -194,23 +213,42 @@ filter.inspect('وبعيرك', { includeSuppressed: true });
|
|
|
194
213
|
| `mosul` | `شغموطة` → شرموطة (with `dialects: ['mosul']`) |
|
|
195
214
|
| `phrase` | `تعال العب بيه` (with `includePhrases`) |
|
|
196
215
|
| `split` | `ع*ي*ر`, `ع ي ر` (pieces joined again) |
|
|
216
|
+
| `repeated` | `عيرعير`, `gahbagahba` (an entry typed several times with no space) |
|
|
197
217
|
| `symbols` | `g@hba`, `$harmoota` (@ as a, $ as s) |
|
|
198
218
|
|
|
219
|
+
#### Severity
|
|
220
|
+
|
|
221
|
+
Every match has a `severity`, so an app can block some words and only warn on others:
|
|
222
|
+
|
|
223
|
+
| Severity | What | Examples |
|
|
224
|
+
|---|---|---|
|
|
225
|
+
| `'mild'` | Insults with an everyday meaning: everything in [`insults.json`](src/insults.json) | `كلب`, `حمار`, `قندرة` |
|
|
226
|
+
| `'strong'` | Profanity and insults with no clean meaning | `كواد`, `سرسري`, `يلعن ابوك` |
|
|
227
|
+
| `'severe'` | Sexual and explicit words | `كس`, `شرموطة`, `تعال العب بيه` |
|
|
228
|
+
|
|
229
|
+
Your own words and phrases are `'strong'` unless you give them a severity (`{ word: 'نذل', severity: 'mild' }`). A word already on a built-in list keeps that list's severity.
|
|
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
|
+
|
|
199
237
|
Including the output of `inspect` in a [“Clean word flagged” issue](https://github.com/karam-fikre/iraqi-dialect-bad-words/issues/new?template=false-positive.yml) shows exactly which entry matched and which rules it needed; the issue form has a field for it.
|
|
200
238
|
|
|
201
|
-
### `addWords(words: string[]): void`
|
|
239
|
+
### `addWords(words: (string | CustomWord)[]): void`
|
|
202
240
|
|
|
203
|
-
Adds words to this filter.
|
|
241
|
+
Adds words to this filter, like the `customWords` option.
|
|
204
242
|
|
|
205
|
-
### `addSafeWords(words: string[]): void`
|
|
243
|
+
### `addSafeWords(words: string[]): void` / `removeSafeWords(words: string[]): void`
|
|
206
244
|
|
|
207
|
-
Adds safe stems to this filter, like the `safeWords` option.
|
|
245
|
+
Adds safe stems to this filter, like the `safeWords` option, or removes them, including built-in ones (with their Mosul forms when the dialect is on).
|
|
208
246
|
|
|
209
247
|
### `removeWords(words: string[]): void`
|
|
210
248
|
|
|
211
249
|
Removes words from this filter, for example a built-in word you don't want flagged in your app.
|
|
212
250
|
|
|
213
|
-
### `addPhrases(phrases: string[]): void` / `removePhrases(phrases: string[]): void`
|
|
251
|
+
### `addPhrases(phrases: (string | CustomPhrase)[]): void` / `removePhrases(phrases: string[]): void`
|
|
214
252
|
|
|
215
253
|
Adds phrases to this filter, like the `customPhrases` option, or removes them, including built-in ones.
|
|
216
254
|
|
|
@@ -226,6 +264,20 @@ matches `قوم بيه`, `قوم بينو`, `كوم بيه` and `كوم بينو
|
|
|
226
264
|
|
|
227
265
|
The whole phrase is reported as one match with the rule `phrase`, its `entry` being the line as listed, and `clean()` masks all of it. `removePhrases()` takes a line as listed; the order of its alternatives doesn't matter.
|
|
228
266
|
|
|
267
|
+
### The word lists: `iraqi-dialect-bad-words/lists`
|
|
268
|
+
|
|
269
|
+
The built-in lists are also exported on their own, for tools that can't use the filter (a database query, a backend in another language):
|
|
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` and `phrases` are grouped by severity; `insults` are all `'mild'`. The entries are as listed, and they can't be changed. Matching them by plain string comparison catches far less than the filter: it doesn't normalize spelling, strip prefixes, fold Arabizi or add Mosul forms.
|
|
280
|
+
|
|
229
281
|
## What it catches
|
|
230
282
|
|
|
231
283
|
Matching is done per whole word, after normalizing both the text and the word list, so these all match the same entry:
|
|
@@ -236,14 +288,16 @@ Matching is done per whole word, after normalizing both the text and the word li
|
|
|
236
288
|
| Stretched letters | `عاااارة` |
|
|
237
289
|
| Tatweel | `عـــارة` |
|
|
238
290
|
| ة / ه, ى / ي, أ / إ / آ / ا | `عاره` |
|
|
239
|
-
| Persian/Kurdish keyboard letters ی ک ە | `عیر`,
|
|
291
|
+
| Persian/Kurdish keyboard letters ی ک ە پ ڤ | `عیر`, `کس`, `پورنو` |
|
|
240
292
|
| گ read as ق (Iraqi) and as ك (Kurdish typists) | `گحبة` = `قحبة`, `گس` = `كس` |
|
|
293
|
+
| چ read as ك, as Iraqi says ك in many words | `چلب` = `كلب` |
|
|
241
294
|
| Invisible characters hidden inside a word (zero-width, direction marks, soft hyphen) | `عارة` |
|
|
242
295
|
| Letters split by symbols, or by spaces between single letters | `ع*ي*ر`, `ع.ي.ر`, `ع ي ر`, `عي * ر`, `شرم.وطة`, `g-a-h-b-a` |
|
|
243
296
|
| Arabic presentation forms, full-width and mathematical letters | `ﻋﻴﺮ`, `gahba`, `𝐠𝐚𝐡𝐛𝐚` |
|
|
244
297
|
| Greek or Cyrillic look-alike letters in a Latin word | `gаhba` (Cyrillic `а`) |
|
|
245
298
|
| Attached prefixes و ف ب ل ك ال, and يا / ويا | `والعارة`, `للعارة`, `ياعارة`, `يالعارة`, `وياعارة` |
|
|
246
299
|
| Pronoun suffixes (with `stripSuffixes`) | `طيزهم`, `وطيزكم` |
|
|
300
|
+
| The same word typed several times with no space | `عيرعير`, `gahbagahba` |
|
|
247
301
|
| Punctuation next to the word | `عارة!` `عارة،` |
|
|
248
302
|
| Digits glued to or inside an Arabic word | `عارة3`, `عا٣رة` |
|
|
249
303
|
| Arabizi (Latin letters and numbers, Western or Arabic-Indic), including spelling variants | `ga7ba` = `ga٧ba` = `qahba` = `gahba`, `kosomak` = `kusumak`, `3ayri` = `ayri`, `6eezak` = `teezak` (6 = ط), `9awad` = `8awad` = `gawad` (9, 8 = ق) |
|
|
@@ -256,7 +310,7 @@ Split words are found by joining neighbouring pieces when the gap between them i
|
|
|
256
310
|
## Known limitations
|
|
257
311
|
|
|
258
312
|
- Phrases match only as listed, word for word: another word in between (`تعال هنا العب بيه`) or an unlisted form (`بيهم`) isn't matched.
|
|
259
|
-
-
|
|
313
|
+
- Different words glued together without a space aren't detected. The same word typed several times (`عيرعير`) is.
|
|
260
314
|
- A letter replaced by a symbol (`ع*ر`, `ك*بة`) isn't detected: the symbol could stand for any letter, and `ع*ر` could just as well be عمر.
|
|
261
315
|
- Longer pieces split with spaces around the symbol (`شرم * وطة`) aren't joined, so normal sentences aren't either.
|
|
262
316
|
- No word list is complete. Use this as one layer of moderation, not the only one.
|
|
@@ -271,21 +325,28 @@ The word list is the heart of this package, and native speakers are the best jud
|
|
|
271
325
|
A safe stem written with گ protects only words spelled with گ, so `گلب` ("heart", Iraqi spelling of قلب) can be safe while `كلب` is not.
|
|
272
326
|
|
|
273
327
|
- **A sentence is judged wrong:** add it to [`test/fixtures/sentences.json`](test/fixtures/sentences.json) with the verdict a native speaker expects (`"profane": true` or `false`, plus `"options"` if it needs `includeInsults`, `includePhrases` or `stripSuffixes`). Every sentence there runs as a test, so later word-list changes can't silently flip it.
|
|
328
|
+
- **Clean text gets flagged:** add it to [`test/fixtures/clean-text.txt`](test/fixtures/clean-text.txt), which must never produce a match (with or without `stripSuffixes`, `includePhrases` and the Mosul dialect), then fix the lists until the tests pass again.
|
|
274
329
|
|
|
275
|
-
Words that also have a common, normal meaning (like كلب "dog") belong in [`src/insults.json`](src/insults.json), which is opt-in
|
|
330
|
+
In [`src/words.json`](src/words.json), sexual and explicit words go under `"severe"` and other profanity under `"strong"`. Words that also have a common, normal meaning (like كلب "dog") belong in [`src/insults.json`](src/insults.json), which is opt-in and all `'mild'`, not in `words.json`. Phrases of clean words that are vulgar together belong in [`src/phrases.json`](src/phrases.json), also opt-in.
|
|
276
331
|
|
|
277
332
|
### Development
|
|
278
333
|
|
|
279
334
|
```bash
|
|
280
335
|
npm install
|
|
281
|
-
npm test # run tests
|
|
336
|
+
npm test # run tests, including the word-list checks and the clean-text check
|
|
282
337
|
npm run typecheck # type-check
|
|
283
338
|
npm run build # build dist/
|
|
339
|
+
npm run size # check the built files are within their size budgets
|
|
340
|
+
npm run smoke # load the built package with plain Node, as CI does on Node 14-18
|
|
284
341
|
npm run check -- article.txt --phrases # list every match in a text file
|
|
285
342
|
```
|
|
286
343
|
|
|
287
344
|
`npm run check` runs the filter over a text file (or stdin, e.g. `pbpaste | npm run check`) and prints each match with its line and column, the entry it matched, its source and rules, and the text around it. Flags turn on options: `--insults`, `--phrases`, `--suffixes`, `--mosul`, and `--clean` to also print the masked text. Running it on articles you know are clean is the quickest way to find false positives: each match is either a missing safe stem or a word that belongs in an opt-in list.
|
|
288
345
|
|
|
346
|
+
`npm test` also checks the lists themselves: no entry may normalize to the same word as another (`گحبة` and `قحبة`, `qahba` and `gahba`), no entry may be one the filter already catches without it (`الكس` is `ال` + `كس`), and every safe stem must clear a match. When a new entry fails, the message names the entry it duplicates and the rules that already catch it.
|
|
347
|
+
|
|
348
|
+
[README.ar.md](README.ar.md) is an Arabic translation of this file. When you change one, change the other too.
|
|
349
|
+
|
|
289
350
|
## License
|
|
290
351
|
|
|
291
352
|
[MIT](LICENSE)
|