Skip to main content

no-nepali-profanity

A small, dependency-free profanity matcher for English, Romanized (Latin) Nepali and Devanagari Nepali, plus the Hindi slang common in Nepal. Built for moderating user-written text — names, comments, reviews — on Nepali sites, where false positives on real names are more damaging than a missed swear.

Zero runtime dependencies. Python ≥ 3.9. A direct port of the no-nepali-profanity npm package, with the same word lists and matching rules.

Documentation: mukhxadnahunna.com/python

Install

pip install no-nepali-profanity

Usage

from no_nepali_profanity import contains_profanity, find_profanity, tokenize

# boolean check — fastest
contains_profanity("Great teacher!")        # False
contains_profanity("muji")                  # True
contains_profanity("मुजीको कक्षा")           # True  (Devanagari + postposition)

# which words — returns normalised matches as they appeared
find_profanity("f.u.c.k this sh1t")        # ["fuck", "shit"]
find_profanity("f u c k this")              # ["fuck"]
find_profanity("Randip Thapa")              # []
find_profanity("*ss teacher")               # ["*ss"]

# debugging — see what the matcher actually splits into
tokenize("Great teacher!")                  # ["great", "teacher"]

API

Function Returns Notes
contains_profanity(text, options?) bool Yes/no from find_profanity.
find_profanity(text, options?) list[str] Matching words normalised + lowercased (leet decoded, case-folded), deduplicated. Empty when clean.
check(text, options?) ProfanityCheck Scans once; inspect the result and censor it without scanning again.
find_profanity_matches(text, options?) list[ProfanityMatch] Every occurrence with its position in the original text, sorted by position.
censor(text, options?) str The text with each match masked: "you muji" → "you ****". Options: mask (default "*"), replace(match).
create_filter(options?) ProfanityFilter Builds the tables once for fixed options.
tokenize(text) list[str] Raw tokens the matcher sees. Useful for debugging why a word is (or isn't) caught.
lexicon module Tagged entries (WORDS, STEMS, PHRASES), flat per-script lists (LATIN_WORDS, DEVANAGARI_WORDS…) and suffixes.

Match positions (ProfanityMatch.start, end) are character (code point) indices, unlike the npm package which uses UTF-16 code units.

Censoring

censor("you muji")                                   # "you ****"
censor("F.U.C.K this Sh1t!")                          # "******* this ****!"
censor("you muji", {"mask": "#"})                     # "you ####"
censor("you muji", {"replace": lambda m: "[censored]"})  # "you [censored]"
find_profanity_matches("you muji")                     # [ProfanityMatch(text="muji", normalized="muji", start=4, end=8)]

Check and censor in one pass:

result = check("you muji")
result.has_profanity   # True
result.words           # ["muji"]
result.censor()        # "you ****"

Options

find_profanity("fuck muji मुजी", {"languages": ["romanized"]})      # ["muji"]
contains_profanity("you idiot", {"strictness": "lenient"})         # False
find_profanity("damn it", {"strictness": "strict"})   # ["damn"]
  • languages: any of "english", "romanized", "devanagari". Default: all three.
  • strictness: "lenient" (severe words only), "standard" (default, adds milder insults like idiot, murkha) or "strict" (adds entries that are also ordinary words, like damn, and the stems rand, cond, kand, lund; names they would hit, like Randip, are on a built-in allow list).
  • extra_words: more words to flag. allow_words: words never to flag, such as names on your site.

What it catches

  • Case and Unicode forms: IDIOT, full-width letters.
  • Leetspeak: sh1t, @ss (0 1 3 4 5 7 @ $).
  • ! for i between letters: sh!t, b!tch. Sentence-final Great teacher! is left alone.
  • * for a hidden letter: f*ck, sh*t, and markdown emphasis like *sh*t* still reads as the word.
  • Stretched letters: fuuuuck, for words of 4+ letters.
  • Spelled-out letters: f.u.c.k, f u c k.
  • Nepali postpositions and plurals glued on: mujiko, randiharu, मुजीको, …हरू.
  • Devanagari spelling variants: nukta, chandrabindu vs anusvara, zero-width joiners.
  • Stems where no ordinary word starts the same way: fucking, bitches, machiknee.
  • Multi-word phrases: chaak ko pwal, pesa garne, sasto manche (Latin and Devanagari).

What it deliberately doesn't

  • Short words match exactly, so as, class, assignment and Assam are fine.
  • Name collisions: shit is a whole word only, because Shitij / शितिज is a name. Names like मुजी-adjacent Randip / राण्डीप, Putali / पुतली, Asha / आशा are checked in the test suite.
  • No caste names, surnames or ordinary words that are only offensive in context (e.g. kami, kukur). A word list can't tell a slur from someone's name; that needs human moderation.
  • No judgement of context, sarcasm or meaning. This is a first-pass filter, not a moderator.

Development

python -m venv .venv && .venv/bin/pip install -e ".[test]"
.venv/bin/pytest

The word lists live in src/no_nepali_profanity/lexicon.py, apart from the matching logic in src/no_nepali_profanity/core.py. Native-speaker review of the Nepali lists is the most valuable contribution.

License

MIT

Release files for no-nepali-profanity 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for no-nepali-profanity 0.2.0
File Size Uploaded
no_nepali_profanity-0.2.0.tar.gz 21.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for no-nepali-profanity 0.2.0
File Interpreter ABI Platform
no_nepali_profanity-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 36.7 kB

Release files / no_nepali_profanity-0.2.0.tar.gz

Download URL no_nepali_profanity-0.2.0.tar.gz
Size 21.1 kB
Tags Source
SHA-256 checksum
How to use checksums
1f6a5dfd2828a93682bafe1b9c47026ecbdc2772b72c67316bfe15157f31036d
BLAKE2b-256 checksum
How to use checksums
34c8cf5ade04ac116e967a03f8f603d321a870eac270a8de3a99f75679c83a64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / no_nepali_profanity-0.2.0-py3-none-any.whl

Download URL no_nepali_profanity-0.2.0-py3-none-any.whl
Size 15.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5bccaf4548d57b38aad4e26b258a37249d2c3032d2514c687a8503fb7900d0dd
BLAKE2b-256 checksum
How to use checksums
247e5e4628d50aa1d1f803528da2dc1bc918364d9680a661c16afc8b006dad15
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page