| """ |
| teencode_normalizer.py |
| ββββββββββββββββββββββ |
| Vietnamese teencode / internet-slang β standard-form normalizer. |
| |
| Usage: |
| from teencode_normalizer import normalize_teencode |
| clean = normalize_teencode("ko bik j luΓ΄n Γ‘") |
| # β "khΓ΄ng biαΊΏt gΓ¬ luΓ΄n Γ‘" |
| |
| The mapping dictionary is hand-curated for the Vietnamese toxic-comment |
| classification task (ViHOS / UIT-VSMEC style data). It intentionally |
| preserves vulgar terms in their full form so downstream classifiers can |
| learn to detect them. |
| |
| All replacements are **case-insensitive** and respect word boundaries |
| so that partial matches inside longer words are not corrupted. |
| """ |
|
|
| import re |
| from typing import Dict |
|
|
| |
| |
| |
| TEENCODE_MAP: Dict[str, str] = { |
| |
| "ko": "khΓ΄ng", |
| "k": "khΓ΄ng", |
| "kg": "khΓ΄ng", |
| "khg": "khΓ΄ng", |
| "kh": "khΓ΄ng", |
| "hk": "khΓ΄ng", |
| "hem": "khΓ΄ng", |
| "hΓ΄ng": "khΓ΄ng", |
| "kΓ΄": "khΓ΄ng", |
| "0": "khΓ΄ng", |
| "dc": "Δược", |
| "dk": "Δược", |
| "Δc": "Δược", |
| "Δk": "Δược", |
| "duoc": "Δược", |
| "ns": "nΓ³i", |
| "nch": "nΓ³i chuyα»n", |
| "nc": "nΓ³i chuyα»n", |
| "bt": "bΓ¬nh thΖ°α»ng", |
| "bth": "bΓ¬nh thΖ°α»ng", |
| "bn": "bao nhiΓͺu", |
| "bnhieu": "bao nhiΓͺu", |
| "bh": "bao giα»", |
| "bjh": "bao giα»", |
| "lm": "lΓ m", |
| "lam": "lΓ m", |
| "mk": "mình", |
| "mik": "mình", |
| "mn": "mα»i ngΖ°α»i", |
| "mng": "mα»i ngΖ°α»i", |
| "ng": "ngΖ°α»i", |
| "ngta": "ngΖ°α»i ta", |
| "nta": "ngΖ°α»i ta", |
| "nt": "nhαΊ―n tin", |
| "tl": "trαΊ£ lα»i", |
| "rep": "trαΊ£ lα»i", |
| "ib": "inbox", |
| "fb": "facebook", |
| "acc": "tΓ i khoαΊ£n", |
| "ad": "admin", |
| "amin": "admin", |
| "adm": "admin", |
| "a": "anh", |
| "e": "em", |
| "aj": "ai", |
| "aq": "ai", |
| "j": "gì", |
| "ji": "gì", |
| "z": "vαΊy", |
| "v": "vαΊy", |
| "vl": "vΓ£i", |
| "r": "rα»i", |
| "oy": "rα»i", |
| "rui": "rα»i", |
| "roi": "rα»i", |
| "m": "mΓ y", |
| "t": "tao", |
| "tui": "tΓ΄i", |
| "cx": "cΕ©ng", |
| "cg": "cΕ©ng", |
| "cug": "cΕ©ng", |
| "cux": "cΕ©ng", |
| "vs": "vα»i", |
| "voi": "vα»i", |
| "nma": "nhΖ°ng mΓ ", |
| "nhma": "nhΖ°ng mΓ ", |
| "trc": "trΖ°α»c", |
| "trog": "trong", |
| "trg": "trong", |
| "ngoai": "ngoΓ i", |
| "ngΓ²ai": "ngoΓ i", |
| "dg": "ΔΖ°α»ng", |
| "Δg": "ΔΖ°α»ng", |
| "hiu": "hiα»u", |
| "bik": "biαΊΏt", |
| "biet": "biαΊΏt", |
| "bit": "biαΊΏt", |
| "wa": "quΓ‘", |
| "qΓ‘": "quΓ‘", |
| "qa": "quΓ‘", |
| "lun": "luΓ΄n", |
| "luon": "luΓ΄n", |
| "nek": "nè", |
| "nè": "nè", |
| "nha": "nhΓ©", |
| "nhaz": "nhΓ©", |
| "nhen": "nhΓ©", |
| "hen": "nhΓ©", |
| "thik": "thΓch", |
| "thix": "thΓch", |
| "pn": "phα»₯ nα»―", |
| "ck": "chα»ng", |
| "vk": "vợ", |
| "gd": "gia ΔΓ¬nh", |
| "gΔ": "gia ΔΓ¬nh", |
| "xh": "xΓ£ hα»i", |
| "dt": "Δiα»n thoαΊ‘i", |
| "Δt": "Δiα»n thoαΊ‘i", |
| "sdt": "sα» Δiα»n thoαΊ‘i", |
| "zui": "vui", |
| "zαΊy": "vαΊy", |
| "zì": "gì", |
| "dzαΊy": "vαΊy", |
| "dz": "vαΊy", |
| "dαΊy": "vαΊy", |
| "vαΊy": "vαΊy", |
| "mΓ": "vα»i", |
| "iu": "yΓͺu", |
| "yeu": "yΓͺu", |
| "cmt": "bΓ¬nh luαΊn", |
| "comt": "bΓ¬nh luαΊn", |
| "share": "chia sαΊ»", |
| "fl": "theo dΓ΅i", |
| "like": "thΓch", |
| "sub": "ΔΔng kΓ½", |
| "clip": "video", |
| "tks": "cαΊ£m Ζ‘n", |
| "thanks": "cαΊ£m Ζ‘n", |
| "tq": "cαΊ£m Ζ‘n", |
| "ty": "cαΊ£m Ζ‘n", |
| "sr": "xin lα»i", |
| "sorry": "xin lα»i", |
| "plz": "xin hΓ£y", |
| "pls": "xin hΓ£y", |
| "ok": "Δược", |
| "oke": "Δược", |
| "okie": "Δược", |
| "okee": "Δược", |
| "ngon": "ngon", |
| "xin": "xin", |
| "ot": "tΔng ca", |
| "cty": "cΓ΄ng ty", |
| "cong ty": "cΓ΄ng ty", |
| "nyc": "ngΖ°α»i yΓͺu cΕ©", |
| "ny": "ngΖ°α»i yΓͺu", |
| "hs": "hα»c sinh", |
| "sv": "sinh viΓͺn", |
| "gv": "giΓ‘o viΓͺn", |
| "hsg": "hα»c sinh giα»i", |
| "ctv": "cα»ng tΓ‘c viΓͺn", |
| "hcm": "hα» chΓ minh", |
| "hn": "hΓ nα»i", |
| "sg": "sΓ i gΓ²n", |
| "vn": "viα»t nam", |
| "nck": "nghiΓͺn cα»©u khoa hα»c", |
| "Δh": "ΔαΊ‘i hα»c", |
| "dh": "ΔαΊ‘i hα»c", |
| "thpt": "trung hα»c phα» thΓ΄ng", |
| "thcs": "trung hα»c cΖ‘ sα»", |
| "ths": "thαΊ‘c sΔ©", |
| "ts": "tiαΊΏn sΔ©", |
| "gs": "giΓ‘o sΖ°", |
| "pgs": "phΓ³ giΓ‘o sΖ°", |
|
|
| |
| |
| "vcl": "vΓ£i cαΊ£ lα»n", |
| "vkl": "vΓ£i cαΊ£ lα»n", |
| "vcc": "vΓ£i cαΊ£ chΖ°α»ng", |
| "vl": "vΓ£i lα»n", |
| "vcΔ": "vΓ£i cαΊ£ ΔΓ‘i", |
| "Δm": "Δα»₯ mαΊΉ", |
| "dm": "Δα»₯ mαΊΉ", |
| "dmm": "Δα»₯ mαΊΉ mΓ y", |
| "Δmm": "Δα»₯ mαΊΉ mΓ y", |
| "dkm": "Δα»₯ con mαΊΉ", |
| "Δkm": "Δα»₯ con mαΊΉ", |
| "dcm": "Δα»₯ con mαΊΉ", |
| "Δcm": "Δα»₯ con mαΊΉ", |
| "clm": "con lα»n mΓ y", |
| "cl": "cΓ‘i lα»n", |
| "cmm": "con mαΊΉ mΓ y", |
| "cc": "cαΊ·c", |
| "ccc": "cαΊ·c", |
| "ccm": "cΓ‘i cαΊ·c mΓ y", |
| "bkl": "bα» mΓ y cΓ‘i lα»n", |
| "ΔΓ©o": "ΔΓ©o", |
| "deo": "ΔΓ©o", |
| "ΔΓΉ": "Δα»₯", |
| "du": "Δα»₯", |
| "wtf": "cÑi quÑi gì", |
| "stfu": "cΓ’m mα»m Δi", |
| "lol": "haha", |
| "lmao": "haha", |
| "bruh": "bαΊ‘n Ζ‘i", |
| "omg": "trα»i Ζ‘i", |
| "haiz": "thα» dΓ i", |
| "huhu": "khΓ³c", |
| "hihi": "hehe", |
| "kk": "haha", |
| "kkk": "haha", |
| "haha": "haha", |
| "hehe": "hehe", |
| "tl": "trαΊ£ lα»i", |
| "tks": "cαΊ£m Ζ‘n", |
| "ngu": "ngu", |
| "Γ³c chΓ³": "ngu", |
| "vkΔ": "vΓ£i cαΊ£ ΔΓ‘i", |
|
|
| |
| "rag": "rαΊ±ng", |
| "trog": "trong", |
| "nguoi": "ngΖ°α»i", |
| "ngui": "ngΖ°α»i", |
| "nuoc": "nΖ°α»c", |
| "nc": "nΖ°α»c", |
| "nhug": "nhΖ°ng", |
| "nug": "nhΖ°ng", |
| "vΓ’g": "vΓ’ng", |
| "thg": "thαΊ±ng", |
| "thag": "thαΊ±ng", |
| "thΔg": "thαΊ±ng", |
| "cog": "cΓ΄ng", |
| "nhΓ nc": "nhΓ nΖ°α»c", |
| "ks": "khΓ‘ch sαΊ‘n", |
| "bv": "bα»nh viα»n", |
| "sg": "sΓ i gΓ²n", |
| "cp": "chΓnh phα»§", |
| "td": "thα»§ ΔΓ΄", |
|
|
| |
|
|
| |
| "crush": "ngΖ°α»i thΖ°Ζ‘ng", |
| "flex": "khoe", |
| "slay": "quΓ‘ Δα»nh", |
| "real": "thαΊt", |
| "cap": "nΓ³i dα»i", |
| "nocap": "thαΊt luΓ΄n", |
| "no cap": "thαΊt luΓ΄n", |
| "ib": "nhαΊ―n riΓͺng", |
| "toxic": "Δα»c hαΊ‘i", |
| "simp": "theo Δuα»i mΓΉ quΓ‘ng", |
| "gato": "ghen Δn tα»©c α»", |
| "hΓ³ng": "tΓ² mΓ²", |
| "ship": "ghΓ©p ΔΓ΄i", |
| "out trΓ¬nh": "vượt trα»i", |
| "bΓ³c phα»t": "tα» cΓ‘o", |
| "trαΊ©u": "ngu", |
| "Δu trend": "theo xu hΖ°α»ng", |
| "chill": "thΖ° giΓ£n", |
| "vibe": "cαΊ£m giΓ‘c", |
| "deep": "sΓ’u sαΊ―c", |
| "αΊ£o tung chαΊ£o": "quΓ‘ αΊ£o", |
| "chαΊ±m zn": "trαΊ§m cαΊ£m", |
| "xΓ m": "nhαΊ£m nhΓ", |
| "xΓ m lol": "nhαΊ£m nhΓ", |
| "gαΊ₯u": "ngΖ°α»i yΓͺu", |
| "cαΊ©u lΖ°Ζ‘ng": "tΓ¬nh cαΊ£m ngα»t ngΓ o", |
| "sαΊΏn": "sαΊΏn sΓΊa", |
| "xα»n xΓ²": "tα»t ΔαΊΉp", |
| "gΓ©t gΓ΄": "let's go", |
| "Γ©t Γ΄ Γ©t": "kΓͺu cα»©u", |
| "u lΓ trα»i": "trα»i Ζ‘i", |
| } |
|
|
|
|
| |
| |
| |
| |
| |
| _SORTED_KEYS = sorted(TEENCODE_MAP.keys(), key=len, reverse=True) |
|
|
| |
| |
| |
| |
| _PATTERN = re.compile( |
| r"(?<!\w)(" + "|".join(re.escape(k) for k in _SORTED_KEYS) + r")(?!\w)", |
| re.IGNORECASE, |
| ) |
|
|
|
|
| def _replace(match: re.Match) -> str: |
| """Return the standard form for the matched teencode token.""" |
| token = match.group(0).lower() |
| return TEENCODE_MAP.get(token, token) |
|
|
|
|
| |
| |
| |
| |
| _REPEAT_CHAR = re.compile(r"(.)\1{2,}") |
|
|
| |
| _MULTI_SPACE = re.compile(r"\s{2,}") |
|
|
|
|
| def normalize_teencode(text: str) -> str: |
| """ |
| Normalise Vietnamese teencode / internet slang in *text*. |
| |
| Steps |
| ----- |
| 1. Dictionary-based word replacement (case-insensitive, word-boundary aware). |
| 2. Collapse repeated characters (β₯ 3 identical β 2). |
| 3. Strip leading / trailing whitespace and collapse internal runs. |
| |
| Parameters |
| ---------- |
| text : str |
| Raw Vietnamese comment text. |
| |
| Returns |
| ------- |
| str |
| Text with teencode replaced by standard Vietnamese. |
| """ |
| if not isinstance(text, str) or not text: |
| return text |
|
|
| |
| text = _PATTERN.sub(_replace, text) |
|
|
| |
| text = _REPEAT_CHAR.sub(r"\1\1", text) |
|
|
| |
| text = _MULTI_SPACE.sub(" ", text).strip() |
|
|
| return text |
|
|
|
|
| |
| |
| |
| if __name__ == "__main__": |
| examples = [ |
| "ko bik j luΓ΄n Γ‘", |
| "mng Ζ‘i ad ib e vs nha", |
| "thg nΓ y ngu vcl Δm", |
| "sao m ko rep t z", |
| "chill quΓ‘ Δi hehe crush iu wa", |
| "Bα»n nΓ y toΓ n xΓ m lol vcl dmm", |
| "Γ©t Γ΄ Γ©t αΊ£o tung chαΊ£o luΓ΄n", |
| "ΔαΊΉppppp quΓ‘aaaaa", |
| ] |
| for ex in examples: |
| print(f" IN: {ex}") |
| print(f" OUT: {normalize_teencode(ex)}") |
| print() |
|
|