bdkit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bdkit/__init__.py +27 -0
- bdkit/cli.py +76 -0
- bdkit/digits.py +44 -0
- bdkit/phone.py +86 -0
- bdkit/py.typed +0 -0
- bdkit/words.py +123 -0
- bdkit-0.1.0.dist-info/METADATA +111 -0
- bdkit-0.1.0.dist-info/RECORD +11 -0
- bdkit-0.1.0.dist-info/WHEEL +4 -0
- bdkit-0.1.0.dist-info/entry_points.txt +2 -0
- bdkit-0.1.0.dist-info/licenses/LICENSE +21 -0
bdkit/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Everyday Bangladesh utilities: taka in words, mobile numbers, Bangla digits.
|
|
2
|
+
|
|
3
|
+
>>> from bdkit import taka_in_words, normalize_mobile
|
|
4
|
+
>>> taka_in_words(1500)
|
|
5
|
+
'One Thousand Five Hundred Taka Only'
|
|
6
|
+
>>> normalize_mobile("01712-345678")
|
|
7
|
+
'+8801712345678'
|
|
8
|
+
"""
|
|
9
|
+
from .digits import format_lakh, to_bangla_digits, to_english_digits
|
|
10
|
+
from .phone import OPERATORS, MobileNumber, is_valid_mobile, mobile_operator, normalize_mobile, parse_mobile
|
|
11
|
+
from .words import number_to_words, taka_in_words
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"OPERATORS",
|
|
17
|
+
"MobileNumber",
|
|
18
|
+
"format_lakh",
|
|
19
|
+
"is_valid_mobile",
|
|
20
|
+
"mobile_operator",
|
|
21
|
+
"normalize_mobile",
|
|
22
|
+
"number_to_words",
|
|
23
|
+
"parse_mobile",
|
|
24
|
+
"taka_in_words",
|
|
25
|
+
"to_bangla_digits",
|
|
26
|
+
"to_english_digits",
|
|
27
|
+
]
|
bdkit/cli.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""``bdkit`` command line.
|
|
2
|
+
|
|
3
|
+
bdkit words 125050.50 # One Lakh Twenty-Five Thousand Fifty Taka and Fifty Paisa Only
|
|
4
|
+
bdkit words 12500 --bn # বারো হাজার পাঁচ শত টাকা মাত্র
|
|
5
|
+
bdkit phone "+880 1712-345678" # +8801712345678 Grameenphone
|
|
6
|
+
bdkit lakh 12345678 # 1,23,45,678
|
|
7
|
+
bdkit bn 2026 # ২০২৬
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import sys
|
|
13
|
+
from typing import List, Optional
|
|
14
|
+
|
|
15
|
+
from . import __version__
|
|
16
|
+
from .digits import format_lakh, to_bangla_digits, to_english_digits
|
|
17
|
+
from .phone import parse_mobile
|
|
18
|
+
from .words import taka_in_words
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
22
|
+
parser = argparse.ArgumentParser(prog="bdkit", description="Everyday Bangladesh utilities.",
|
|
23
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
24
|
+
epilog=__doc__.split("\n", 2)[2])
|
|
25
|
+
parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}")
|
|
26
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
27
|
+
|
|
28
|
+
words = sub.add_parser("words", help="taka amount in words")
|
|
29
|
+
words.add_argument("amount")
|
|
30
|
+
words.add_argument("--bn", action="store_true", help="in Bangla")
|
|
31
|
+
words.add_argument("--no-only", action="store_true", help='leave out "Only" / "মাত্র"')
|
|
32
|
+
|
|
33
|
+
phone = sub.add_parser("phone", help="validate a mobile number and show its operator")
|
|
34
|
+
phone.add_argument("number", nargs="+")
|
|
35
|
+
|
|
36
|
+
lakh = sub.add_parser("lakh", help="format a number with lakh/crore commas")
|
|
37
|
+
lakh.add_argument("amount")
|
|
38
|
+
lakh.add_argument("-d", "--decimals", type=int, default=0)
|
|
39
|
+
lakh.add_argument("--bn", action="store_true", help="use Bangla digits")
|
|
40
|
+
|
|
41
|
+
bn = sub.add_parser("bn", help="convert digits to Bangla")
|
|
42
|
+
bn.add_argument("text", nargs="+")
|
|
43
|
+
en = sub.add_parser("en", help="convert Bangla digits to English")
|
|
44
|
+
en.add_argument("text", nargs="+")
|
|
45
|
+
return parser
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main(argv: Optional[List[str]] = None) -> int:
|
|
49
|
+
args = build_parser().parse_args(argv)
|
|
50
|
+
try:
|
|
51
|
+
if args.command == "words":
|
|
52
|
+
print(taka_in_words(args.amount, lang="bn" if args.bn else "en", only=not args.no_only))
|
|
53
|
+
elif args.command == "phone":
|
|
54
|
+
status = 0
|
|
55
|
+
for raw in args.number:
|
|
56
|
+
parsed = parse_mobile(raw)
|
|
57
|
+
if parsed:
|
|
58
|
+
print(f"{parsed.e164} {parsed.operator}")
|
|
59
|
+
else:
|
|
60
|
+
print(f"{raw} invalid", file=sys.stderr)
|
|
61
|
+
status = 1
|
|
62
|
+
return status
|
|
63
|
+
elif args.command == "lakh":
|
|
64
|
+
print(format_lakh(args.amount, decimals=args.decimals, bangla=args.bn))
|
|
65
|
+
elif args.command == "bn":
|
|
66
|
+
print(to_bangla_digits(" ".join(args.text)))
|
|
67
|
+
elif args.command == "en":
|
|
68
|
+
print(to_english_digits(" ".join(args.text)))
|
|
69
|
+
except (ValueError, ArithmeticError) as e:
|
|
70
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
71
|
+
return 1
|
|
72
|
+
return 0
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
sys.exit(main())
|
bdkit/digits.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Bangla ↔ English digits and lakh-style number formatting."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
_EN = "0123456789"
|
|
8
|
+
_BN = "০১২৩৪৫৬৭৮৯"
|
|
9
|
+
_TO_BN = str.maketrans(_EN, _BN)
|
|
10
|
+
_TO_EN = str.maketrans(_BN, _EN)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def to_bangla_digits(value: object) -> str:
|
|
14
|
+
"""``'2026-11-15' -> '২০২৬-১১-১৫'``"""
|
|
15
|
+
return str(value).translate(_TO_BN)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def to_english_digits(value: object) -> str:
|
|
19
|
+
"""``'০১৭১২' -> '01712'``"""
|
|
20
|
+
return str(value).translate(_TO_EN)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def format_lakh(amount: Union[int, float, Decimal, str], decimals: int = 0, bangla: bool = False) -> str:
|
|
24
|
+
"""Group digits the South Asian way: ``12345678 -> '1,23,45,678'``.
|
|
25
|
+
|
|
26
|
+
>>> format_lakh(12345678.5, decimals=2)
|
|
27
|
+
'1,23,45,678.50'
|
|
28
|
+
>>> format_lakh(150000, bangla=True)
|
|
29
|
+
'১,৫০,০০০'
|
|
30
|
+
"""
|
|
31
|
+
value = Decimal(to_english_digits(str(amount)).replace(",", ""))
|
|
32
|
+
sign = "-" if value < 0 else ""
|
|
33
|
+
text = f"{abs(value):.{decimals}f}"
|
|
34
|
+
whole, _, frac = text.partition(".")
|
|
35
|
+
if len(whole) > 3:
|
|
36
|
+
head, tail = whole[:-3], whole[-3:]
|
|
37
|
+
groups = []
|
|
38
|
+
while len(head) > 2:
|
|
39
|
+
groups.insert(0, head[-2:])
|
|
40
|
+
head = head[:-2]
|
|
41
|
+
groups.insert(0, head)
|
|
42
|
+
whole = ",".join(groups + [tail])
|
|
43
|
+
result = sign + whole + (f".{frac}" if frac else "")
|
|
44
|
+
return to_bangla_digits(result) if bangla else result
|
bdkit/phone.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Bangladeshi mobile numbers: validate, normalise and detect the operator."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from .digits import to_english_digits
|
|
9
|
+
|
|
10
|
+
OPERATORS = {
|
|
11
|
+
"013": "Grameenphone",
|
|
12
|
+
"017": "Grameenphone",
|
|
13
|
+
"014": "Banglalink",
|
|
14
|
+
"019": "Banglalink",
|
|
15
|
+
"016": "Robi", # formerly Airtel, merged into Robi
|
|
16
|
+
"018": "Robi",
|
|
17
|
+
"015": "Teletalk",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_SEPARATORS = re.compile(r"[\s\-().]")
|
|
21
|
+
# Optional country code (+88 / 0088 / 88), optional trunk 0, then 1 + operator digit + 8 digits.
|
|
22
|
+
_PATTERN = re.compile(r"^(?:(?:\+|00)?88)?0?(1[3-9]\d{8})$")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class MobileNumber:
|
|
27
|
+
"""A valid Bangladeshi mobile number."""
|
|
28
|
+
|
|
29
|
+
local: str # 01712345678
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def e164(self) -> str:
|
|
33
|
+
"""International format: ``+8801712345678``."""
|
|
34
|
+
return "+88" + self.local
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def prefix(self) -> str:
|
|
38
|
+
return self.local[:3]
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def operator(self) -> str:
|
|
42
|
+
return OPERATORS[self.prefix]
|
|
43
|
+
|
|
44
|
+
def formatted(self) -> str:
|
|
45
|
+
"""Readable local format: ``01712-345678``."""
|
|
46
|
+
return f"{self.local[:5]}-{self.local[5:]}"
|
|
47
|
+
|
|
48
|
+
def __str__(self) -> str:
|
|
49
|
+
return self.e164
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def parse_mobile(number: str) -> Optional[MobileNumber]:
|
|
53
|
+
"""Parse a mobile number written in almost any common way.
|
|
54
|
+
|
|
55
|
+
Accepts ``01712345678``, ``+880 1712-345678``, ``8801712345678``,
|
|
56
|
+
``008801712345678`` and Bangla digits. Returns ``None`` if invalid.
|
|
57
|
+
|
|
58
|
+
>>> parse_mobile("+880 1712-345678").operator
|
|
59
|
+
'Grameenphone'
|
|
60
|
+
>>> parse_mobile("০১৮১২৩৪৫৬৭৮").e164
|
|
61
|
+
'+8801812345678'
|
|
62
|
+
>>> parse_mobile("0123") is None
|
|
63
|
+
True
|
|
64
|
+
"""
|
|
65
|
+
cleaned = _SEPARATORS.sub("", to_english_digits(str(number)))
|
|
66
|
+
match = _PATTERN.match(cleaned)
|
|
67
|
+
return MobileNumber("0" + match.group(1)) if match else None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def is_valid_mobile(number: str) -> bool:
|
|
71
|
+
"""``True`` if ``number`` is a valid Bangladeshi mobile number."""
|
|
72
|
+
return parse_mobile(number) is not None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def normalize_mobile(number: str) -> str:
|
|
76
|
+
"""Return the number as ``+8801XXXXXXXXX`` or raise ``ValueError``."""
|
|
77
|
+
parsed = parse_mobile(number)
|
|
78
|
+
if parsed is None:
|
|
79
|
+
raise ValueError(f"Not a valid Bangladeshi mobile number: {number!r}")
|
|
80
|
+
return parsed.e164
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def mobile_operator(number: str) -> Optional[str]:
|
|
84
|
+
"""Operator name for a number (``'Grameenphone'``, ``'Robi'``...), or ``None`` if invalid."""
|
|
85
|
+
parsed = parse_mobile(number)
|
|
86
|
+
return parsed.operator if parsed else None
|
bdkit/py.typed
ADDED
|
File without changes
|
bdkit/words.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Numbers and taka amounts in words, using the lakh/crore system, in English or Bangla."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from decimal import ROUND_HALF_UP, Decimal, InvalidOperation
|
|
5
|
+
from typing import List, Tuple, Union
|
|
6
|
+
|
|
7
|
+
Number = Union[int, float, str, Decimal]
|
|
8
|
+
|
|
9
|
+
_EN_ONES = (
|
|
10
|
+
"Zero One Two Three Four Five Six Seven Eight Nine Ten Eleven Twelve Thirteen Fourteen "
|
|
11
|
+
"Fifteen Sixteen Seventeen Eighteen Nineteen"
|
|
12
|
+
).split()
|
|
13
|
+
_EN_TENS = "_ _ Twenty Thirty Forty Fifty Sixty Seventy Eighty Ninety".split()
|
|
14
|
+
|
|
15
|
+
# Bangla number words 0-99 are irregular, so they are listed in full.
|
|
16
|
+
_BN_0_99 = (
|
|
17
|
+
"শূন্য এক দুই তিন চার পাঁচ ছয় সাত আট নয় "
|
|
18
|
+
"দশ এগারো বারো তেরো চৌদ্দ পনেরো ষোলো সতেরো আঠারো উনিশ "
|
|
19
|
+
"বিশ একুশ বাইশ তেইশ চব্বিশ পঁচিশ ছাব্বিশ সাতাশ আটাশ ঊনত্রিশ "
|
|
20
|
+
"ত্রিশ একত্রিশ বত্রিশ তেত্রিশ চৌত্রিশ পঁয়ত্রিশ ছত্রিশ সাঁইত্রিশ আটত্রিশ ঊনচল্লিশ "
|
|
21
|
+
"চল্লিশ একচল্লিশ বিয়াল্লিশ তেতাল্লিশ চুয়াল্লিশ পঁয়তাল্লিশ ছেচল্লিশ সাতচল্লিশ আটচল্লিশ ঊনপঞ্চাশ "
|
|
22
|
+
"পঞ্চাশ একান্ন বায়ান্ন তিপ্পান্ন চুয়ান্ন পঞ্চান্ন ছাপ্পান্ন সাতান্ন আটান্ন ঊনষাট "
|
|
23
|
+
"ষাট একষট্টি বাষট্টি তেষট্টি চৌষট্টি পঁয়ষট্টি ছেষট্টি সাতষট্টি আটষট্টি ঊনসত্তর "
|
|
24
|
+
"সত্তর একাত্তর বাহাত্তর তিয়াত্তর চুয়াত্তর পঁচাত্তর ছিয়াত্তর সাতাত্তর আটাত্তর ঊনআশি "
|
|
25
|
+
"আশি একাশি বিরাশি তিরাশি চুরাশি পঁচাশি ছিয়াশি সাতাশি আটাশি ঊননব্বই "
|
|
26
|
+
"নব্বই একানব্বই বিরানব্বই তিরানব্বই চুরানব্বই পঁচানব্বই ছিয়ানব্বই সাতানব্বই আটানব্বই নিরানব্বই"
|
|
27
|
+
).split()
|
|
28
|
+
assert len(_BN_0_99) == 100
|
|
29
|
+
|
|
30
|
+
# (value, english, bangla) from largest to smallest; crore repeats for bigger numbers.
|
|
31
|
+
_SCALES: Tuple[Tuple[int, str, str], ...] = (
|
|
32
|
+
(10_000_000, "Crore", "কোটি"),
|
|
33
|
+
(100_000, "Lakh", "লক্ষ"),
|
|
34
|
+
(1_000, "Thousand", "হাজার"),
|
|
35
|
+
(100, "Hundred", "শত"),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _en_below_100(n: int) -> str:
|
|
40
|
+
if n < 20:
|
|
41
|
+
return _EN_ONES[n]
|
|
42
|
+
tens, ones = divmod(n, 10)
|
|
43
|
+
return _EN_TENS[tens] + (f"-{_EN_ONES[ones]}" if ones else "")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _words(n: int, lang: str) -> List[str]:
|
|
47
|
+
"""Words for a non-negative integer, lakh/crore style."""
|
|
48
|
+
small = _en_below_100 if lang == "en" else (lambda k: _BN_0_99[k])
|
|
49
|
+
if n < 100:
|
|
50
|
+
return [small(n)]
|
|
51
|
+
parts: List[str] = []
|
|
52
|
+
for value, en, bn in _SCALES:
|
|
53
|
+
if n >= value:
|
|
54
|
+
count, n = divmod(n, value)
|
|
55
|
+
# Above 99 crore, the crore count itself is spelled out ("One Hundred Crore").
|
|
56
|
+
parts += _words(count, lang) if count >= 100 else [small(count)]
|
|
57
|
+
parts.append(en if lang == "en" else bn)
|
|
58
|
+
if n:
|
|
59
|
+
parts.append(small(n))
|
|
60
|
+
return parts
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _check_lang(lang: str) -> str:
|
|
64
|
+
lang = lang.lower()
|
|
65
|
+
if lang not in ("en", "bn"):
|
|
66
|
+
raise ValueError("lang must be 'en' or 'bn'")
|
|
67
|
+
return lang
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def number_to_words(number: int, lang: str = "en") -> str:
|
|
71
|
+
"""Spell out a whole number using lakh and crore.
|
|
72
|
+
|
|
73
|
+
>>> number_to_words(1_25_000)
|
|
74
|
+
'One Lakh Twenty-Five Thousand'
|
|
75
|
+
>>> number_to_words(1_25_000, lang="bn")
|
|
76
|
+
'এক লক্ষ পঁচিশ হাজার'
|
|
77
|
+
"""
|
|
78
|
+
lang = _check_lang(lang)
|
|
79
|
+
if isinstance(number, bool) or int(number) != number:
|
|
80
|
+
raise ValueError("number_to_words needs a whole number; use taka_in_words for money")
|
|
81
|
+
n = int(number)
|
|
82
|
+
prefix = ("Minus " if lang == "en" else "ঋণাত্মক ") if n < 0 else ""
|
|
83
|
+
return prefix + " ".join(_words(abs(n), lang))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _to_decimal(amount: Number) -> Decimal:
|
|
87
|
+
try:
|
|
88
|
+
value = Decimal(str(amount).replace(",", "").replace("৳", "").strip())
|
|
89
|
+
except InvalidOperation:
|
|
90
|
+
raise ValueError(f"Not a valid amount: {amount!r}") from None
|
|
91
|
+
if not value.is_finite():
|
|
92
|
+
raise ValueError(f"Not a valid amount: {amount!r}")
|
|
93
|
+
return value.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def taka_in_words(amount: Number, lang: str = "en", only: bool = True) -> str:
|
|
97
|
+
"""Write a taka amount in words, as on invoices and cheques.
|
|
98
|
+
|
|
99
|
+
Paisa (two decimal places) are included when present. Pass ``only=False``
|
|
100
|
+
to drop the trailing "Only" / "মাত্র".
|
|
101
|
+
|
|
102
|
+
>>> taka_in_words(125050.50)
|
|
103
|
+
'One Lakh Twenty-Five Thousand Fifty Taka and Fifty Paisa Only'
|
|
104
|
+
>>> taka_in_words("12,500", lang="bn")
|
|
105
|
+
'বারো হাজার পাঁচ শত টাকা মাত্র'
|
|
106
|
+
"""
|
|
107
|
+
lang = _check_lang(lang)
|
|
108
|
+
value = _to_decimal(amount)
|
|
109
|
+
negative = value < 0
|
|
110
|
+
taka, paisa = divmod(int(abs(value) * 100), 100)
|
|
111
|
+
|
|
112
|
+
if lang == "en":
|
|
113
|
+
text = f"{number_to_words(taka)} Taka"
|
|
114
|
+
if paisa:
|
|
115
|
+
text += f" and {number_to_words(paisa)} Paisa"
|
|
116
|
+
text += " Only" if only else ""
|
|
117
|
+
return ("Minus " + text) if negative else text
|
|
118
|
+
|
|
119
|
+
text = f"{number_to_words(taka, 'bn')} টাকা"
|
|
120
|
+
if paisa:
|
|
121
|
+
text += f" {number_to_words(paisa, 'bn')} পয়সা"
|
|
122
|
+
text += " মাত্র" if only else ""
|
|
123
|
+
return ("ঋণাত্মক " + text) if negative else text
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: bdkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Everyday Bangladesh utilities: taka in words (English/Bangla, lakh/crore), mobile number validation with operator detection, Bangla digits.
|
|
5
|
+
Project-URL: Homepage, https://github.com/sarif-mia/bdkit
|
|
6
|
+
Project-URL: Source, https://github.com/sarif-mia/bdkit
|
|
7
|
+
Project-URL: Tracker, https://github.com/sarif-mia/bdkit/issues
|
|
8
|
+
Author: Sarif Mia
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: amount-in-words,bangla,bangladesh,bengali,crore,grameenphone,lakh,phone,taka
|
|
12
|
+
Classifier: Natural Language :: Bengali
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Provides-Extra: test
|
|
20
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# bdkit
|
|
24
|
+
|
|
25
|
+
Everyday utilities for Bangladeshi apps: write taka amounts in words for invoices and cheques (English or Bangla, lakh/crore), validate mobile numbers and detect the operator, and convert between Bangla and English digits.
|
|
26
|
+
|
|
27
|
+
No dependencies. Python 3.9+.
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pip install bdkit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Taka in words
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from bdkit import taka_in_words, number_to_words
|
|
37
|
+
|
|
38
|
+
taka_in_words(125050.50)
|
|
39
|
+
# 'One Lakh Twenty-Five Thousand Fifty Taka and Fifty Paisa Only'
|
|
40
|
+
|
|
41
|
+
taka_in_words("12,500", lang="bn")
|
|
42
|
+
# 'বারো হাজার পাঁচ শত টাকা মাত্র'
|
|
43
|
+
|
|
44
|
+
taka_in_words(1000, only=False)
|
|
45
|
+
# 'One Thousand Taka'
|
|
46
|
+
|
|
47
|
+
number_to_words(52_71_036, lang="bn")
|
|
48
|
+
# 'বায়ান্ন লক্ষ একাত্তর হাজার ছত্রিশ'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Amounts can be numbers, `Decimal`s or strings with commas or `৳`. Paisa are rounded half up to two decimal places.
|
|
52
|
+
|
|
53
|
+
## Mobile numbers
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from bdkit import parse_mobile, normalize_mobile, mobile_operator, is_valid_mobile
|
|
57
|
+
|
|
58
|
+
normalize_mobile("01712-345678") # '+8801712345678'
|
|
59
|
+
mobile_operator("+880 1912 345678") # 'Banglalink'
|
|
60
|
+
is_valid_mobile("০১৮১২৩৪৫৬৭৮") # True (Bangla digits work)
|
|
61
|
+
|
|
62
|
+
m = parse_mobile("008801512345678")
|
|
63
|
+
m.local, m.e164, m.operator, m.formatted()
|
|
64
|
+
# ('01512345678', '+8801512345678', 'Teletalk', '01512-345678')
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Accepted forms are `017…`, `+880 17…`, `+88 017…`, `88017…` and `0088017…`, with or without spaces, dashes or brackets.
|
|
68
|
+
|
|
69
|
+
| Prefix | Operator |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| 013, 017 | Grameenphone |
|
|
72
|
+
| 014, 019 | Banglalink |
|
|
73
|
+
| 016, 018 | Robi |
|
|
74
|
+
| 015 | Teletalk |
|
|
75
|
+
|
|
76
|
+
## Digits and lakh formatting
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from bdkit import to_bangla_digits, to_english_digits, format_lakh
|
|
80
|
+
|
|
81
|
+
to_bangla_digits("2026-11-15") # '২০২৬-১১-১৫'
|
|
82
|
+
to_english_digits("৳১,২৫,০০০") # '৳1,25,000'
|
|
83
|
+
format_lakh(12345678) # '1,23,45,678'
|
|
84
|
+
format_lakh(1234567.5, decimals=2) # '12,34,567.50'
|
|
85
|
+
format_lakh(150000, bangla=True) # '১,৫০,০০০'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Command line
|
|
89
|
+
|
|
90
|
+
```console
|
|
91
|
+
$ bdkit words 125050.50
|
|
92
|
+
One Lakh Twenty-Five Thousand Fifty Taka and Fifty Paisa Only
|
|
93
|
+
$ bdkit words 12500 --bn
|
|
94
|
+
বারো হাজার পাঁচ শত টাকা মাত্র
|
|
95
|
+
$ bdkit phone "+880 1712-345678" 01812345678
|
|
96
|
+
+8801712345678 Grameenphone
|
|
97
|
+
+8801812345678 Robi
|
|
98
|
+
$ bdkit lakh 12345678
|
|
99
|
+
1,23,45,678
|
|
100
|
+
$ bdkit bn 2026
|
|
101
|
+
২০২৬
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Related
|
|
105
|
+
|
|
106
|
+
- [bd-income-tax](https://pypi.org/project/bd-income-tax/): Bangladesh income tax calculator
|
|
107
|
+
- [bangladesh-mcp](https://pypi.org/project/bangladesh-mcp/): these tools for AI assistants (MCP)
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
bdkit/__init__.py,sha256=J9BaTfF-14VHaCDgeXjxWMjX9O3d8RCYj0NJ5oCBiL0,760
|
|
2
|
+
bdkit/cli.py,sha256=vTMODd9tb22Nonp-CREKr6iVOskK6YcCOMtdsxwSULg,3120
|
|
3
|
+
bdkit/digits.py,sha256=Qz177DAClKyUd8-Iy-ZOx9NnvBbBMoUA5vDYcVrakQc,1436
|
|
4
|
+
bdkit/phone.py,sha256=LpS-r_uBnbczVgupnRkvzY_diQ6--8Hy-ekEHC9-M-A,2560
|
|
5
|
+
bdkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
bdkit/words.py,sha256=aBy9HcrxlpmdFmm9N_ryY3yA4-pgCTPOEobGJ8a9rEU,6156
|
|
7
|
+
bdkit-0.1.0.dist-info/METADATA,sha256=HhCa1m0H0RgVIobvbfuhp2I0MMLLt4ELv6DIjVHlL9Y,3568
|
|
8
|
+
bdkit-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
9
|
+
bdkit-0.1.0.dist-info/entry_points.txt,sha256=qGzJ_IC2kG6HI2RLOhgAXa819r_3Q2mf4LH44HZsbgU,41
|
|
10
|
+
bdkit-0.1.0.dist-info/licenses/LICENSE,sha256=vOTqrVxUhsF2lbDM6B4om77vgEd8hlC_kIHmtMKa4R8,1066
|
|
11
|
+
bdkit-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sarif Mia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|