FAQ
Can it replace bad words with ****?
Yes. Use censor, or check(text).censor() if you also want to know what matched:
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:
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:
from no_nepali_profanity import create_filter
profanity_filter = create_filter({"extra_words": ["someword"], "allow_words": ["somename"]})
profanity_filter.contains_profanity("s0mew0rd") # TrueExtra 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:
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:
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.