django-nepkit 0.1.0__py3-none-any.whl → 0.2.1__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.
Files changed (39) hide show
  1. django_nepkit/__init__.py +20 -0
  2. django_nepkit/admin.py +243 -93
  3. django_nepkit/conf.py +34 -0
  4. django_nepkit/constants.py +129 -0
  5. django_nepkit/filters.py +113 -0
  6. django_nepkit/forms.py +9 -9
  7. django_nepkit/lang_utils.py +52 -0
  8. django_nepkit/models.py +138 -161
  9. django_nepkit/serializers.py +127 -28
  10. django_nepkit/static/django_nepkit/js/address-chaining.js +129 -29
  11. django_nepkit/static/django_nepkit/js/nepal-data.js +1 -0
  12. django_nepkit/static/django_nepkit/js/nepali-datepicker-init.js +55 -46
  13. django_nepkit/utils.py +270 -46
  14. django_nepkit/validators.py +1 -1
  15. django_nepkit/views.py +127 -10
  16. django_nepkit/widgets.py +100 -31
  17. django_nepkit-0.2.1.dist-info/METADATA +308 -0
  18. django_nepkit-0.2.1.dist-info/RECORD +37 -0
  19. example/demo/admin.py +45 -21
  20. example/demo/models.py +41 -4
  21. example/demo/serializers.py +39 -0
  22. example/demo/urls.py +13 -2
  23. example/demo/views.py +125 -3
  24. example/example_project/settings.py +10 -0
  25. example/example_project/urls.py +1 -1
  26. example/manage.py +2 -2
  27. django_nepkit/templatetags/__init__.py +0 -0
  28. django_nepkit/templatetags/nepali.py +0 -74
  29. django_nepkit-0.1.0.dist-info/METADATA +0 -377
  30. django_nepkit-0.1.0.dist-info/RECORD +0 -39
  31. example/demo/migrations/0001_initial.py +0 -2113
  32. example/demo/migrations/0002_alter_person_phone_number.py +0 -18
  33. example/demo/migrations/0003_person_created_at_person_updated_at.py +0 -27
  34. example/demo/migrations/0004_alter_person_created_at_alter_person_updated_at.py +0 -23
  35. example/demo/migrations/0005_alter_person_created_at_alter_person_updated_at.py +0 -27
  36. example/demo/migrations/__init__.py +0 -0
  37. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.1.dist-info}/WHEEL +0 -0
  38. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.1.dist-info}/licenses/LICENSE +0 -0
  39. {django_nepkit-0.1.0.dist-info → django_nepkit-0.2.1.dist-info}/top_level.txt +0 -0
django_nepkit/__init__.py CHANGED
@@ -6,14 +6,26 @@ from .models import (
6
6
  ProvinceField,
7
7
  DistrictField,
8
8
  MunicipalityField,
9
+ NepaliCurrencyField,
9
10
  )
10
11
  from .admin import (
11
12
  NepaliDateFilter,
13
+ NepaliMonthFilter,
12
14
  format_nepali_date,
13
15
  format_nepali_datetime,
14
16
  NepaliModelAdmin,
15
17
  NepaliAdminMixin,
16
18
  )
19
+ from .serializers import (
20
+ NepaliCurrencySerializerField,
21
+ NepaliLocalizedSerializerMixin,
22
+ )
23
+ from .filters import (
24
+ NepaliDateYearFilter,
25
+ NepaliDateMonthFilter,
26
+ NepaliDateRangeFilter,
27
+ NepaliCurrencyRangeFilter,
28
+ )
17
29
 
