iraqi-dialect-bad-words 1.2.0 → 1.2.1
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.md +4 -1
- package/dist/index.d.mts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +48 -21
- package/dist/index.mjs +48 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,10 +131,13 @@ Matching is done per whole word, after normalizing both the text and the word li
|
|
|
131
131
|
| Stretched letters | `عاااارة` |
|
|
132
132
|
| Tatweel | `عـــارة` |
|
|
133
133
|
| ة / ه, ى / ي, أ / إ / آ / ا | `عاره` |
|
|
134
|
+
| Persian/Kurdish keyboard letters ی ک ە | `عیر`, `کس` |
|
|
135
|
+
| Invisible characters hidden inside a word (zero-width, direction marks, soft hyphen) | `عارة` |
|
|
134
136
|
| Attached prefixes و ف ب ل ك ال | `والعارة`, `للعارة` |
|
|
135
137
|
| Pronoun suffixes (with `stripSuffixes`) | `طيزهم`, `وطيزكم` |
|
|
136
138
|
| Punctuation next to the word | `عارة!` `عارة،` |
|
|
137
|
-
|
|
|
139
|
+
| Digits glued to or inside an Arabic word | `عارة3`, `عا٣رة` |
|
|
140
|
+
| Arabizi (Latin letters and numbers, Western or Arabic-Indic), including spelling variants | `ga7ba` = `ga٧ba` = `qahba` = `gahba`, `kosomak` = `kusumak`, `3ayri` = `ayri` |
|
|
138
141
|
|
|
139
142
|
Because matching is whole-word, a clean word that merely contains a bad word is not flagged.
|
|
140
143
|
|
package/dist/index.d.mts
CHANGED
|
@@ -56,8 +56,10 @@ declare class IraqiBadWordsFilter {
|
|
|
56
56
|
constructor(options?: string[] | IraqiBadWordsFilterOptions);
|
|
57
57
|
/**
|
|
58
58
|
* Normalizes Arabic text so spelling variants compare equal:
|
|
59
|
-
* removes diacritics (Tashkeel) and
|
|
60
|
-
* forms
|
|
59
|
+
* removes diacritics (Tashkeel), tatweel and invisible characters, unifies
|
|
60
|
+
* Alef/Teh Marbuta/Alef Maksura forms and the Persian/Kurdish keyboard letters
|
|
61
|
+
* ی ک ە, turns Arabic-Indic digits into 0-9, and collapses stretched letters
|
|
62
|
+
* (عاااارة -> عارة).
|
|
61
63
|
*/
|
|
62
64
|
private normalizeArabic;
|
|
63
65
|
/**
|
|
@@ -80,8 +82,13 @@ declare class IraqiBadWordsFilter {
|
|
|
80
82
|
* A match cleared by a safe stem is returned with `suppressedBy` set.
|
|
81
83
|
*/
|
|
82
84
|
private matchWord;
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Finds every match in the text, including ones cleared by a safe stem.
|
|
87
|
+
* A token with invisible characters is matched whole first (عارة -> عارة);
|
|
88
|
+
* if that finds nothing profane, each visible piece is tried on its own, so an
|
|
89
|
+
* invisible character between two words can't hide a bad word either.
|
|
90
|
+
*/
|
|
91
|
+
private findMatches;
|
|
85
92
|
/**
|
|
86
93
|
* Checks if a given text contains profane words.
|
|
87
94
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -56,8 +56,10 @@ declare class IraqiBadWordsFilter {
|
|
|
56
56
|
constructor(options?: string[] | IraqiBadWordsFilterOptions);
|
|
57
57
|
/**
|
|
58
58
|
* Normalizes Arabic text so spelling variants compare equal:
|
|
59
|
-
* removes diacritics (Tashkeel) and
|
|
60
|
-
* forms
|
|
59
|
+
* removes diacritics (Tashkeel), tatweel and invisible characters, unifies
|
|
60
|
+
* Alef/Teh Marbuta/Alef Maksura forms and the Persian/Kurdish keyboard letters
|
|
61
|
+
* ی ک ە, turns Arabic-Indic digits into 0-9, and collapses stretched letters
|
|
62
|
+
* (عاااارة -> عارة).
|
|
61
63
|
*/
|
|
62
64
|
private normalizeArabic;
|
|
63
65
|
/**
|
|
@@ -80,8 +82,13 @@ declare class IraqiBadWordsFilter {
|
|
|
80
82
|
* A match cleared by a safe stem is returned with `suppressedBy` set.
|
|
81
83
|
*/
|
|
82
84
|
private matchWord;
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Finds every match in the text, including ones cleared by a safe stem.
|
|
87
|
+
* A token with invisible characters is matched whole first (عارة -> عارة);
|
|
88
|
+
* if that finds nothing profane, each visible piece is tried on its own, so an
|
|
89
|
+
* invisible character between two words can't hide a bad word either.
|
|
90
|
+
*/
|
|
91
|
+
private findMatches;
|
|
85
92
|
/**
|
|
86
93
|
* Checks if a given text contains profane words.
|
|
87
94
|
*/
|
package/dist/index.js
CHANGED
|
@@ -239,7 +239,14 @@ var safe_stems_default = [
|
|
|
239
239
|
];
|
|
240
240
|
|
|
241
241
|
// src/index.ts
|
|
242
|
-
var
|
|
242
|
+
var INVISIBLE = "\\u00AD\\u061C\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF";
|
|
243
|
+
var INVISIBLE_CHARS = new RegExp(`[${INVISIBLE}]`, "g");
|
|
244
|
+
var HAS_INVISIBLE = new RegExp(`[${INVISIBLE}]`);
|
|
245
|
+
var VISIBLE_RUN = new RegExp(`[^${INVISIBLE}]+`, "g");
|
|
246
|
+
var WORD_PATTERN = new RegExp(
|
|
247
|
+
`[A-Za-z0-9_\xC0-\u024F\u0600-\u060B\u060D-\u061A\u061C-\u061E\u0620-\u0669\u066E-\u06D3\u06D5-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFC${INVISIBLE}]+`,
|
|
248
|
+
"g"
|
|
249
|
+
);
|
|
243
250
|
var CONJUNCTIONS = ["", "\u0648", "\u0641"];
|
|
244
251
|
var PREPOSITIONS = ["", "\u0628", "\u0644", "\u0643"];
|
|
245
252
|
var ARTICLES = ["", "\u0627\u0644"];
|
|
@@ -256,11 +263,13 @@ var IraqiBadWordsFilter = class {
|
|
|
256
263
|
}
|
|
257
264
|
/**
|
|
258
265
|
* Normalizes Arabic text so spelling variants compare equal:
|
|
259
|
-
* removes diacritics (Tashkeel) and
|
|
260
|
-
* forms
|
|
266
|
+
* removes diacritics (Tashkeel), tatweel and invisible characters, unifies
|
|
267
|
+
* Alef/Teh Marbuta/Alef Maksura forms and the Persian/Kurdish keyboard letters
|
|
268
|
+
* ی ک ە, turns Arabic-Indic digits into 0-9, and collapses stretched letters
|
|
269
|
+
* (عاااارة -> عارة).
|
|
261
270
|
*/
|
|
262
271
|
normalizeArabic(text) {
|
|
263
|
-
return text.replace(/[
|
|
272
|
+
return text.replace(/[\u064B-\u065F\u0670\u0640]/g, "").replace(INVISIBLE_CHARS, "").replace(/[أإآٱ]/g, "\u0627").replace(/[ةەہۀ]/g, "\u0647").replace(/[ىی]/g, "\u064A").replace(/[کڪ]/g, "\u0643").replace(/[٠-٩]/g, (digit) => String(digit.charCodeAt(0) - 1632)).replace(/[۰-۹]/g, (digit) => String(digit.charCodeAt(0) - 1776)).replace(/(.)\1+/gu, "$1").toLowerCase();
|
|
264
273
|
}
|
|
265
274
|
/**
|
|
266
275
|
* Folds Arabizi (Arabic written in Latin letters and digits) so spelling
|
|
@@ -274,7 +283,8 @@ var IraqiBadWordsFilter = class {
|
|
|
274
283
|
}
|
|
275
284
|
/** Normalizes a word or token for comparison. */
|
|
276
285
|
normalize(text) {
|
|
277
|
-
|
|
286
|
+
const word = this.normalizeArabic(text);
|
|
287
|
+
return /[\u0600-\uFEFF]/.test(word) ? word.replace(/[0-9]/g, "").replace(/(.)\1+/gu, "$1") : this.foldArabizi(word);
|
|
278
288
|
}
|
|
279
289
|
/**
|
|
280
290
|
* Adds words from one list. A word already present keeps its first source.
|
|
@@ -333,20 +343,43 @@ var IraqiBadWordsFilter = class {
|
|
|
333
343
|
}
|
|
334
344
|
return result;
|
|
335
345
|
}
|
|
336
|
-
/**
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
346
|
+
/**
|
|
347
|
+
* Finds every match in the text, including ones cleared by a safe stem.
|
|
348
|
+
* A token with invisible characters is matched whole first (عارة -> عارة);
|
|
349
|
+
* if that finds nothing profane, each visible piece is tried on its own, so an
|
|
350
|
+
* invisible character between two words can't hide a bad word either.
|
|
351
|
+
*/
|
|
352
|
+
*findMatches(text) {
|
|
353
|
+
const pattern = new RegExp(WORD_PATTERN.source, "g");
|
|
354
|
+
let found;
|
|
355
|
+
while (found = pattern.exec(text)) {
|
|
356
|
+
const token = found[0];
|
|
357
|
+
const start = found.index;
|
|
358
|
+
const whole = this.matchWord(token);
|
|
359
|
+
const wholeMatch = whole && { token, start, end: start + token.length, ...whole };
|
|
360
|
+
if (whole && whole.suppressedBy === void 0 || !HAS_INVISIBLE.test(token)) {
|
|
361
|
+
if (wholeMatch) yield wholeMatch;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
const pieces = [];
|
|
365
|
+
const piecePattern = new RegExp(VISIBLE_RUN.source, "g");
|
|
366
|
+
let piece;
|
|
367
|
+
while (piece = piecePattern.exec(token)) {
|
|
368
|
+
const match = this.matchWord(piece[0]);
|
|
369
|
+
const pieceStart = start + piece.index;
|
|
370
|
+
if (match) pieces.push({ token: piece[0], start: pieceStart, end: pieceStart + piece[0].length, ...match });
|
|
371
|
+
}
|
|
372
|
+
if (pieces.length) yield* pieces;
|
|
373
|
+
else if (wholeMatch) yield wholeMatch;
|
|
374
|
+
}
|
|
340
375
|
}
|
|
341
376
|
/**
|
|
342
377
|
* Checks if a given text contains profane words.
|
|
343
378
|
*/
|
|
344
379
|
isProfane(text) {
|
|
345
380
|
if (!text) return false;
|
|
346
|
-
for (const
|
|
347
|
-
if (
|
|
348
|
-
return true;
|
|
349
|
-
}
|
|
381
|
+
for (const match of this.findMatches(text)) {
|
|
382
|
+
if (match.suppressedBy === void 0) return true;
|
|
350
383
|
}
|
|
351
384
|
return false;
|
|
352
385
|
}
|
|
@@ -357,14 +390,8 @@ var IraqiBadWordsFilter = class {
|
|
|
357
390
|
inspect(text, options = {}) {
|
|
358
391
|
if (!text) return [];
|
|
359
392
|
const matches = [];
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
while (found = pattern.exec(text)) {
|
|
363
|
-
const token = found[0];
|
|
364
|
-
const match = this.matchWord(token);
|
|
365
|
-
if (match && (match.suppressedBy === void 0 || options.includeSuppressed)) {
|
|
366
|
-
matches.push({ token, start: found.index, end: found.index + token.length, ...match });
|
|
367
|
-
}
|
|
393
|
+
for (const match of this.findMatches(text)) {
|
|
394
|
+
if (match.suppressedBy === void 0 || options.includeSuppressed) matches.push(match);
|
|
368
395
|
}
|
|
369
396
|
return matches;
|
|
370
397
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -212,7 +212,14 @@ var safe_stems_default = [
|
|
|
212
212
|
];
|
|
213
213
|
|
|
214
214
|
// src/index.ts
|
|
215
|
-
var
|
|
215
|
+
var INVISIBLE = "\\u00AD\\u061C\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF";
|
|
216
|
+
var INVISIBLE_CHARS = new RegExp(`[${INVISIBLE}]`, "g");
|
|
217
|
+
var HAS_INVISIBLE = new RegExp(`[${INVISIBLE}]`);
|
|
218
|
+
var VISIBLE_RUN = new RegExp(`[^${INVISIBLE}]+`, "g");
|
|
219
|
+
var WORD_PATTERN = new RegExp(
|
|
220
|
+
`[A-Za-z0-9_\xC0-\u024F\u0600-\u060B\u060D-\u061A\u061C-\u061E\u0620-\u0669\u066E-\u06D3\u06D5-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFC${INVISIBLE}]+`,
|
|
221
|
+
"g"
|
|
222
|
+
);
|
|
216
223
|
var CONJUNCTIONS = ["", "\u0648", "\u0641"];
|
|
217
224
|
var PREPOSITIONS = ["", "\u0628", "\u0644", "\u0643"];
|
|
218
225
|
var ARTICLES = ["", "\u0627\u0644"];
|
|
@@ -229,11 +236,13 @@ var IraqiBadWordsFilter = class {
|
|
|
229
236
|
}
|
|
230
237
|
/**
|
|
231
238
|
* Normalizes Arabic text so spelling variants compare equal:
|
|
232
|
-
* removes diacritics (Tashkeel) and
|
|
233
|
-
* forms
|
|
239
|
+
* removes diacritics (Tashkeel), tatweel and invisible characters, unifies
|
|
240
|
+
* Alef/Teh Marbuta/Alef Maksura forms and the Persian/Kurdish keyboard letters
|
|
241
|
+
* ی ک ە, turns Arabic-Indic digits into 0-9, and collapses stretched letters
|
|
242
|
+
* (عاااارة -> عارة).
|
|
234
243
|
*/
|
|
235
244
|
normalizeArabic(text) {
|
|
236
|
-
return text.replace(/[
|
|
245
|
+
return text.replace(/[\u064B-\u065F\u0670\u0640]/g, "").replace(INVISIBLE_CHARS, "").replace(/[أإآٱ]/g, "\u0627").replace(/[ةەہۀ]/g, "\u0647").replace(/[ىی]/g, "\u064A").replace(/[کڪ]/g, "\u0643").replace(/[٠-٩]/g, (digit) => String(digit.charCodeAt(0) - 1632)).replace(/[۰-۹]/g, (digit) => String(digit.charCodeAt(0) - 1776)).replace(/(.)\1+/gu, "$1").toLowerCase();
|
|
237
246
|
}
|
|
238
247
|
/**
|
|
239
248
|
* Folds Arabizi (Arabic written in Latin letters and digits) so spelling
|
|
@@ -247,7 +256,8 @@ var IraqiBadWordsFilter = class {
|
|
|
247
256
|
}
|
|
248
257
|
/** Normalizes a word or token for comparison. */
|
|
249
258
|
normalize(text) {
|
|
250
|
-
|
|
259
|
+
const word = this.normalizeArabic(text);
|
|
260
|
+
return /[\u0600-\uFEFF]/.test(word) ? word.replace(/[0-9]/g, "").replace(/(.)\1+/gu, "$1") : this.foldArabizi(word);
|
|
251
261
|
}
|
|
252
262
|
/**
|
|
253
263
|
* Adds words from one list. A word already present keeps its first source.
|
|
@@ -306,20 +316,43 @@ var IraqiBadWordsFilter = class {
|
|
|
306
316
|
}
|
|
307
317
|
return result;
|
|
308
318
|
}
|
|
309
|
-
/**
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
319
|
+
/**
|
|
320
|
+
* Finds every match in the text, including ones cleared by a safe stem.
|
|
321
|
+
* A token with invisible characters is matched whole first (عارة -> عارة);
|
|
322
|
+
* if that finds nothing profane, each visible piece is tried on its own, so an
|
|
323
|
+
* invisible character between two words can't hide a bad word either.
|
|
324
|
+
*/
|
|
325
|
+
*findMatches(text) {
|
|
326
|
+
const pattern = new RegExp(WORD_PATTERN.source, "g");
|
|
327
|
+
let found;
|
|
328
|
+
while (found = pattern.exec(text)) {
|
|
329
|
+
const token = found[0];
|
|
330
|
+
const start = found.index;
|
|
331
|
+
const whole = this.matchWord(token);
|
|
332
|
+
const wholeMatch = whole && { token, start, end: start + token.length, ...whole };
|
|
333
|
+
if (whole && whole.suppressedBy === void 0 || !HAS_INVISIBLE.test(token)) {
|
|
334
|
+
if (wholeMatch) yield wholeMatch;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const pieces = [];
|
|
338
|
+
const piecePattern = new RegExp(VISIBLE_RUN.source, "g");
|
|
339
|
+
let piece;
|
|
340
|
+
while (piece = piecePattern.exec(token)) {
|
|
341
|
+
const match = this.matchWord(piece[0]);
|
|
342
|
+
const pieceStart = start + piece.index;
|
|
343
|
+
if (match) pieces.push({ token: piece[0], start: pieceStart, end: pieceStart + piece[0].length, ...match });
|
|
344
|
+
}
|
|
345
|
+
if (pieces.length) yield* pieces;
|
|
346
|
+
else if (wholeMatch) yield wholeMatch;
|
|
347
|
+
}
|
|
313
348
|
}
|
|
314
349
|
/**
|
|
315
350
|
* Checks if a given text contains profane words.
|
|
316
351
|
*/
|
|
317
352
|
isProfane(text) {
|
|
318
353
|
if (!text) return false;
|
|
319
|
-
for (const
|
|
320
|
-
if (
|
|
321
|
-
return true;
|
|
322
|
-
}
|
|
354
|
+
for (const match of this.findMatches(text)) {
|
|
355
|
+
if (match.suppressedBy === void 0) return true;
|
|
323
356
|
}
|
|
324
357
|
return false;
|
|
325
358
|
}
|
|
@@ -330,14 +363,8 @@ var IraqiBadWordsFilter = class {
|
|
|
330
363
|
inspect(text, options = {}) {
|
|
331
364
|
if (!text) return [];
|
|
332
365
|
const matches = [];
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
while (found = pattern.exec(text)) {
|
|
336
|
-
const token = found[0];
|
|
337
|
-
const match = this.matchWord(token);
|
|
338
|
-
if (match && (match.suppressedBy === void 0 || options.includeSuppressed)) {
|
|
339
|
-
matches.push({ token, start: found.index, end: found.index + token.length, ...match });
|
|
340
|
-
}
|
|
366
|
+
for (const match of this.findMatches(text)) {
|
|
367
|
+
if (match.suppressedBy === void 0 || options.includeSuppressed) matches.push(match);
|
|
341
368
|
}
|
|
342
369
|
return matches;
|
|
343
370
|
}
|
package/package.json
CHANGED