uzkit 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.
uzkit/__init__.py ADDED
@@ -0,0 +1,25 @@
1
+ """uzkit - O'zbekiston uchun kichik yordamchi vositalar to'plami."""
2
+
3
+ from uzkit.money import format_money, money_to_words
4
+ from uzkit.numbers import number_to_words, ordinal
5
+ from uzkit.regions import Region, get_region, list_regions
6
+ from uzkit.translit import to_cyrillic, to_latin
7
+ from uzkit.validators import is_valid_phone, is_valid_plate, normalize_phone
8
+
9
+ __version__ = "0.1.0"
10
+
11
+ __all__ = [
12
+ "__version__",
13
+ "Region",
14
+ "format_money",
15
+ "get_region",
16
+ "is_valid_phone",
17
+ "is_valid_plate",
18
+ "list_regions",
19
+ "money_to_words",
20
+ "normalize_phone",
21
+ "number_to_words",
22
+ "ordinal",
23
+ "to_cyrillic",
24
+ "to_latin",
25
+ ]
@@ -0,0 +1,16 @@
1
+ [
2
+ {"name": "Andijon viloyati", "name_cyrillic": "Андижон вилояти", "capital": "Andijon", "capital_cyrillic": "Андижон", "plate_code": "60"},
3
+ {"name": "Buxoro viloyati", "name_cyrillic": "Бухоро вилояти", "capital": "Buxoro", "capital_cyrillic": "Бухоро", "plate_code": "80"},
4
+ {"name": "Farg'ona viloyati", "name_cyrillic": "Фарғона вилояти", "capital": "Farg'ona", "capital_cyrillic": "Фарғона", "plate_code": "40"},
5
+ {"name": "Jizzax viloyati", "name_cyrillic": "Жиззах вилояти", "capital": "Jizzax", "capital_cyrillic": "Жиззах", "plate_code": "25"},
6
+ {"name": "Namangan viloyati", "name_cyrillic": "Наманган вилояти", "capital": "Namangan", "capital_cyrillic": "Наманган", "plate_code": "50"},
7
+ {"name": "Navoiy viloyati", "name_cyrillic": "Навоий вилояти", "capital": "Navoiy", "capital_cyrillic": "Навоий", "plate_code": "85"},
8
+ {"name": "Qashqadaryo viloyati", "name_cyrillic": "Қашқадарё вилояти", "capital": "Qarshi", "capital_cyrillic": "Қарши", "plate_code": "70"},
9
+ {"name": "Qoraqalpog'iston Respublikasi", "name_cyrillic": "Қорақалпоғистон Республикаси", "capital": "Nukus", "capital_cyrillic": "Нукус", "plate_code": "95"},
10
+ {"name": "Samarqand viloyati", "name_cyrillic": "Самарқанд вилояти", "capital": "Samarqand", "capital_cyrillic": "Самарқанд", "plate_code": "30"},
11
+ {"name": "Sirdaryo viloyati", "name_cyrillic": "Сирдарё вилояти", "capital": "Guliston", "capital_cyrillic": "Гулистон", "plate_code": "20"},
12
+ {"name": "Surxondaryo viloyati", "name_cyrillic": "Сурхондарё вилояти", "capital": "Termiz", "capital_cyrillic": "Термиз", "plate_code": "75"},
13
+ {"name": "Toshkent shahri", "name_cyrillic": "Тошкент шаҳри", "capital": "Toshkent", "capital_cyrillic": "Тошкент", "plate_code": "01"},
14
+ {"name": "Toshkent viloyati", "name_cyrillic": "Тошкент вилояти", "capital": "Nurafshon", "capital_cyrillic": "Нурафшон", "plate_code": "10"},
15
+ {"name": "Xorazm viloyati", "name_cyrillic": "Хоразм вилояти", "capital": "Urganch", "capital_cyrillic": "Урганч", "plate_code": "90"}
16
+ ]
uzkit/formatters.py ADDED
@@ -0,0 +1 @@
1
+ """Formatlash yordamchilari (hozircha bo'sh)."""
uzkit/money.py ADDED
@@ -0,0 +1,56 @@
1
+ """So'm summalarini so'zga aylantirish va formatlash."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from decimal import ROUND_HALF_UP, Decimal
6
+ from typing import Union
7
+
8
+ from uzkit.numbers import number_to_words
9
+ from uzkit.translit import OKINA
10
+
11
+ SOM = f"so{OKINA}m"
12
+ TIYIN = "tiyin"
13
+
14
+ Amount = Union[int, float, Decimal, str]
15
+
16
+
17
+ def _to_decimal(amount: Amount) -> Decimal:
18
+ if isinstance(amount, bool) or not isinstance(amount, (int, float, Decimal, str)):
19
+ raise TypeError(f"Unsupported amount type: {type(amount).__name__}")
20
+ return Decimal(str(amount)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
21
+
22
+
23
+ def money_to_words(amount: Amount, *, with_tiyin: bool = True) -> str:
24
+ """Summani so'm va tiyinda so'z bilan yozadi.
25
+
26
+ >>> money_to_words(1250)
27
+ 'bir ming ikki yuz ellik soʻm'
28
+ >>> money_to_words("1250.50")
29
+ 'bir ming ikki yuz ellik soʻm ellik tiyin'
30
+ """
31
+ value = _to_decimal(amount)
32
+ sign = "minus " if value < 0 else ""
33
+ value = abs(value)
34
+ som = int(value)
35
+ tiyin = int((value - som) * 100)
36
+ parts = [number_to_words(som), SOM]
37
+ if with_tiyin and tiyin:
38
+ parts += [number_to_words(tiyin), TIYIN]
39
+ return sign + " ".join(parts)
40
+
41
+
42
+ def format_money(amount: Amount) -> str:
43
+ """Summani o'qishga qulay ko'rinishda yozadi.
44
+
45
+ >>> format_money(1250000)
46
+ '1 250 000 soʻm'
47
+ """
48
+ value = _to_decimal(amount)
49
+ sign = "-" if value < 0 else ""
50
+ value = abs(value)
51
+ som = int(value)
52
+ tiyin = int((value - som) * 100)
53
+ text = f"{som:,}".replace(",", " ")
54
+ if tiyin:
55
+ text += f",{tiyin:02d}"
56
+ return f"{sign}{text} {SOM}"
uzkit/numbers.py ADDED
@@ -0,0 +1,126 @@
1
+ """Sonlarni o'zbekcha so'zlar bilan yozish (0 dan 10**12 gacha)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import List
6
+
7
+ from uzkit.translit import OKINA
8
+
9
+ __all__ = ["MAX_NUMBER", "number_to_words", "ordinal"]
10
+
11
+ #: Qo'llab-quvvatlanadigan eng katta son (modul bo'yicha).
12
+ MAX_NUMBER = 10**12
13
+
14
+ _ONES = (
15
+ "",
16
+ "bir",
17
+ "ikki",
18
+ "uch",
19
+ f"to{OKINA}rt",
20
+ "besh",
21
+ "olti",
22
+ "yetti",
23
+ "sakkiz",
24
+ f"to{OKINA}qqiz",
25
+ )
26
+
27
+ _TENS = (
28
+ "",
29
+ f"o{OKINA}n",
30
+ "yigirma",
31
+ f"o{OKINA}ttiz",
32
+ "qirq",
33
+ "ellik",
34
+ "oltmish",
35
+ "yetmish",
36
+ "sakson",
37
+ f"to{OKINA}qson",
38
+ )
39
+
40
+ _SCALES = (
41
+ (10**12, "trillion"),
42
+ (10**9, "milliard"),
43
+ (10**6, "million"),
44
+ (10**3, "ming"),
45
+ )
46
+
47
+ _VOWELS = frozenset("aeiou")
48
+
49
+
50
+ def _validate(n: object) -> None:
51
+ if isinstance(n, bool) or not isinstance(n, int):
52
+ raise TypeError(f"n must be int, not {type(n).__name__}")
53
+ if abs(n) > MAX_NUMBER:
54
+ raise ValueError(f"n must be within [-{MAX_NUMBER}, {MAX_NUMBER}], got {n}")
55
+
56
+
57
+ def _below_thousand(n: int) -> List[str]:
58
+ """1..999 oralig'idagi sonni so'zlar ro'yxati sifatida qaytaradi."""
59
+ words: List[str] = []
60
+ hundreds, rest = divmod(n, 100)
61
+ if hundreds:
62
+ words += [_ONES[hundreds], "yuz"]
63
+ tens, ones = divmod(rest, 10)
64
+ if tens:
65
+ words.append(_TENS[tens])
66
+ if ones:
67
+ words.append(_ONES[ones])
68
+ return words
69
+
70
+
71
+ def number_to_words(n: int) -> str:
72
+ """Butun sonni o'zbekcha so'zlar bilan yozadi.
73
+
74
+ >>> number_to_words(1250)
75
+ 'bir ming ikki yuz ellik'
76
+ >>> number_to_words(0)
77
+ 'nol'
78
+ >>> number_to_words(-5)
79
+ 'minus besh'
80
+
81
+ Oraliq: ``-10**12 <= n <= 10**12``. Manfiy sonlar ``minus`` bilan
82
+ boshlanadi. ``yuz`` va ``ming`` oldidan ``bir`` doim yoziladi
83
+ (``100`` -> ``bir yuz``).
84
+
85
+ ``oʻ`` va ``gʻ`` dagi belgi :data:`uzkit.translit.OKINA` (U+02BB).
86
+
87
+ ``int`` bo'lmagan (``bool`` ham) kiritish uchun ``TypeError``, oraliqdan
88
+ tashqari son uchun ``ValueError`` ko'taradi.
89
+ """
90
+ _validate(n)
91
+ if n == 0:
92
+ return "nol"
93
+
94
+ words: List[str] = ["minus"] if n < 0 else []
95
+ remainder = abs(n)
96
+ for value, name in _SCALES:
97
+ count, remainder = divmod(remainder, value)
98
+ if count:
99
+ words += _below_thousand(count) + [name]
100
+ words += _below_thousand(remainder)
101
+ return " ".join(words)
102
+
103
+
104
+ def ordinal(n: int) -> str:
105
+ """Tartib sonni yozadi: oxirgi so'zga ``-inchi`` yoki ``-nchi`` qo'shiladi.
106
+
107
+ >>> ordinal(3)
108
+ 'uchinchi'
109
+ >>> ordinal(2)
110
+ 'ikkinchi'
111
+ >>> ordinal(1250)
112
+ 'bir ming ikki yuz ellikinchi'
113
+
114
+ Oxirgi so'z unli bilan tugasa ``-nchi``, undosh bilan tugasa ``-inchi``
115
+ qo'shiladi. Oraliq: ``0 <= n <= 10**12``.
116
+
117
+ ``int`` bo'lmagan kiritish uchun ``TypeError``, manfiy yoki oraliqdan
118
+ tashqari son uchun ``ValueError`` ko'taradi.
119
+ """
120
+ _validate(n)
121
+ if n < 0:
122
+ raise ValueError(f"n must be non-negative, got {n}")
123
+
124
+ words = number_to_words(n)
125
+ suffix = "nchi" if words[-1] in _VOWELS else "inchi"
126
+ return words + suffix
uzkit/py.typed ADDED
File without changes
uzkit/regions.py ADDED
@@ -0,0 +1,42 @@
1
+ """O'zbekiston viloyatlari va tarkibiy birliklari."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import dataclass
7
+ from functools import lru_cache
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ __all__ = ["Region", "get_region", "list_regions"]
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class Region:
16
+ name: str
17
+ name_cyrillic: str
18
+ capital: str
19
+ capital_cyrillic: str
20
+ plate_code: str
21
+
22
+
23
+ @lru_cache(maxsize=1)
24
+ def _regions() -> tuple[Region, ...]:
25
+ path = Path(__file__).resolve().parent / "data" / "regions.json"
26
+ with path.open(encoding="utf-8") as file:
27
+ raw = json.load(file)
28
+ return tuple(Region(**item) for item in raw)
29
+
30
+
31
+ def list_regions() -> list[Region]:
32
+ return list(_regions())
33
+
34
+
35
+ def get_region(name: str) -> Optional[Region]:
36
+ if not isinstance(name, str) or not name.strip():
37
+ return None
38
+ needle = name.casefold()
39
+ for region in _regions():
40
+ if region.name.casefold() == needle or region.name_cyrillic.casefold() == needle:
41
+ return region
42
+ return None
uzkit/translit.py ADDED
@@ -0,0 +1,105 @@
1
+ """O'zbek tili uchun Lotin <-> Kirill transliteratsiyasi."""
2
+
3
+ from __future__ import annotations
4
+
5
+ OKINA = "\u02bb" # ʻ o‘ va g‘ dagi belgi
6
+ TUTUQ = "\u02bc" # ʼ tutuq belgisi (ma’no)
7
+
8
+ _APOSTROPHES = frozenset("'\u2019\u02bb\u2018`\u00b4\u02bc")
9
+ _LAT_VOWELS = frozenset("aeiou")
10
+ _CYR_VOWELS = frozenset("аеёиоуўэюяы")
11
+
12
+ _LAT2CYR = {
13
+ "a": "а", "b": "б", "d": "д", "f": "ф", "g": "г", "h": "ҳ",
14
+ "i": "и", "j": "ж", "k": "к", "l": "л", "m": "м", "n": "н",
15
+ "o": "о", "p": "п", "q": "қ", "r": "р", "s": "с", "t": "т",
16
+ "u": "у", "v": "в", "x": "х", "y": "й", "z": "з",
17
+ }
18
+ _Y_COMBOS = {"o": "ё", "u": "ю", "a": "я", "e": "е"}
19
+
20
+ _CYR2LAT = {
21
+ "а": "a", "б": "b", "в": "v", "г": "g", "д": "d", "ё": "yo",
22
+ "ж": "j", "з": "z", "и": "i", "й": "y", "к": "k", "л": "l",
23
+ "м": "m", "н": "n", "о": "o", "п": "p", "р": "r", "с": "s",
24
+ "т": "t", "у": "u", "ф": "f", "х": "x", "ц": "ts", "ч": "ch",
25
+ "ш": "sh", "щ": "shch", "ъ": TUTUQ, "ы": "i", "ь": "", "э": "e",
26
+ "ю": "yu", "я": "ya", "ў": "o" + OKINA, "қ": "q",
27
+ "ғ": "g" + OKINA, "ҳ": "h",
28
+ }
29
+
30
+
31
+ def to_cyrillic(text: str) -> str:
32
+ """Lotin yozuvidagi matnni Kirillga o'giradi.
33
+
34
+ >>> to_cyrillic("O'zbekiston")
35
+ 'Ўзбекистон'
36
+ """
37
+ out: list[str] = []
38
+ n = len(text)
39
+ i = 0
40
+ while i < n:
41
+ ch = text[i]
42
+ low = ch.lower()
43
+ nxt = text[i + 1] if i + 1 < n else ""
44
+ nlow = nxt.lower()
45
+ prev = text[i - 1] if i > 0 else ""
46
+ step = 1
47
+
48
+ if low in ("o", "g") and nxt in _APOSTROPHES:
49
+ res = "ў" if low == "o" else "ғ"
50
+ step = 2
51
+ elif low in ("s", "c") and nlow == "h":
52
+ res = "ш" if low == "s" else "ч"
53
+ step = 2
54
+ elif low == "y" and nlow in _Y_COMBOS:
55
+ res = _Y_COMBOS[nlow]
56
+ step = 2
57
+ elif low == "e":
58
+ at_start = not prev.isalpha() or prev.lower() in _LAT_VOWELS
59
+ res = "э" if at_start else "е"
60
+ elif ch in _APOSTROPHES and prev.isalpha() and nxt.isalpha():
61
+ res = "ъ"
62
+ elif low in _LAT2CYR:
63
+ res = _LAT2CYR[low]
64
+ else:
65
+ out.append(ch)
66
+ i += 1
67
+ continue
68
+
69
+ out.append(res.upper() if ch.isupper() else res)
70
+ i += step
71
+ return "".join(out)
72
+
73
+
74
+ def _has_upper_neighbor(text: str, i: int) -> bool:
75
+ before = i > 0 and text[i - 1].isupper()
76
+ after = i + 1 < len(text) and text[i + 1].isupper()
77
+ return before or after
78
+
79
+
80
+ def to_latin(text: str) -> str:
81
+ """Kirill yozuvidagi matnni Lotinga o'giradi (2023 alifbosi).
82
+
83
+ >>> to_latin("Ўзбекистон")
84
+ 'Oʻzbekiston'
85
+ """
86
+ out: list[str] = []
87
+ for i, ch in enumerate(text):
88
+ low = ch.lower()
89
+ if low == "е":
90
+ prev = text[i - 1].lower() if i > 0 else ""
91
+ ye = not prev.isalpha() or prev in _CYR_VOWELS or prev in "ъь"
92
+ res = "ye" if ye else "e"
93
+ elif low in _CYR2LAT:
94
+ res = _CYR2LAT[low]
95
+ else:
96
+ out.append(ch)
97
+ continue
98
+
99
+ if ch.isupper() and res:
100
+ if len(res) > 1 and not _has_upper_neighbor(text, i):
101
+ res = res.capitalize()
102
+ else:
103
+ res = res.upper()
104
+ out.append(res)
105
+ return "".join(out)
uzkit/utils.py ADDED
@@ -0,0 +1 @@
1
+ """Umumiy yordamchi funksiyalar (hozircha bo'sh)."""
uzkit/validators.py ADDED
@@ -0,0 +1,112 @@
1
+ """O'zbekiston telefon raqamlari va avtomobil davlat raqamlarini tekshirish."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from typing import Any, Optional
7
+
8
+ __all__ = [
9
+ "MOBILE_OPERATOR_CODES",
10
+ "is_valid_phone",
11
+ "is_valid_plate",
12
+ "normalize_phone",
13
+ ]
14
+
15
+ COUNTRY_CODE = "998"
16
+
17
+ #: Mobil operator kodlari (+998 XX ...).
18
+ MOBILE_OPERATOR_CODES = frozenset(
19
+ {
20
+ "20", # Sardor Telecom / OQ
21
+ "33", # Humans
22
+ "50", # Ucell
23
+ "55", # Ucell; VoIP (Uztelecom), mobil emas
24
+ "77", # Uzmobile
25
+ "88", # Mobiuz / Humans
26
+ "90", # Beeline
27
+ "91", # Beeline
28
+ "93", # Ucell
29
+ "94", # Ucell
30
+ "95", # Uzmobile
31
+ "97", # Mobiuz
32
+ "98", # Mobiuz
33
+ "99", # Uzmobile
34
+ }
35
+ )
36
+
37
+ # Raqamlar orasida ruxsat etilgan ajratuvchilar: bo'shliq, tire, nuqta, qavs.
38
+ _PHONE_SEPARATORS = re.compile(r"[\s\-.()]+")
39
+ _PHONE_BODY = re.compile(r"\+?[0-9]+", re.ASCII)
40
+
41
+ # 01 A 123 AA (jismoniy shaxs) va 01 123 AAA (yuridik shaxs)
42
+ _PLATE_PERSONAL = re.compile(r"(\d{2}) ?([A-Z]) ?(\d{3}) ?([A-Z]{2})", re.ASCII)
43
+ _PLATE_LEGAL = re.compile(r"(\d{2}) ?(\d{3}) ?([A-Z]{3})", re.ASCII)
44
+
45
+
46
+ def normalize_phone(phone: Any) -> Optional[str]:
47
+ """Telefon raqamini ``+998XXXXXXXXX`` ko'rinishiga keltiradi.
48
+
49
+ Qabul qilinadigan kiritishlar (bo'shliq, tire, nuqta va qavslar e'tiborga
50
+ olinmaydi)::
51
+
52
+ +998 90 123-45-67
53
+ 998901234567
54
+ 90 123 45 67 (milliy 9 xonali format)
55
+
56
+ Raqam noto'g'ri bo'lsa (yoki ``str`` bo'lmasa) ``None`` qaytariladi.
57
+ Faqat mobil operator kodlari (:data:`MOBILE_OPERATOR_CODES`) qabul qilinadi.
58
+ """
59
+ if not isinstance(phone, str):
60
+ return None
61
+
62
+ cleaned = _PHONE_SEPARATORS.sub("", phone)
63
+ if not _PHONE_BODY.fullmatch(cleaned):
64
+ return None
65
+
66
+ digits = cleaned[1:] if cleaned.startswith("+") else cleaned
67
+
68
+ if len(digits) == 12 and digits.startswith(COUNTRY_CODE):
69
+ national = digits[len(COUNTRY_CODE):]
70
+ elif len(digits) == 9 and not cleaned.startswith("+"):
71
+ national = digits
72
+ else:
73
+ return None
74
+
75
+ if national[:2] not in MOBILE_OPERATOR_CODES:
76
+ return None
77
+
78
+ return f"+{COUNTRY_CODE}{national}"
79
+
80
+
81
+ def is_valid_phone(phone: Any) -> bool:
82
+ """Telefon raqami O'zbekiston mobil raqami ekanini tekshiradi.
83
+
84
+ >>> is_valid_phone("+998 90 123 45 67")
85
+ True
86
+ >>> is_valid_phone("+998 10 123 45 67")
87
+ False
88
+ """
89
+ return normalize_phone(phone) is not None
90
+
91
+
92
+ def is_valid_plate(plate: Any) -> bool:
93
+ """Avtomobil davlat raqamini tekshiradi.
94
+
95
+ Qo'llab-quvvatlanadigan formatlar::
96
+
97
+ 01 A 123 AA (jismoniy shaxslar)
98
+ 01 123 AAA (yuridik shaxslar)
99
+
100
+ Bo'shliqlar ixtiyoriy (``01A123AA``), harflar katta-kichikligi farqlanmaydi.
101
+ Viloyat kodi 00 dan farqli ikki xonali kod bo'lishi shart (01-99).
102
+ """
103
+ if not isinstance(plate, str):
104
+ return False
105
+
106
+ value = plate.strip().upper()
107
+ match = _PLATE_PERSONAL.fullmatch(value) or _PLATE_LEGAL.fullmatch(value)
108
+ if match is None:
109
+ return False
110
+
111
+ code = match.group(1)
112
+ return code != "00" and code.isdigit() and len(code) == 2
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.5
2
+ Name: uzkit
3
+ Version: 0.1.0
4
+ Summary: O'zbek tili va ma'lumotlari uchun Python kutubxonasi: transliteratsiya, raqamlarni so'zga aylantirish, so'm summalari, telefon va avtoraqam validatorlari, viloyatlar.
5
+ Project-URL: Homepage, https://github.com/Mr-Ali-web/Uzkit
6
+ Project-URL: Issues, https://github.com/Mr-Ali-web/Uzkit/issues
7
+ Author: Ali
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: license-plate,numbers,phone,transliteration,uzbek,uzbekistan,validators
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.9
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ [![tests](https://github.com/Mr-Ali-web/uzkit/actions/workflows/tests.yml/badge.svg)](https://github.com/Mr-Ali-web/uzkit/actions/workflows/tests.yml)
29
+
30
+ # uzkit
31
+
32
+ O'zbek tili va ma'lumotlari uchun yordamchi Python kutubxonasi.
33
+ Small Python library for Uzbek language and data helpers.
34
+
35
+ ## O'rnatish / Installation
36
+
37
+ ```bash
38
+ pip install -e ".[dev]"
39
+ ```
40
+
41
+ ## Misollar / Examples
42
+
43
+ ```python
44
+ from uzkit import to_cyrillic, to_latin, number_to_words, money_to_words, format_money, normalize_phone, is_valid_phone, is_valid_plate, ordinal
45
+
46
+ to_cyrillic("Salom, dunyo!") # 'Салом, дунё!'
47
+ to_latin("Салом, дунё!") # 'Salom, dunyo!'
48
+ number_to_words(1250) # 'bir ming ikki yuz ellik'
49
+ money_to_words(1250) # 'bir ming ikki yuz ellik soʻm'
50
+ format_money(1250000) # '1 250 000 soʻm'
51
+ normalize_phone("+998 90 123 45 67") # '+998901234567'
52
+ is_valid_phone("+998 90 123 45 67") # True
53
+ is_valid_plate("01A123BC") # True
54
+ ordinal(3) # 'uchinchi'
55
+ ```
56
+
57
+ ## Testlar / Tests
58
+
59
+ ```bash
60
+ pytest
61
+ ```
62
+
63
+ ## Cheklovlar / Limitations
64
+
65
+ - Telefon kodlari Wikipedia va operator sahifalariga asoslangan, regulyatorning rasmiy hujjati bilan tekshirilmagan.
66
+ - Faqat mobil raqamlar qo'llab-quvvatlanadi.
67
+ - Viloyatlar ro'yxati Wikipedia/ISO 3166-2:UZ manbalariga asoslangan; rasmiy davlat manbasi bilan to'liq solishtirilmagan.
68
+
69
+ Operator and region codes are not verified against an official source. Only mobile phone numbers are supported.
70
+ Region list is based on Wikipedia/ISO 3166-2:UZ and has not been fully reconciled with official government sources.# Uzkit
@@ -0,0 +1,14 @@
1
+ uzkit/__init__.py,sha256=pISo_MTs9z7SgmIibuNWjsawsHlPfuTK9RC3x4saoxQ,650
2
+ uzkit/formatters.py,sha256=sbg1hZ4UPdAPo2-lRKvtaHZYGbVZFG7q9gRrCaAAD5g,49
3
+ uzkit/money.py,sha256=8K-ZHunER85jT_flq5s1Crtvna6v5vTB3GqqweJ58wU,1615
4
+ uzkit/numbers.py,sha256=b6kIKglSqwbBuivSvRx7feshP6UH-TBOaxTtefucZOw,3061
5
+ uzkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ uzkit/regions.py,sha256=s0alAZSB-4fbp20Sf9h6-T9xwzU7sV63Ov_9OvYZOpw,1050
7
+ uzkit/translit.py,sha256=k_UClZQ5zVC8bT5Dy0R9wHRWv1QybS7JkkG3NaWeh8s,3366
8
+ uzkit/utils.py,sha256=pYn4mzXJIHHd6mOfRME7FecOyRrZj7p5BHEsyl6lg9s,53
9
+ uzkit/validators.py,sha256=ANt4gA_yOJg17sQjewWKa5ysn5YDNwUUCTjq2CGy2YY,3157
10
+ uzkit/data/regions.json,sha256=vA3LFkKK_jNtHfImo-IgIXcXuyZKLMAtIlElmXjDPkY,2316
11
+ uzkit-0.1.0.dist-info/METADATA,sha256=Gkxwzsy-0G6Bg7RepB6zOAoIUS5y-cf7gPsVNoRnZKc,2801
12
+ uzkit-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
13
+ uzkit-0.1.0.dist-info/licenses/LICENSE,sha256=Bz5H3MtpNpTVPvu3BCs2Ecaafpc1NSQ-vekroqlZc9I,1060
14
+ uzkit-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ali
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.