18
30
  __all__ = [
19
31
  "NepaliDateField",
@@ -24,8 +36,16 @@ __all__ = [
24
36
  "DistrictField",
25
37
  "MunicipalityField",
26
38
  "NepaliDateFilter",
39
+ "NepaliMonthFilter",
27
40
  "format_nepali_date",
28
41
  "format_nepali_datetime",
29
42
  "NepaliModelAdmin",
30
43
  "NepaliAdminMixin",
44
+ "NepaliCurrencyField",
45
+ "NepaliCurrencySerializerField",
46
+ "NepaliLocalizedSerializerMixin",
47
+ "NepaliDateYearFilter",
48
+ "NepaliDateMonthFilter",
49
+ "NepaliDateRangeFilter",
50
+ "NepaliCurrencyRangeFilter",
31
51
  ]
django_nepkit/admin.py CHANGED
@@ -2,77 +2,71 @@ from django.contrib import admin
2
2
  from django.utils.translation import gettext_lazy as _
3
3
  from nepali.datetime import nepalidate, nepalidatetime
4
4
 
5
- from django_nepkit.models import NepaliDateField
5
+ from django_nepkit.conf import nepkit_settings
6
+ from django_nepkit.models import (
7
+ NepaliDateField,
8
+ NepaliDateTimeField,
9
+ NepaliCurrencyField,
10
+ )
6
11
  from django_nepkit.utils import (
7
12
  try_parse_nepali_date,
8
13
  try_parse_nepali_datetime,
14
+ format_nepali_currency,
9
15
  )
16
+ from django_nepkit.utils import BS_DATE_FORMAT
10
17
 
11
18
 
12
- def format_nepali_date(date_value, format_string="%B %d, %Y"):
13
- """
14
- Format a nepalidate object with Nepali month names.
15
-
16
- Args:
17
- date_value: A nepalidate object or string in YYYY-MM-DD format
18
- format_string: strftime format string (default: '%B %d, %Y')
19
- %B = Full month name (Baishak, Jestha, etc.)
20
- %b = Short month name
21
- %d = Day of month
22
- %Y = Year
23
-
24
- Returns:
25
- Formatted date string with Nepali month names, or empty string if invalid
26
- """
27
- if date_value is None:
19
+ def _format_nepali_common(value, try_parse_func, format_string, ne, cls_type):
20
+ """Helper to format dates/times with optional Devanagari support."""
21
+ if value is None:
28
22
  return ""
29
23
 
30
24
  try:
31
- parsed = try_parse_nepali_date(date_value)
25
+ parsed = try_parse_func(value)
32
26
  if parsed is not None:
27
+ if ne and hasattr(parsed, "strftime_ne"):
28
+ return parsed.strftime_ne(format_string)
33
29
  return parsed.strftime(format_string)
34
- if isinstance(date_value, nepalidate):
35
- return date_value.strftime(
36
- format_string
37
- ) # defensive; should be covered above
30
+ if isinstance(value, cls_type):
31
+ if ne and hasattr(value, "strftime_ne"):
32
+ return value.strftime_ne(format_string)
33
+ return value.strftime(format_string)
38
34
  except (ValueError, TypeError, AttributeError):
39
35
  pass
40
36
 
41
- return str(date_value) if date_value else ""
37
+ return str(value) if value else ""
42
38
 
43
39
 
44
- def format_nepali_datetime(datetime_value, format_string="%B %d, %Y %I:%M %p"):
40
+ def format_nepali_date(date_value, format_string="%B %d, %Y", ne=False):
45
41
  """
46
- Format a nepalidatetime object with Nepali month names.
47
-
48
- Default output uses **12-hour time with AM/PM**.
42
+ Format a nepalidate object with Nepali month names.
49
43
  """
50
- if datetime_value is None:
51
- return ""
52
-
53
- try:
54
- parsed = try_parse_nepali_datetime(datetime_value)
55
- if parsed is not None:
56
- return parsed.strftime(format_string)
57
- if isinstance(datetime_value, nepalidatetime):
58
- return datetime_value.strftime(
59
- format_string
60
- ) # defensive; should be covered above
61
- except (ValueError, TypeError, AttributeError):
62
- pass
44
+ return _format_nepali_common(
45
+ date_value, try_parse_nepali_date, format_string, ne, nepalidate
46
+ )
63
47
 
64
- return str(datetime_value) if datetime_value else ""
65
48
 
66
-
67
- class NepaliDateFilter(admin.FieldListFilter):
49
+ def format_nepali_datetime(datetime_value, format_string=None, ne=False):
68
50
  """
69
- A list filter for NepaliDateField to filter by BS Year.
51
+ Format a nepalidatetime object with Nepali month names.
70
52
  """
53
+ if format_string is None:
54
+ if nepkit_settings.TIME_FORMAT == 24:
55
+ format_string = "%B %d, %Y %H:%M"
56
+ else:
57
+ format_string = "%B %d, %Y %I:%M %p"
58
+
59
+ return _format_nepali_common(
60
+ datetime_value, try_parse_nepali_datetime, format_string, ne, nepalidatetime
61
+ )
62
+
63
+
64
+ class BaseNepaliDateFilter(admin.FieldListFilter):
65
+ """Base class for date filters (Year/Month)."""
71
66
 
72
67
  def __init__(self, field, request, params, model, model_admin, field_path):
73
- self.parameter_name = f"{field_path}_bs_year"
68
+ self.parameter_name = f"{field_path}_{self.suffix}"
74
69
  super().__init__(field, request, params, model, model_admin, field_path)
75
- self.title = _("Nepali Date (Year)")
76
70
 
77
71
  def expected_parameters(self):
78
72
  return [self.parameter_name]
@@ -83,40 +77,85 @@ class NepaliDateFilter(admin.FieldListFilter):
83
77
  "query_string": changelist.get_query_string(remove=[self.parameter_name]),
84
78
  "display": _("All"),
85
79
  }
