Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 78 additions & 46 deletions pythainlp/util/syllable.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ def sound_syllable(syllable: str) -> str:
):
return "live"

# Handle syllables with only อ (no other consonants from thai_consonants_all)
if len(consonants) == 0 and "อ" in syllable:
# Syllables with only อ and long vowels are live
if any((c in set("าีืแูเโไใำ")) for c in syllable):
return "live"
# Short vowels or special cases
if any((c in set(short)) for c in syllable):
return "dead"
# Default to live for vowel-only syllables
return "live"

# get spelling consonants
spelling_consonant = consonants[-1]
if (spelling_consonant in _check_2) and (
Expand Down Expand Up @@ -228,6 +239,10 @@ def _check_sonorant_syllable(syllable: str) -> bool:
_sonorant = [i for i in syllable if i in thai_low_sonorants]
consonants = [i for i in syllable if i in list(thai_consonants)]

# Return False if no sonorants or not enough consonants
if not _sonorant or len(consonants) < 2:
return False

if _sonorant[-1] == consonants[-2]:
return True

Expand Down Expand Up @@ -267,87 +282,104 @@ def tone_detector(syllable: str) -> str:
s = sound_syllable(syllable)
# get consonants
consonants = [i for i in syllable if i in list(thai_consonants)]

# Handle syllables with no consonants (e.g., ฤ, ฦ)
if len(consonants) == 0:
return ""

initial_consonant = consonants[0]
tone_mark = _tone_mark_detector(syllable)
syllable_check = syllable_open_close_detector(syllable)
syllable_check_length = syllable_length(syllable)
initial_consonant_type = thai_initial_consonant_to_type[initial_consonant]
# r for store value
r = ""
# Special handling for อ and ห with sonorants
if len(consonants) > 1 and (initial_consonant in ("อ", "ห")):
consonant_ending = _check_sonorant_syllable(syllable)
if (
initial_consonant == "อ"
and consonant_ending
and s == "live"
and tone_mark == ""
):
r = "l"
elif (
initial_consonant == "ห"
and consonant_ending
and s == "live"
and tone_mark == "่"
):
r = "l"
elif initial_consonant == "อ" and consonant_ending and s == "dead":
r = "l"
elif (
initial_consonant == "ห"
and consonant_ending
and s == "live"
and tone_mark == "้"
):
r = "f"
elif initial_consonant == "ห" and consonant_ending and s == "dead":
r = "l"
elif initial_consonant == "ห" and consonant_ending and s == "live":
r = "r"
elif initial_consonant_type == "high" and s == "live" and tone_mark == "่":
if consonant_ending:
# Only apply special rules if there are sonorants
if (
initial_consonant == ""
and s == "live"
and tone_mark == "่"
):
r = "l"
elif (
initial_consonant == "ห"
and s == "live"
and tone_mark == "่"
):
r = "l"
elif initial_consonant == "อ" and s == "dead":
r = "l"
elif (
initial_consonant == "ห"
and s == "live"
and tone_mark == ""
):
r = "f"
elif initial_consonant == "ห" and s == "dead":
r = "l"
elif initial_consonant == "ห" and s == "live":
r = "r"
# If r is still empty, apply general tone rules
if r == "" and initial_consonant_type == "high" and s == "live" and tone_mark == "่":
r = "l"
elif initial_consonant_type == "mid" and s == "live" and tone_mark == "่":
if r == "" and initial_consonant_type == "mid" and s == "live" and tone_mark == "่":
r = "l"
elif initial_consonant_type == "low" and tone_mark == "้":
if r == "" and initial_consonant_type == "low" and tone_mark == "้":
r = "h"
elif initial_consonant_type == "mid" and tone_mark == "๋":
if r == "" and initial_consonant_type == "mid" and tone_mark == "๋":
r = "r"
elif initial_consonant_type == "mid" and tone_mark == "๊":
if r == "" and initial_consonant_type == "mid" and tone_mark == "๊":
r = "h"
elif initial_consonant_type == "low" and tone_mark == "่":
if r == "" and initial_consonant_type == "low" and tone_mark == "่":
r = "f"
elif initial_consonant_type == "mid" and tone_mark == "้":
if r == "" and initial_consonant_type == "mid" and tone_mark == "้":
r = "f"
elif initial_consonant_type == "high" and tone_mark == "้":
if r == "" and initial_consonant_type == "high" and tone_mark == "้":
r = "f"
elif (
initial_consonant_type == "low"
if (
r == ""
and initial_consonant_type == "low"
and syllable_check_length == "short"
and syllable_check == "close"
and s == "dead"
):
r = "h"
elif (
initial_consonant_type == "low"
if (
r == ""
and initial_consonant_type == "low"
and syllable_check_length == "long"
and syllable_check == "close"
and s == "dead"
):
r = "f"
elif (
initial_consonant_type == "low"
if (
r == ""
and initial_consonant_type == "low"
and syllable_check_length == "short"
and syllable_check == "open"
):
r = "h"
elif initial_consonant_type == "mid" and s == "dead":
if (
r == ""
and initial_consonant_type == "low"
and syllable_check_length == "long"
and syllable_check == "open"
and s == "dead"
):
r = "f"
if r == "" and initial_consonant_type == "mid" and s == "dead":
r = "l"
elif initial_consonant_type == "high" and s == "dead":
if r == "" and initial_consonant_type == "high" and s == "dead":
r = "l"
elif initial_consonant_type == "low" and s == "live":
if r == "" and initial_consonant_type == "low" and s == "live":
r = "m"
elif initial_consonant_type == "mid" and s == "live":
if r == "" and initial_consonant_type == "mid" and s == "live":
r = "m"
elif initial_consonant_type == "high" and s == "live":
if r == "" and initial_consonant_type == "high" and s == "live":
r = "r"

return r
52 changes: 52 additions & 0 deletions tests/core/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,58 @@ def test_tone_detector(self):
("h", "ครับ"),
("f", "ค่ะ"),
("m", "เอ"),
# Test cases from issue #1176 - syllables that previously returned UNKNOWN_tone
# Low consonant + dead + long + open → falling tone
("f", "คอ"),
("f", "พฤ"),
("f", "พอ"),
("f", "หญ้า"),
("f", "อ้อม"),
("f", "อ้าง"),
("f", "เท"),
("f", "เธอ"),
("f", "เพ"),
("f", "เภอ"),
("f", "เอื้อ"),
("f", "แฟ"),
("f", "โค"),
("f", "โฆ"),
("f", "ไอ้"),
# Mid/high consonant + dead → low tone
("l", "หรอก"),
("l", "หลอก"),
("l", "หัก"),
("l", "หาก"),
("l", "ห่อ"),
("l", "อด"),
("l", "อดีต"),
("l", "อธิ"),
("l", "อบ"),
("l", "ออก"),
("l", "อะ"),
("l", "อัด"),
("l", "อาจ"),
("l", "อาด"),
("l", "อิ"),
("l", "อีก"),
("l", "อุต"),
("l", "อุป"),
("l", "อู่"),
("l", "เหตุ"),
("l", "เอก"),
("l", "แอบ"),
("l", "ใหญ่"),
# Mid consonant + live → mid tone (especially syllables with only อ)
("m", "อนา"),
("m", "อัตรา"),
("m", "อา"),
("m", "อำ"),
("m", "เอง"),
("m", "เอา"),
("m", "แอ"),
("m", "โอ"),
("m", "โอน"),
("m", "ไอ"),
]
for i, j in data:
self.assertEqual(
Expand Down
Loading