Skip to content

FAQ ​

Can it replace bad words with ****? ​

Yes. Use censor, or check(text).censor() if you also want to know what matched:

py
censor("you muji")             # "you ****"
check("you muji").censor()     # "you ****"

See Censoring.

Can I highlight matches instead of hiding them? ​

Yes. Pass a replace function to censor, or use the positions from find_profanity_matches:

py
censor("you muji", {"replace": lambda m: f"<mark>{m.text}</mark>"})   # "you <mark>muji</mark>"

Escape the rest of the text first if it goes into HTML. See Highlight matches for moderators.

Can I add my own words? ​

Yes. Pass extra_words to flag more words, and allow_words to never flag a word, such as a name on your site:

py
from no_nepali_profanity import create_filter

profanity_filter = create_filter({"extra_words": ["someword"], "allow_words": ["somename"]})

profanity_filter.contains_profanity("s0mew0rd")   # True

Extra words are matched like the built-in ones, so leetspeak, stretched letters and postpositions are still caught. To add words for everyone, see Contributing.

Why was this word flagged? ​

Run the text through find_profanity to see which word matched, and tokenize to see the words the filter checked:

py
from no_nepali_profanity import find_profanity, tokenize

find_profanity("damn it", {"strictness": "strict"})   # ["damn"]
tokenize("damn it")                                   # ["damn", "it"]

If an ordinary word or a name is flagged at "standard" or "lenient", please report it.

Why wasn't this word caught? ​

It's probably not in the word lists, or it's in a stricter level than the one you use. Check the lexicon:

py
from no_nepali_profanity import lexicon

next(e for e in lexicon.WORDS if e.text == "idiot")
# LexiconEntry(text="idiot", language="english", strictness="standard")

If it isn't there, suggest it.

Which strictness should I use? ​

Use the default, "standard", for most sites. Use "lenient" if mild insults are fine in your community. Use "strict" only when a person reviews what gets flagged, because it catches a few ordinary words, like damn. See Block severe words, review the rest.

Is it fast? ​

Yes, for typical user text like comments, reviews and names. A filter builds its lookup tables once, and each check is a single pass over the words in the text. To both check and censor, use check(text), which reuses one scan for both.

Is it thread-safe? ​

Yes. A filter never changes after it's built, so one filter, or the top-level functions, can be used from many threads at once.

Does it give the same results as the JavaScript package? ​

Yes. Every port uses the same word lists and matching rules, and is tested against the JavaScript package's output. The one difference is how match positions are counted: Python indexes strings by code point, so start and end count code points, where JavaScript counts UTF-16 code units. Either way, slicing the input with them gives the matched text.