86
- current_year = nepalidate.today().year
87
- for year in range(current_year - 10, current_year + 2):
80
+ for value, display in self.get_filter_options():
88
81
  yield {
89
- "selected": self.used_parameters.get(self.parameter_name) == str(year),
82
+ "selected": self.used_parameters.get(self.parameter_name) == str(value),
90
83
  "query_string": changelist.get_query_string(
91
- {self.parameter_name: str(year)}
84
+ {self.parameter_name: str(value)}
92
85
  ),
93
- "display": str(year),
86
+ "display": display,
94
87
  }
95
88
 
96
89
  def queryset(self, request, queryset):
97
90
  value = self.used_parameters.get(self.parameter_name)
98
91
  if value:
99
- year = int(value)
100
- # Convert BS year range to AD date range
101
- start_date_bs = nepalidate(year, 1, 1)
102
- # Find last day of the year. Some BS years end on the 30th,
103
- # so we try 31 first and then fall back to 30 if that date
104
- # is not valid.
105
- try:
106
- end_date_bs = nepalidate(year, 12, 31)
107
- except ValueError:
108
- end_date_bs = nepalidate(year, 12, 30)
92
+ return self.apply_filter(queryset, value)
93
+ return queryset
109
94
 
110
- start_date_ad = start_date_bs.to_date()
111
- end_date_ad = end_date_bs.to_date()
95
+ def get_filter_options(self):
96
+ raise NotImplementedError
112
97
 
98
+ def apply_filter(self, queryset, value):
99
+ raise NotImplementedError
100
+
101
+
102
+ class NepaliDateFilter(BaseNepaliDateFilter):
103
+ """Filter by Nepali Year (e.g., 2080)."""
104
+
105
+ suffix = "bs_year"
106
+ title = _("Nepali Date (Year)")
107
+
108
+ def get_filter_options(self):
109
+ current_year = nepalidate.today().year
110
+ return [(y, str(y)) for y in range(current_year - 10, current_year + 2)]
111
+
112
+ def apply_filter(self, queryset, value):
113
+ if BS_DATE_FORMAT.startswith("%Y"):
114
+ separator = BS_DATE_FORMAT[2] if len(BS_DATE_FORMAT) > 2 else "-"
113
115
  return queryset.filter(
114
- **{f"{self.field_path}__range": (start_date_ad, end_date_ad)}
116
+ **{f"{self.field_path}__startswith": f"{value}{separator}"}
115
117
  )
