# SPDX-FileCopyrightText: 2016-2026 PyThaiNLP Project
# SPDX-FileType: SOURCE
# SPDX-License-Identifier: Apache-2.0
"""
Convert numbers to Thai text read out.
Adapted from:
* https://justmindthought.blogspot.com/2012/12/code-php.html
* https://suksit.com/post/writing-bahttext-in-php/
"""
from __future__ import annotations
import math
from typing import Optional
__all__: list[str] = ["bahttext", "num_to_thaiword", "num_to_thaiword_float"]
_VALUES: list[str] = [
"",
"หนึ่ง",
"สอง",
"สาม",
"สี่",
"ห้า",
"หก",
"เจ็ด",
"แปด",
"เก้า",
]
_PLACES: list[str] = ["", "สิบ", "ร้อย", "พัน", "หมื่น", "แสน", "ล้าน"]
_DIGITS: list[str] = [
"ศูนย์",
"หนึ่ง",
"สอง",
"สาม",
"สี่",
"ห้า",
"หก",
"เจ็ด",
"แปด",
"เก้า",
]
_EXCEPTIONS: dict[str, str] = {"หนึ่งสิบ": "สิบ", "สองสิบ": "ยี่สิบ", "สิบหนึ่ง": "สิบเอ็ด"}
[docs]
def bahttext(number: float) -> str:
"""
Convert a number to Thai text in Baht currency format.
Add the suffix "บาท" (Baht). The precision is fixed at two decimal
places (0.00) to fit the "สตางค์" (Satang) unit.
This function works similarly to the ``BAHTTEXT`` function in
Microsoft Excel.
:param float number: number to be converted
:return: text representing the amount of money in Thai currency
format
:rtype: str
:raises TypeError: if *number* is not a numeric type
:Example:
>>> from pythainlp.util import bahttext
>>> bahttext(1)
'หนึ่งบาทถ้วน'
>>> bahttext(21)
'ยี่สิบเอ็ดบาทถ้วน'
>>> bahttext(200)
'สองร้อยบาทถ้วน'
"""
if not isinstance(number, (int, float)):
raise TypeError(
f"number must be a numeric type, not {type(number).__name__!r}"
)
ret = ""
if number == 0:
ret = "ศูนย์บาทถ้วน"
else:
num_int_str, num_dec_str = f"{number:.2f}".split(".")
num_int = int(num_int_str)
num_dec = int(num_dec_str)
baht = num_to_thaiword(num_int)
if baht:
ret = f"{ret}{baht}บาท"
satang = num_to_thaiword(num_dec)
if satang and satang != "ศูนย์":
ret = f"{ret}{satang}สตางค์"
else:
ret = f"{ret}ถ้วน"
return ret
def _num_to_thaiword_block(num: int) -> str:
"""
Convert a positive integer < 1,000,000 to Thai text.
This is the core logic for a single block of up to 6 digits.
"""
if num == 0:
return ""
output = ""
num_str = str(num)
for place, value in enumerate(list(num_str[::-1])):
if value != "0":
output = _VALUES[int(value)] + _PLACES[place] + output
for search, replac in _EXCEPTIONS.items():
output = output.replace(search, replac)
# เอ็ด rule: trailing หนึ่ง in ones place
if num != 1 and output.endswith("หนึ่ง"):
output = output[: -len("หนึ่ง")] + "เอ็ด"
return output
[docs]
def num_to_thaiword(number: Optional[int]) -> str:
"""
Convert an integer to Thai text.
:param Optional[int] number: integer to be converted
:return: text representing the number in Thai
:rtype: str
:Example:
>>> from pythainlp.util import num_to_thaiword
>>> num_to_thaiword(1)
'หนึ่ง'
>>> num_to_thaiword(11)
'สิบเอ็ด'
"""
if number is None:
return ""
if number == 0:
return "ศูนย์"
number_abs = abs(number)
number_str = str(number_abs)
# Split into groups of 6 digits from right side
groups: list[str] = []
while number_str:
group = number_str[-6:]
number_str = number_str[:-6]
groups.append(group)
output = ""
for i in range(len(groups) - 1, -1, -1):
group_num = int(groups[i])
if group_num > 0:
output += _num_to_thaiword_block(group_num)
if i > 0:
output += "ล้าน"
# Global เอ็ด rule: trailing หนึ่ง in the full text
if number_abs != 1 and output.endswith("หนึ่ง"):
output = output[: -len("หนึ่ง")] + "เอ็ด"
if number < 0:
output = "ลบ" + output
return output
def _expand_exponent(num_str: str) -> str:
"""Rewrite scientific notation (such as "1e-05") as a decimal string."""
if "e" not in num_str.lower():
return num_str
mantissa, exp_str = num_str.lower().split("e", 1)
mant_int, _, mant_frac = mantissa.partition(".")
digits = mant_int + mant_frac
shift = int(exp_str) - len(mant_frac)
if shift >= 0:
return digits + ("0" * shift)
pos = len(digits) + shift
if pos > 0:
return digits[:pos] + "." + digits[pos:]
return "0." + ("0" * (-pos)) + digits
[docs]
def num_to_thaiword_float(number: float) -> str:
"""
Convert a floating-point number to Thai text.
Convert the integer part with :func:`num_to_thaiword`.
Read the decimal point as "จุด".
Read each digit after the decimal point individually, without place
descriptions.
:param float number: number to be converted
:return: text representing the number in Thai
:rtype: str
:raises TypeError: if *number* is not a numeric type
:raises ValueError: if *number* is not finite
:Example:
>>> from pythainlp.util import num_to_thaiword_float
>>> num_to_thaiword_float(123.45)
'หนึ่งร้อยยี่สิบสามจุดสี่ห้า'
>>> num_to_thaiword_float(3.14159)
'สามจุดหนึ่งสี่หนึ่งห้าเก้า'
"""
if not isinstance(number, (int, float)):
raise TypeError(
f"number must be a numeric type, not {type(number).__name__!r}"
)
# Reject non-finite floats early (nan/inf), since they cannot be rendered.
if isinstance(number, float) and not math.isfinite(number):
raise ValueError("number must be a finite float")
if isinstance(number, int) or number.is_integer():
return num_to_thaiword(int(number))
num_str = _expand_exponent(str(abs(number)))
if "." in num_str:
int_part, dec_part = num_str.split(".")
result = num_to_thaiword(int(int_part)) + "จุด"
result += "".join(_DIGITS[int(digit)] for digit in dec_part)
else:
result = num_to_thaiword(int(num_str))
return "ลบ" + result if number < 0 else result