A Thai-aware profanity filter for Dart — detect, censor, and score Thai bad words. Pure Dart, dependency-free, works everywhere (mobile, web, desktop, server, CLI).
Thai has no spaces between words, so a naive "does the string contain a bad
word" check both misses evasions (ค ว ย, เหี้ยยยย) and flags innocent
words (หีบ "chest" contains หี). This package handles both: it normalizes
away common evasions and uses an allow-list so clean words that merely contain
a bad substring are not flagged.
- Detect / score / censor —
hasProfanity,isClean,severityOf,findMatches(with positions in the original string), andcensor. - Severity tiers —
mild(rude pronouns like กู/มึง),moderate(clearly vulgar),severe(sexual / strongly offensive). Filter by a minimum severity. - Evasion handling — collapses repeated characters (
เหี้ยยยย→เหี้ย), joins spaced-out letters (ค ว ย→ควย), and strips zero-width characters. - False-positive guard — a bundled allow-list of clean words that contain a
bad substring (e.g.
หีบ,ตระกูล,แกงกะหรี่) so they are never flagged. - Fully customizable — add your own words, unblock defaults, extend the allow-list, all per filter instance.
dependencies:
thai_profanity_filter: ^0.1.0import 'package:thai_profanity_filter/thai_profanity_filter.dart';
void main() {
final filter = ThaiProfanityFilter();
filter.hasProfanity('ไอ้ควย'); // true
filter.isClean('สวัสดีครับ'); // true
filter.hasProfanity('เปิดหีบสมบัติ'); // false (หีบ is allow-listed)
// Evasion is handled by default:
filter.hasProfanity('เหี้ยยยย'); // true
filter.hasProfanity('ค ว ย'); // true
// Censor, preserving the surrounding text:
filter.censor('เอ๊ย ไอ้ควย'); // 'เอ๊ย ไอ้***'
// Inspect what matched, and where (indices into the original string):
for (final m in filter.findMatches('มึงมันเหี้ย')) {
print('${m.word} [${m.start}..${m.end}] ${m.severity}');
}
}Severity is ordered mild < moderate < severe. By default the filter flags
everything from mild up, so the rude pronouns กู / มึง are caught. If you
only care about clearly-vulgar language and want the fewest false positives,
raise the threshold:
final strict = ThaiProfanityFilter(minSeverity: Severity.moderate);
strict.isClean('มึงกับกู'); // true — mild pronouns ignored
strict.hasProfanity('ควย'); // true — still caughtfinal filter = ThaiProfanityFilter(
extraWords: {'บักหำ': Severity.mild}, // add your own
removeWords: {'กู'}, // unblock a default
allowList: {'ระยำกิจ'}, // never flag this clean word
minSeverity: Severity.moderate,
detectEvasion: true,
);- It's a heuristic, not a moderator. Thai profanity is deeply context-dependent (เหี้ย is also an animal, ควาย/สัตว์ are animals). The default list deliberately leaves out the most ambiguous dual-use words and relies on an allow-list for the rest; you will still want human review for anything high-stakes.
- The allow-list is not exhaustive. A rare clean word that contains a bad
substring and isn't listed may be flagged — add it via
allowList. - Evasion vs. false positives.
detectEvasionjoins spaced letters, which in rare cases can join two clean words into a flagged sequence. Turn it off for exact-only matching. - The bundled word list is a starter set; extend it for your product.
MIT © 2026 MaIII Themd (ultramcu)