116
- return queryset
118
+
119
+ return queryset.filter(**{f"{self.field_path}__icontains": f"{value}"})
120
+
121
+
122
+ class NepaliMonthFilter(BaseNepaliDateFilter):
123
+ """Filter by Nepali Month (e.g., Baisakh)."""
124
+
125
+ suffix = "bs_month"
126
+ title = _("Nepali Date (Month)")
127
+
128
+ def get_filter_options(self):
129
+ ne = nepkit_settings.DEFAULT_LANGUAGE == "ne"
130
+ names = [
131
+ ("बैशाख", "Baisakh"),
132
+ ("जेठ", "Jestha"),
133
+ ("असार", "Ashad"),
134
+ ("साउन", "Shrawan"),
135
+ ("भदौ", "Bhadra"),
136
+ ("असोज", "Ashwin"),
137
+ ("कात्तिक", "Kartik"),
138
+ ("मंसिर", "Mangsir"),
139
+ ("पुष", "Poush"),
140
+ ("माघ", "Magh"),
141
+ ("फागुन", "Falgun"),
142
+ ("चैत", "Chaitra"),
143
+ ]
144
+ return [(f"{i:02d}", n[0] if ne else n[1]) for i, n in enumerate(names, 1)]
145
+
146
+ def apply_filter(self, queryset, value):
147
+ from django_nepkit.utils import BS_DATE_FORMAT
148
+
149
+ if BS_DATE_FORMAT == "%Y-%m-%d":
150
+ return queryset.filter(**{f"{self.field_path}__contains": f"-{value}-"})
151
+
152
+ separator = BS_DATE_FORMAT[2] if len(BS_DATE_FORMAT) > 2 else "-"
153
+ return queryset.filter(
154
+ **{f"{self.field_path}__contains": f"{separator}{value}{separator}"}
155
+ )
117
156
 
118
157
 
