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("terms and conditions", {"strictness": "strict"})   # ["conditions"]
  • 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 the stems rand, cond, kand, lund, which also hit words like Randip and conditions).

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.1.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.1.0
File Size Uploaded
no_nepali_profanity-0.1.0.tar.gz 17.5 kB Details

Built distribution (wheel)

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

Total release size: 30.5 kB

Release files / no_nepali_profanity-0.1.0.tar.gz

Download URL no_nepali_profanity-0.1.0.tar.gz
Size 17.5 kB
Tags Source
SHA-256 checksum
How to use checksums
dd97cabd084852b33a2673dfdaeb890c5e35c06b3a334c7f3cd0f10cf9003f72
BLAKE2b-256 checksum
How to use checksums
4b356c07689f21a83c3315773a7f8a78110726039d5ff5ae91bcdfeeffcdabd2
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.1.0-py3-none-any.whl

Download URL no_nepali_profanity-0.1.0-py3-none-any.whl
Size 13.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7bf7b499d6583749f7fbd1c8361ab67906b1683ea752076225a96a42a19d7a5e
BLAKE2b-256 checksum
How to use checksums
b612f00f87b96b1e30a3afe0428ca0adf24922cbb259c5b71e468be8b918650d
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.1.0 This release

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