119
- # Register NepaliDateFilter as the default list filter for NepaliDateField
158
+ # Standard filter for any NepaliDateField in Admin
120
159
  admin.FieldListFilter.register(
121
160
  lambda f: isinstance(f, NepaliDateField),
122
161
  NepaliDateFilter,
@@ -125,65 +164,176 @@ admin.FieldListFilter.register(
125
164
 
126
165
 
127
166
  class NepaliAdminMixin:
128
- """
129
- Mixin for Django admin classes that provides Nepali date utilities.
130
- Makes format_nepali_date and NepaliDateFilter available without explicit imports.
131
- """
167
+ """Provides date formatting tools for Admin classes."""
168
+
169
+ def _get_field_ne_setting(self, field_name):
170
+ """
171
+ Get the 'ne' setting from a model field.
172
+
173
+ Args:
174
+ field_name: Name of the field in the model
175
+
176
+ Returns:
177
+ True if field has ne=True, False otherwise
178
+ """
179
+ if not hasattr(self, "model"):
180
+ return False
132
181
 
133
- def format_nepali_date(self, date_value, format_string="%B %d, %Y"):
182
+ try:
183
+ field = self.model._meta.get_field(field_name)
184
+ if hasattr(field, "ne"):
185
+ return field.ne
186
+ except (AttributeError, LookupError):
187
+ pass
188
+
189
+ return False
190
+
191
+ def format_nepali_date(
192
+ self, date_value, format_string="%B %d, %Y", ne=None, field_name=None
193
+ ):
134
194
  """
135
195
  Format a nepalidate object with Nepali month names.
136
196
  Available as a method on admin classes using this mixin.
197
+
198
+ Args:
199
+ date_value: A nepalidate object or string
200
+ format_string: strftime format string
201
+ ne: If True, format using Devanagari script. If None, auto-detect from field or global settings (default: None)
202
+ field_name: Name of the field to auto-detect 'ne' setting from (optional)
137
203
  """
138
- return format_nepali_date(date_value, format_string)
204
+ if ne is None and field_name:
205
+ ne = self._get_field_ne_setting(field_name)
206
+ elif ne is None:
207
+ ne = nepkit_settings.DEFAULT_LANGUAGE == "ne"
208
+
209
+ return format_nepali_date(date_value, format_string, ne=ne)
139
210
 
140
211
  def format_nepali_datetime(
141
- self, datetime_value, format_string="%B %d, %Y %I:%M %p"
212
+ self,
213
+ datetime_value,
214
+ format_string=None,
215
+ ne=None,
216
+ field_name=None,
142
217
  ):
143
- return format_nepali_datetime(datetime_value, format_string)
218
+ """
219
+ Format a nepalidatetime object with Nepali month names.
220
+
221
+ Args:
222
+ datetime_value: A nepalidatetime object or string
223
+ format_string: strftime format string
224
+ ne: If True, format using Devanagari script. If None, auto-detect from field or global settings (default: None)
225
+ field_name: Name of the field to auto-detect 'ne' setting from (optional)
226
+ """
227
+ if ne is None and field_name:
228
+ ne = self._get_field_ne_setting(field_name)
229
+ elif ne is None:
230
+ ne = nepkit_settings.DEFAULT_LANGUAGE == "ne"
231
+
232
+ return format_nepali_datetime(datetime_value, format_string, ne=ne)
233
+
234
+ def format_nepali_currency(self, value, currency_symbol="Rs.", ne=False, **kwargs):
235
+ """
236
+ Format a number with Nepali-style commas.
237
+ Available as a method on admin classes using this mixin.
238
+ """
239
+ return format_nepali_currency(value, currency_symbol=currency_symbol, ne=ne)
144
240
 
145
241
 
146
242
  class NepaliModelAdmin(NepaliAdminMixin, admin.ModelAdmin):
147
243
  """
148
- Base ModelAdmin class with Nepali date utilities built-in.
149
- Use this instead of admin.ModelAdmin to have format_nepali_date available.
244
+ Standard Admin class that automatically formats Nepali dates in lists.
150
245
 
151
246
  Example:
152
247
  from django_nepkit import NepaliModelAdmin, NepaliDateFilter
153
248
 
154
249
  @admin.register(MyModel)
155
250
  class MyModelAdmin(NepaliModelAdmin):
156
- list_filter = (('date_field', NepaliDateFilter),)
157
-
158
- def display_date(self, obj):
159
- return self.format_nepali_date(obj.date_field)
251
+ list_display = ("name", "birth_date", "created_at") # auto-formatted
252
+ list_filter = (("birth_date", NepaliDateFilter),)
160
253
  """
161
254
 
162
- # Make NepaliDateFilter available as a class attribute
255
+ # Make filters available as class attributes
163
256
  NepaliDateFilter = NepaliDateFilter
257
+ NepaliMonthFilter = NepaliMonthFilter
258
+
259
+ def _make_nepali_display(self, field_name, formatter_method):
260
+ """Helper to create display columns for Nepali dates."""
261
+ admin_instance = self
262
+ try:
263
+ field = self.model._meta.get_field(field_name)
264
+ short_description = getattr(
265
+ field, "verbose_name", field_name.replace("_", " ").title()
266
+ )
267
+ except Exception:
268
+ short_description = field_name.replace("_", " ").title()
269
+
270
+ def display(obj):
271
+ val = getattr(obj, field_name, None)
272
+ if val is None:
273
+ return admin_instance.get_empty_value_display()
274
+ # Call the passed formatter method (bound to self)
275
+ return formatter_method(val, field_name=field_name)
276
+
277
+ display.short_description = short_description
278
+ display.admin_order_field = field_name
279
+ return display
280
+
281
+ def _make_nepali_date_display(self, field_name):
282
+ return self._make_nepali_display(field_name, self.format_nepali_date)
283
+
284
+ def _make_nepali_datetime_display(self, field_name):
285
+ return self._make_nepali_display(field_name, self.format_nepali_datetime)
286
+
287
+ def _make_nepali_currency_display(self, field_name):
288
+ return self._make_nepali_display(field_name, self.format_nepali_currency)
289
+
290
+ def get_list_display(self, request):
291
+ list_display = super().get_list_display(request)
292
+ result = []
293
+ for item in list_display:
294
+ if not isinstance(item, str):
295
+ result.append(item)
296
+ continue
297
+ try:
298
+ field = self.model._meta.get_field(item)
299
+ if isinstance(field, NepaliDateField):
300
+ result.append(self._make_nepali_date_display(item))
301
+ continue
302
+ if isinstance(field, NepaliDateTimeField):
303
+ result.append(self._make_nepali_datetime_display(item))
304
+ continue
305
+ if isinstance(field, NepaliCurrencyField):
306
+ result.append(self._make_nepali_currency_display(item))
307
+ continue
308
+ except Exception:
309
+ pass
310
+ result.append(item)
311
+ return result
164
312
 
165
- # Ensure admin forms render Nepali fields with the proper widget,
166
- # even if a project doesn't provide custom ModelForms.
167
313
  def formfield_for_dbfield(self, db_field, request, **kwargs):
168
- """
169
- Force Nepali widgets in Django admin without requiring user forms.
170
- """
314
+ """Automatically use NepaliDatePicker in the admin form."""
171
315
  try:
172
316
  from django_nepkit.models import NepaliDateField, NepaliDateTimeField
173
317
  from django_nepkit.widgets import NepaliDatePickerWidget
174
318
  except Exception:
175
319
  return super().formfield_for_dbfield(db_field, request, **kwargs)
176
320
 
177
- if isinstance(db_field, (NepaliDateField, NepaliDateTimeField)):
178
- kwargs.setdefault("widget", NepaliDatePickerWidget)
321
+ if (
322
+ isinstance(db_field, (NepaliDateField, NepaliDateTimeField))
323
+ and nepkit_settings.ADMIN_DATEPICKER
324
+ ):
325
+ # Pass ne/en parameters from field to widget if they exist
326
+ widget_kwargs = {}
327
+ if hasattr(db_field, "ne"):
328
+ widget_kwargs["ne"] = db_field.ne
329
+ if hasattr(db_field, "en"):
330
+ widget_kwargs["en"] = db_field.en
331
+ kwargs.setdefault("widget", NepaliDatePickerWidget(**widget_kwargs))
179
332
 
180
333
  return super().formfield_for_dbfield(db_field, request, **kwargs)
181
334
 
182
335
  class Media:
183
- """
184
- Django admin ships jQuery as `django.jQuery` (not `window.jQuery`).
185
- The Nepali date picker library expects a global `jQuery`, so we bridge it.
186
- """
336
+ """Loads the Nepali Datepicker and bridging scripts."""
187
337
 
188
338
  css = {
189
339
  "all": (
@@ -201,9 +351,9 @@ class NepaliModelAdmin(NepaliAdminMixin, admin.ModelAdmin):
201
351
  )
202
352
 
203
353
 
204
- # Exporting for easy usage
205
354
  __all__ = [
206
355
  "NepaliDateFilter",
356
+ "NepaliMonthFilter",
207
357
  "format_nepali_date",
208
358
  "format_nepali_datetime",
209
359
  "NepaliAdminMixin",
django_nepkit/conf.py ADDED
@@ -0,0 +1,34 @@
1
+ from django.conf import settings
2
+
3
+ DEFAULTS = {
4
+ "DEFAULT_LANGUAGE": "en",
5
+ "DATE_INPUT_FORMATS": ["%Y-%m-%d", "%d/%m/%Y", "%d-%m-%Y"],
6
+ "ADMIN_DATEPICKER": True,
7
+ "TIME_FORMAT": 12,
8
+ "BS_DATE_FORMAT": "%Y-%m-%d",
9
+ "BS_DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S",
10
+ }
11
+
12
+
13
+ class NepkitSettings:
14
+ """Handles the global NEPKIT settings from your settings.py."""
15
+
16
+ def __init__(self, user_settings=None, defaults=None):
17
+ self._user_settings = user_settings or {}
18
+ self.defaults = defaults or DEFAULTS
19
+
20
+ def __getattr__(self, attr):
21
+ if attr not in self.defaults:
22
+ raise AttributeError(f"Invalid NEPKIT setting: '{attr}'")
23
+
24
+ try:
25
+ # Check if the user has a custom setting
26
+ val = self._user_settings[attr]
27
+ except KeyError:
28
+ # Use default value if not set
29
+ val = self.defaults[attr]
30
+
31
+ return val
32
+
33
+
34
+ nepkit_settings = NepkitSettings(getattr(settings, "NEPKIT", {}), DEFAULTS)
@@ -0,0 +1,129 @@
1
+ """
2
+ Constants for django-nepkit package.
3
+ Contains hardcoded values, word mappings, and configuration data.
4
+ """
5
+
6
+ # Nepali number words mapping (0-99)
7
+ NEPALI_ONES = [
8
+ "",
9
+ "एक",
10
+ "दुई",
11
+ "तीन",
12
+ "चार",
13
+ "पाँच",
14
+ "छ",
15
+ "सात",
16
+ "आठ",
17
+ "नौ",
18
+ "दश",
19
+ "एघार",
20
+ "बाह्र",
21
+ "तेह्र",
22
+ "चौध",
23
+ "पन्ध्र",
24
+ "सोह्र",
25
+ "सत्र",
26
+ "अठार",
27
+ "उन्नाइस",
28
+ "बीस",
29
+ "एकाइस",
30
+ "बाइस",
31
+ "तेईस",
32
+ "चौबीस",
33
+ "पच्चीस",
34
+ "छब्बीस",
35
+ "सत्ताइस",
36
+ "अठाइस",
37
+ "उनन्तीस",
38
+ "तीस",
39
+ "एकतीस",
40
+ "बत्तीस",
41
+ "तेत्तीस",
42
+ "चौंतीस",
43
+ "पैंतीस",
44
+ "छत्तीस",
45
+ "सैंतीस",
46
+ "अठतीस",
47
+ "उनन्चालीस",
48
+ "चालीस",
49
+ "एकचालीस",
50
+ "बयालीस",
51
+ "त्रिचालीस",
52
+ "चवालीस",
53
+ "पैंतालीस",
54
+ "छयालीस",
55
+ "सत्तालीस",
56
+ "अठचालीस",
57
+ "उनन्पचास",
58
+ "पचास",
59
+ "एकाउन्न",
60
+ "बाउन्न",
61
+ "त्रिपन्न",
62
+ "चउन्न",
63
+ "पचपन्न",
64
+ "छपन्न",
65
+ "सन्ताउन्न",
66
+ "अन्ठाउन्न",
67
+ "उनन्साठी",
68
+ "साठी",
69
+ "एकसाठी",
70
+ "बासट्ठी",
71
+ "त्रिसट्ठी",
72
+ "चौसट्ठी",
73
+ "पैंसट्ठी",
74
+ "छयसट्ठी",
75
+ "सतसट्ठी",
76
+ "अठसट्ठी",
77
+ "उनन्सत्तरी",
78
+ "सत्तरी",
79
+ "एकहत्तर",
80
+ "बाहत्तर",
81
+ "त्रिहत्तर",
82
+ "चौरहत्तर",
83
+ "पचहत्तर",
84
+ "छयहत्तर",
85
+ "सतहत्तर",
86
+ "अठहत्तर",
87
+ "उनन्असी",
88
+ "असी",
89
+ "एकासी",
90
+ "बयासी",
91
+ "त्रियासी",
92
+ "चौरासी",
93
+ "पचासी",
94
+ "छयासी",
95
+ "सतासी",
96
+ "अठासी",
97
+ "उनन्नब्बे",
98
+ "नब्बे",
99
+ "एकानब्बे",
100
+ "बयानब्बे",
101
+ "त्रियानब्बे",
102
+ "चौरानब्बे",
103
+ "पञ्चानब्बे",
104
+ "छ्यानब्बे",
105
+ "सन्तानब्बे",
106
+ "अन्ठानब्बे",
107
+ "उनन्सय",
108
+ ]
109
+
110
+ # Nepali number units and their corresponding names
111
+ NEPALI_UNITS = [
112
+ ("", ""),
113
+ (100, "सय"),
114
+ (1000, "हजार"),
115
+ (100000, "लाख"),
116
+ (10000000, "करोड"),
117
+ (1000000000, "अरब"),
118
+ (100000000000, "खरब"),
119
+ ]
120
+
121
+ # Placeholder text for location selects
122
+ PLACEHOLDERS = {
123
+ "province": {"ne": "प्रदेश छान्नुहोस्", "en": "Select Province"},
124
+ "district": {"ne": "जिल्ला छान्नुहोस्", "en": "Select District"},
125
+ "municipality": {"ne": "नगरपालिका छान्नुहोस्", "en": "Select Municipality"},
126
+ }
127
+
128
+ # Internal parameters to exclude from fallback logic
129
+ INTERNAL_PARAMS = ["ne", "en", "html"]