n2words 2.0.0 → 3.1.0

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 (335) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +86 -188
  3. package/dist/languages/am-Latn.js +3 -0
  4. package/dist/languages/am-Latn.js.map +1 -0
  5. package/dist/languages/am.js +3 -0
  6. package/dist/languages/am.js.map +1 -0
  7. package/dist/languages/ar.js +3 -0
  8. package/dist/languages/ar.js.map +1 -0
  9. package/dist/languages/az.js +3 -0
  10. package/dist/languages/az.js.map +1 -0
  11. package/dist/languages/bn.js +3 -0
  12. package/dist/languages/bn.js.map +1 -0
  13. package/dist/languages/cs.js +3 -0
  14. package/dist/languages/cs.js.map +1 -0
  15. package/dist/languages/da.js +3 -0
  16. package/dist/languages/da.js.map +1 -0
  17. package/dist/languages/de.js +3 -0
  18. package/dist/languages/de.js.map +1 -0
  19. package/dist/languages/el.js +3 -0
  20. package/dist/languages/el.js.map +1 -0
  21. package/dist/languages/en.js +3 -0
  22. package/dist/languages/en.js.map +1 -0
  23. package/dist/languages/es.js +3 -0
  24. package/dist/languages/es.js.map +1 -0
  25. package/dist/languages/fa.js +3 -0
  26. package/dist/languages/fa.js.map +1 -0
  27. package/dist/languages/fi.js +3 -0
  28. package/dist/languages/fi.js.map +1 -0
  29. package/dist/languages/fil.js +3 -0
  30. package/dist/languages/fil.js.map +1 -0
  31. package/dist/languages/fr-BE.js +3 -0
  32. package/dist/languages/fr-BE.js.map +1 -0
  33. package/dist/languages/fr.js +3 -0
  34. package/dist/languages/fr.js.map +1 -0
  35. package/dist/languages/gu.js +3 -0
  36. package/dist/languages/gu.js.map +1 -0
  37. package/dist/languages/ha.js +3 -0
  38. package/dist/languages/ha.js.map +1 -0
  39. package/dist/languages/hbo.js +3 -0
  40. package/dist/languages/hbo.js.map +1 -0
  41. package/dist/languages/he.js +3 -0
  42. package/dist/languages/he.js.map +1 -0
  43. package/dist/languages/hi.js +3 -0
  44. package/dist/languages/hi.js.map +1 -0
  45. package/dist/languages/hr.js +3 -0
  46. package/dist/languages/hr.js.map +1 -0
  47. package/dist/languages/hu.js +3 -0
  48. package/dist/languages/hu.js.map +1 -0
  49. package/dist/languages/id.js +3 -0
  50. package/dist/languages/id.js.map +1 -0
  51. package/dist/languages/it.js +3 -0
  52. package/dist/languages/it.js.map +1 -0
  53. package/dist/languages/ja.js +3 -0
  54. package/dist/languages/ja.js.map +1 -0
  55. package/dist/languages/ka.js +3 -0
  56. package/dist/languages/ka.js.map +1 -0
  57. package/dist/languages/kn.js +3 -0
  58. package/dist/languages/kn.js.map +1 -0
  59. package/dist/languages/ko.js +3 -0
  60. package/dist/languages/ko.js.map +1 -0
  61. package/dist/languages/lt.js +3 -0
  62. package/dist/languages/lt.js.map +1 -0
  63. package/dist/languages/lv.js +3 -0
  64. package/dist/languages/lv.js.map +1 -0
  65. package/dist/languages/mr.js +3 -0
  66. package/dist/languages/mr.js.map +1 -0
  67. package/dist/languages/ms.js +3 -0
  68. package/dist/languages/ms.js.map +1 -0
  69. package/dist/languages/nb.js +3 -0
  70. package/dist/languages/nb.js.map +1 -0
  71. package/dist/languages/nl.js +3 -0
  72. package/dist/languages/nl.js.map +1 -0
  73. package/dist/languages/pa.js +3 -0
  74. package/dist/languages/pa.js.map +1 -0
  75. package/dist/languages/pl.js +3 -0
  76. package/dist/languages/pl.js.map +1 -0
  77. package/dist/languages/pt.js +3 -0
  78. package/dist/languages/pt.js.map +1 -0
  79. package/dist/languages/ro.js +3 -0
  80. package/dist/languages/ro.js.map +1 -0
  81. package/dist/languages/ru.js +3 -0
  82. package/dist/languages/ru.js.map +1 -0
  83. package/dist/languages/sr-Cyrl.js +3 -0
  84. package/dist/languages/sr-Cyrl.js.map +1 -0
  85. package/dist/languages/sr-Latn.js +3 -0
  86. package/dist/languages/sr-Latn.js.map +1 -0
  87. package/dist/languages/sv.js +3 -0
  88. package/dist/languages/sv.js.map +1 -0
  89. package/dist/languages/sw.js +3 -0
  90. package/dist/languages/sw.js.map +1 -0
  91. package/dist/languages/ta.js +3 -0
  92. package/dist/languages/ta.js.map +1 -0
  93. package/dist/languages/te.js +3 -0
  94. package/dist/languages/te.js.map +1 -0
  95. package/dist/languages/th.js +3 -0
  96. package/dist/languages/th.js.map +1 -0
  97. package/dist/languages/tr.js +3 -0
  98. package/dist/languages/tr.js.map +1 -0
  99. package/dist/languages/uk.js +3 -0
  100. package/dist/languages/uk.js.map +1 -0
  101. package/dist/languages/ur.js +3 -0
  102. package/dist/languages/ur.js.map +1 -0
  103. package/dist/languages/vi.js +3 -0
  104. package/dist/languages/vi.js.map +1 -0
  105. package/dist/languages/yo.js +3 -0
  106. package/dist/languages/yo.js.map +1 -0
  107. package/dist/languages/zh-Hans.js +3 -0
  108. package/dist/languages/zh-Hans.js.map +1 -0
  109. package/dist/languages/zh-Hant.js +3 -0
  110. package/dist/languages/zh-Hant.js.map +1 -0
  111. package/dist/n2words.js +2 -2
  112. package/dist/n2words.js.map +1 -1
  113. package/lib/languages/am-Latn.d.ts +7 -0
  114. package/lib/languages/am-Latn.js +159 -0
  115. package/lib/languages/am.d.ts +7 -0
  116. package/lib/languages/am.js +159 -0
  117. package/lib/languages/ar.d.ts +14 -27
  118. package/lib/languages/ar.js +175 -129
  119. package/lib/languages/az.d.ts +4 -9
  120. package/lib/languages/az.js +166 -37
  121. package/lib/languages/bn.d.ts +4 -8
  122. package/lib/languages/bn.js +159 -124
  123. package/lib/languages/cs.d.ts +15 -85
  124. package/lib/languages/cs.js +293 -114
  125. package/lib/languages/da.d.ts +11 -12
  126. package/lib/languages/da.js +269 -101
  127. package/lib/languages/de.d.ts +14 -11
  128. package/lib/languages/de.js +305 -86
  129. package/lib/languages/el.d.ts +11 -11
  130. package/lib/languages/el.js +224 -78
  131. package/lib/languages/en.d.ts +14 -13
  132. package/lib/languages/en.js +228 -72
  133. package/lib/languages/es.d.ts +18 -12
  134. package/lib/languages/es.js +297 -103
  135. package/lib/languages/fa.d.ts +4 -44
  136. package/lib/languages/fa.js +112 -122
  137. package/lib/languages/fi.d.ts +14 -0
  138. package/lib/languages/fi.js +238 -0
  139. package/lib/languages/fil.d.ts +4 -13
  140. package/lib/languages/fil.js +196 -106
  141. package/lib/languages/fr-BE.d.ts +8 -8
  142. package/lib/languages/fr-BE.js +285 -19
  143. package/lib/languages/fr.d.ts +18 -12
  144. package/lib/languages/fr.js +339 -89
  145. package/lib/languages/gu.d.ts +4 -8
  146. package/lib/languages/gu.js +143 -125
  147. package/lib/languages/ha.d.ts +7 -0
  148. package/lib/languages/ha.js +225 -0
  149. package/lib/languages/hbo.d.ts +10 -110
  150. package/lib/languages/hbo.js +245 -214
  151. package/lib/languages/he.d.ts +10 -77
  152. package/lib/languages/he.js +231 -172
  153. package/lib/languages/hi.d.ts +4 -8
  154. package/lib/languages/hi.js +163 -124
  155. package/lib/languages/hr.d.ts +8 -77
  156. package/lib/languages/hr.js +200 -89
  157. package/lib/languages/hu.d.ts +4 -19
  158. package/lib/languages/hu.js +198 -119
  159. package/lib/languages/id.d.ts +4 -34
  160. package/lib/languages/id.js +166 -129
  161. package/lib/languages/it.d.ts +16 -34
  162. package/lib/languages/it.js +307 -97
  163. package/lib/languages/ja.d.ts +14 -14
  164. package/lib/languages/ja.js +221 -111
  165. package/lib/languages/ka.d.ts +17 -0
  166. package/lib/languages/ka.js +291 -0
  167. package/lib/languages/kn.d.ts +4 -8
  168. package/lib/languages/kn.js +143 -35
  169. package/lib/languages/ko.d.ts +11 -11
  170. package/lib/languages/ko.js +250 -49
  171. package/lib/languages/lt.d.ts +15 -67
  172. package/lib/languages/lt.js +287 -122
  173. package/lib/languages/lv.d.ts +15 -67
  174. package/lib/languages/lv.js +288 -106
  175. package/lib/languages/mr.d.ts +4 -8
  176. package/lib/languages/mr.js +143 -125
  177. package/lib/languages/ms.d.ts +4 -28
  178. package/lib/languages/ms.js +166 -116
  179. package/lib/languages/nb.d.ts +11 -9
  180. package/lib/languages/nb.js +272 -87
  181. package/lib/languages/nl.d.ts +23 -13
  182. package/lib/languages/nl.js +299 -133
  183. package/lib/languages/pa.d.ts +4 -8
  184. package/lib/languages/pa.js +151 -124
  185. package/lib/languages/pl.d.ts +19 -77
  186. package/lib/languages/pl.js +294 -87
  187. package/lib/languages/pt.d.ts +14 -26
  188. package/lib/languages/pt.js +272 -92
  189. package/lib/languages/ro.d.ts +15 -155
  190. package/lib/languages/ro.js +219 -235
  191. package/lib/languages/ru.d.ts +8 -82
  192. package/lib/languages/ru.js +239 -90
  193. package/lib/languages/sr-Cyrl.d.ts +8 -77
  194. package/lib/languages/sr-Cyrl.js +197 -89
  195. package/lib/languages/sr-Latn.d.ts +8 -77
  196. package/lib/languages/sr-Latn.js +197 -89
  197. package/lib/languages/sv.d.ts +11 -11
  198. package/lib/languages/sv.js +278 -74
  199. package/lib/languages/sw.d.ts +4 -36
  200. package/lib/languages/sw.js +133 -106
  201. package/lib/languages/ta.d.ts +4 -17
  202. package/lib/languages/ta.js +143 -202
  203. package/lib/languages/te.d.ts +4 -19
  204. package/lib/languages/te.js +133 -196
  205. package/lib/languages/th.d.ts +4 -14
  206. package/lib/languages/th.js +135 -91
  207. package/lib/languages/tr.d.ts +15 -9
  208. package/lib/languages/tr.js +245 -49
  209. package/lib/languages/uk.d.ts +8 -82
  210. package/lib/languages/uk.js +206 -78
  211. package/lib/languages/ur.d.ts +4 -8
  212. package/lib/languages/ur.js +151 -124
  213. package/lib/languages/vi.d.ts +14 -69
  214. package/lib/languages/vi.js +278 -129
  215. package/lib/languages/yo.d.ts +7 -0
  216. package/lib/languages/yo.js +303 -0
  217. package/lib/languages/zh-Hans.d.ts +8 -18
  218. package/lib/languages/zh-Hans.js +163 -92
  219. package/lib/languages/zh-Hant.d.ts +8 -18
  220. package/lib/languages/zh-Hant.js +181 -90
  221. package/lib/n2words.d.ts +55 -209
  222. package/lib/n2words.js +115 -530
  223. package/lib/utils/is-plain-object.d.ts +13 -0
  224. package/lib/utils/is-plain-object.js +17 -0
  225. package/lib/utils/parse-numeric.d.ts +17 -0
  226. package/lib/utils/parse-numeric.js +108 -0
  227. package/lib/utils/validate-options.d.ts +8 -0
  228. package/lib/utils/validate-options.js +16 -0
  229. package/package.json +26 -14
  230. package/dist/ArabicConverter.js +0 -3
  231. package/dist/ArabicConverter.js.map +0 -1
  232. package/dist/AzerbaijaniConverter.js +0 -3
  233. package/dist/AzerbaijaniConverter.js.map +0 -1
  234. package/dist/BanglaConverter.js +0 -3
  235. package/dist/BanglaConverter.js.map +0 -1
  236. package/dist/BiblicalHebrewConverter.js +0 -3
  237. package/dist/BiblicalHebrewConverter.js.map +0 -1
  238. package/dist/CroatianConverter.js +0 -3
  239. package/dist/CroatianConverter.js.map +0 -1
  240. package/dist/CzechConverter.js +0 -3
  241. package/dist/CzechConverter.js.map +0 -1
  242. package/dist/DanishConverter.js +0 -3
  243. package/dist/DanishConverter.js.map +0 -1
  244. package/dist/DutchConverter.js +0 -3
  245. package/dist/DutchConverter.js.map +0 -1
  246. package/dist/EnglishConverter.js +0 -3
  247. package/dist/EnglishConverter.js.map +0 -1
  248. package/dist/FilipinoConverter.js +0 -3
  249. package/dist/FilipinoConverter.js.map +0 -1
  250. package/dist/FrenchBelgiumConverter.js +0 -3
  251. package/dist/FrenchBelgiumConverter.js.map +0 -1
  252. package/dist/FrenchConverter.js +0 -3
  253. package/dist/FrenchConverter.js.map +0 -1
  254. package/dist/GermanConverter.js +0 -3
  255. package/dist/GermanConverter.js.map +0 -1
  256. package/dist/GreekConverter.js +0 -3
  257. package/dist/GreekConverter.js.map +0 -1
  258. package/dist/GujaratiConverter.js +0 -3
  259. package/dist/GujaratiConverter.js.map +0 -1
  260. package/dist/HebrewConverter.js +0 -3
  261. package/dist/HebrewConverter.js.map +0 -1
  262. package/dist/HindiConverter.js +0 -3
  263. package/dist/HindiConverter.js.map +0 -1
  264. package/dist/HungarianConverter.js +0 -3
  265. package/dist/HungarianConverter.js.map +0 -1
  266. package/dist/IndonesianConverter.js +0 -3
  267. package/dist/IndonesianConverter.js.map +0 -1
  268. package/dist/ItalianConverter.js +0 -3
  269. package/dist/ItalianConverter.js.map +0 -1
  270. package/dist/JapaneseConverter.js +0 -3
  271. package/dist/JapaneseConverter.js.map +0 -1
  272. package/dist/KannadaConverter.js +0 -3
  273. package/dist/KannadaConverter.js.map +0 -1
  274. package/dist/KoreanConverter.js +0 -3
  275. package/dist/KoreanConverter.js.map +0 -1
  276. package/dist/LatvianConverter.js +0 -3
  277. package/dist/LatvianConverter.js.map +0 -1
  278. package/dist/LithuanianConverter.js +0 -3
  279. package/dist/LithuanianConverter.js.map +0 -1
  280. package/dist/MalayConverter.js +0 -3
  281. package/dist/MalayConverter.js.map +0 -1
  282. package/dist/MarathiConverter.js +0 -3
  283. package/dist/MarathiConverter.js.map +0 -1
  284. package/dist/NorwegianBokmalConverter.js +0 -3
  285. package/dist/NorwegianBokmalConverter.js.map +0 -1
  286. package/dist/PersianConverter.js +0 -3
  287. package/dist/PersianConverter.js.map +0 -1
  288. package/dist/PolishConverter.js +0 -3
  289. package/dist/PolishConverter.js.map +0 -1
  290. package/dist/PortugueseConverter.js +0 -3
  291. package/dist/PortugueseConverter.js.map +0 -1
  292. package/dist/PunjabiConverter.js +0 -3
  293. package/dist/PunjabiConverter.js.map +0 -1
  294. package/dist/RomanianConverter.js +0 -3
  295. package/dist/RomanianConverter.js.map +0 -1
  296. package/dist/RussianConverter.js +0 -3
  297. package/dist/RussianConverter.js.map +0 -1
  298. package/dist/SerbianCyrillicConverter.js +0 -3
  299. package/dist/SerbianCyrillicConverter.js.map +0 -1
  300. package/dist/SerbianLatinConverter.js +0 -3
  301. package/dist/SerbianLatinConverter.js.map +0 -1
  302. package/dist/SimplifiedChineseConverter.js +0 -3
  303. package/dist/SimplifiedChineseConverter.js.map +0 -1
  304. package/dist/SpanishConverter.js +0 -3
  305. package/dist/SpanishConverter.js.map +0 -1
  306. package/dist/SwahiliConverter.js +0 -3
  307. package/dist/SwahiliConverter.js.map +0 -1
  308. package/dist/SwedishConverter.js +0 -3
  309. package/dist/SwedishConverter.js.map +0 -1
  310. package/dist/TamilConverter.js +0 -3
  311. package/dist/TamilConverter.js.map +0 -1
  312. package/dist/TeluguConverter.js +0 -3
  313. package/dist/TeluguConverter.js.map +0 -1
  314. package/dist/ThaiConverter.js +0 -3
  315. package/dist/ThaiConverter.js.map +0 -1
  316. package/dist/TraditionalChineseConverter.js +0 -3
  317. package/dist/TraditionalChineseConverter.js.map +0 -1
  318. package/dist/TurkishConverter.js +0 -3
  319. package/dist/TurkishConverter.js.map +0 -1
  320. package/dist/UkrainianConverter.js +0 -3
  321. package/dist/UkrainianConverter.js.map +0 -1
  322. package/dist/UrduConverter.js +0 -3
  323. package/dist/UrduConverter.js.map +0 -1
  324. package/dist/VietnameseConverter.js +0 -3
  325. package/dist/VietnameseConverter.js.map +0 -1
  326. package/lib/classes/abstract-language.d.ts +0 -178
  327. package/lib/classes/abstract-language.js +0 -268
  328. package/lib/classes/greedy-scale-language.d.ts +0 -109
  329. package/lib/classes/greedy-scale-language.js +0 -201
  330. package/lib/classes/slavic-language.d.ts +0 -148
  331. package/lib/classes/slavic-language.js +0 -281
  332. package/lib/classes/south-asian-language.d.ts +0 -70
  333. package/lib/classes/south-asian-language.js +0 -154
  334. package/lib/classes/turkic-language.d.ts +0 -26
  335. package/lib/classes/turkic-language.js +0 -59
@@ -1,143 +1,322 @@
1
- import { SlavicLanguage } from '../classes/slavic-language.js'
2
-
3
1
  /**
4
- * Czech language converter.
2
+ * Czech language converter - Functional Implementation
3
+ *
4
+ * Self-contained module with its own input validation, ready for subpath exports.
5
5
  *
6
- * Supports:
7
- * - Three-form pluralization (one/few/many)
8
- * - Dynamic decimal separator (celá/celé/celých)
9
- * - Gender agreement in number words
6
+ * Czech-specific rules:
7
+ * - Three-form pluralization: 1 = singular, 2-4 = few, 5+ = many
8
+ * - Irregular hundreds: sto, dvě stě, tři sta, čtyři sta, pět set...
9
+ * - Gender: dva (masc) vs dvě (fem) for 2
10
+ * - Omit "one" before scale words: "tisíc" not "jedna tisíc"
11
+ * - Dynamic decimal separator: celá/celé/celých based on integer
12
+ */
13
+
14
+ import { parseNumericValue } from '../utils/parse-numeric.js'
15
+
16
+ // ============================================================================
17
+ // Vocabulary (module-level constants)
18
+ // ============================================================================
19
+
20
+ // Ones words (masculine form)
21
+ const ONES = ['', 'jedna', 'dva', 'tři', 'čtyři', 'pět', 'šest', 'sedm', 'osm', 'devět']
22
+
23
+ // Teens (10-19)
24
+ const TEENS = ['deset', 'jedenáct', 'dvanáct', 'třináct', 'čtrnáct', 'patnáct', 'šestnáct', 'sedmnáct', 'osmnáct', 'devatenáct']
25
+
26
+ // Tens (20-90)
27
+ const TENS = ['', '', 'dvacet', 'třicet', 'čtyřicet', 'padesát', 'šedesát', 'sedmdesát', 'osmdesát', 'devadesát']
28
+
29
+ // Irregular hundreds
30
+ const HUNDREDS = ['', 'sto', 'dvě stě', 'tři sta', 'čtyři sta', 'pět set', 'šest set', 'sedm set', 'osm set', 'devět set']
31
+
32
+ // Scale plural forms [singular, few (2-4), many (5+)]
33
+ const PLURAL_FORMS = {
34
+ 1: ['tisíc', 'tisíce', 'tisíc'], // 10^3
35
+ 2: ['milion', 'miliony', 'milionů'], // 10^6
36
+ 3: ['miliarda', 'miliardy', 'miliard'], // 10^9
37
+ 4: ['bilion', 'biliony', 'bilionů'], // 10^12
38
+ 5: ['biliarda', 'biliardy', 'biliard'], // 10^15
39
+ 6: ['trilion', 'triliony', 'trilionů'], // 10^18
40
+ 7: ['triliarda', 'triliardy', 'triliard'], // 10^21
41
+ 8: ['kvadrilion', 'kvadriliony', 'kvadrilionů'], // 10^24
42
+ 9: ['kvadriliarda', 'kvadriliardy', 'kvadriliard'] // 10^27
43
+ }
44
+
45
+ const ZERO = 'nula'
46
+ const NEGATIVE = 'mínus'
47
+
48
+ // ============================================================================
49
+ // Segment Building
50
+ // ============================================================================
51
+
52
+ /**
53
+ * Builds segment word for 0-999 (masculine, default form).
10
54
  */
11
- export class Czech extends SlavicLanguage {
12
- negativeWord = 'mínus'
13
- zeroWord = 'nula'
14
-
15
- onesWords = {
16
- 1: 'jedna',
17
- 2: 'dva',
18
- 3: 'tři',
19
- 4: 'čtyři',
20
- 5: 'pět',
21
- 6: 'šest',
22
- 7: 'sedm',
23
- 8: 'osm',
24
- 9: 'devět'
55
+ function buildSegment (n) {
56
+ if (n === 0) return ''
57
+
58
+ const ones = n % 10
59
+ const tens = Math.floor(n / 10) % 10
60
+ const hundreds = Math.floor(n / 100)
61
+
62
+ const parts = []
63
+
64
+ // Hundreds (irregular)
65
+ if (hundreds > 0) {
66
+ parts.push(HUNDREDS[hundreds])
25
67
  }
26
68
 
27
- onesFeminineWords = {
28
- 1: 'jedna',
29
- 2: 'dvě',
30
- 3: 'tři',
31
- 4: 'čtyři',
32
- 5: 'pět',
33
- 6: 'šest',
34
- 7: 'sedm',
35
- 8: 'osm',
36
- 9: 'devět'
69
+ // Tens and ones
70
+ if (tens === 1) {
71
+ // Teens
72
+ parts.push(TEENS[ones])
73
+ } else if (tens >= 2) {
74
+ parts.push(TENS[tens])
75
+ if (ones > 0) {
76
+ parts.push(ONES[ones])
77
+ }
78
+ } else if (ones > 0) {
79
+ parts.push(ONES[ones])
37
80
  }
38
81
 
39
- teensWords = {
40
- 0: 'deset',
41
- 1: 'jedenáct',
42
- 2: 'dvanáct',
43
- 3: 'třináct',
44
- 4: 'čtrnáct',
45
- 5: 'patnáct',
46
- 6: 'šestnáct',
47
- 7: 'sedmnáct',
48
- 8: 'osmnáct',
49
- 9: 'devatenáct'
82
+ return parts.join(' ')
83
+ }
84
+
85
+ /**
86
+ * Builds segment word for 0-999 with feminine hundreds.
87
+ * Hundreds use irregular forms (dvě stě, tři sta) but ones remain masculine.
88
+ */
89
+ function buildSegmentWithHundreds (n) {
90
+ if (n === 0) return ''
91
+
92
+ const ones = n % 10
93
+ const tens = Math.floor(n / 10) % 10
94
+ const hundreds = Math.floor(n / 100)
95
+
96
+ const parts = []
97
+
98
+ // Hundreds use the irregular HUNDREDS array (already has "dvě stě" etc.)
99
+ if (hundreds > 0) {
100
+ parts.push(HUNDREDS[hundreds])
101
+ }
102
+
103
+ // Tens and ones use masculine form
104
+ if (tens === 1) {
105
+ parts.push(TEENS[ones])
106
+ } else if (tens >= 2) {
107
+ parts.push(TENS[tens])
108
+ if (ones > 0) {
109
+ parts.push(ONES[ones]) // masculine
110
+ }
111
+ } else if (ones > 0) {
112
+ parts.push(ONES[ones]) // masculine
113
+ }
114
+
115
+ return parts.join(' ')
116
+ }
117
+
118
+ // ============================================================================
119
+ // Helper Functions
120
+ // ============================================================================
121
+
122
+ /**
123
+ * Czech pluralization: 1 = singular, 2-4 = few, else = many.
124
+ * Teens (11-19) always use "many" form.
125
+ *
126
+ * @param {bigint} n - The number
127
+ * @param {string[]} forms - [singular, few, many]
128
+ * @returns {string} The appropriate form
129
+ */
130
+ function pluralize (n, forms) {
131
+ if (n === 1n) return forms[0]
132
+
133
+ const lastDigit = n % 10n
134
+ const lastTwoDigits = n % 100n
135
+
136
+ // 2-4, but not 12-14 (teens use "many")
137
+ if (lastDigit >= 2n && lastDigit <= 4n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {
138
+ return forms[1]
139
+ }
140
+
141
+ return forms[2]
142
+ }
143
+
144
+ /**
145
+ * Gets the decimal separator word based on integer part.
146
+ * celá (0-1), celé (2-4), celých (5+)
147
+ */
148
+ function getDecimalSeparator (integerPart) {
149
+ if (integerPart === 0n || integerPart === 1n) {
150
+ return 'celá'
151
+ } else if (integerPart >= 2n && integerPart <= 4n) {
152
+ return 'celé'
153
+ } else {
154
+ return 'celých'
50
155
  }
156
+ }
51
157
 
52
- twentiesWords = {
53
- 2: 'dvacet',
54
- 3: 'třicet',
55
- 4: 'čtyřicet',
56
- 5: 'padesát',
57
- 6: 'šedesát',
58
- 7: 'sedmdesát',
59
- 8: 'osmdesát',
60
- 9: 'devadesát'
158
+ // ============================================================================
159
+ // Conversion Functions
160
+ // ============================================================================
161
+
162
+ /**
163
+ * Converts a non-negative integer to Czech words.
164
+ *
165
+ * @param {bigint} n - Non-negative integer to convert
166
+ * @returns {string} Czech words
167
+ */
168
+ function integerToWords (n) {
169
+ if (n === 0n) return ZERO
170
+
171
+ // Fast path: numbers < 1000
172
+ if (n < 1000n) {
173
+ return buildSegment(Number(n))
61
174
  }
62
175
 
63
- hundredsWords = {
64
- 1: 'sto',
65
- 2: 'dvě stě',
66
- 3: 'tři sta',
67
- 4: 'čtyři sta',
68
- 5: 'pět set',
69
- 6: 'šest set',
70
- 7: 'sedm set',
71
- 8: 'osm set',
72
- 9: 'devět set'
176
+ // Fast path: numbers < 1,000,000 (thousands)
177
+ if (n < 1_000_000n) {
178
+ const thousands = n / 1000n
179
+ const remainder = Number(n % 1000n)
180
+
181
+ const scaleWord = pluralize(thousands, PLURAL_FORMS[1])
182
+
183
+ let result
184
+ if (thousands === 1n) {
185
+ // Omit "one" before tisíc
186
+ result = scaleWord
187
+ } else {
188
+ result = buildSegment(Number(thousands)) + ' ' + scaleWord
189
+ }
190
+
191
+ if (remainder > 0) {
192
+ // Use form with irregular hundreds (for "dvě stě" etc.)
193
+ result += ' ' + buildSegmentWithHundreds(remainder)
194
+ }
195
+
196
+ return result
73
197
  }
74
198
 
75
- pluralForms = {
76
- 1: ['tisíc', 'tisíce', 'tisíc'], // 10^ 3
77
- 2: ['milion', 'miliony', 'milionů'], // 10^ 6
78
- 3: ['miliarda', 'miliardy', 'miliard'], // 10^ 9
79
- 4: ['bilion', 'biliony', 'bilionů'], // 10^ 12
80
- 5: ['biliarda', 'biliardy', 'biliard'], // 10^ 15
81
- 6: ['trilion', 'triliony', 'trilionů'], // 10^ 18
82
- 7: ['triliarda', 'triliardy', 'triliard'], // 10^ 21
83
- 8: ['kvadrilion', 'kvadriliony', 'kvadrilionů'], // 10^ 24
84
- 9: ['kvadriliarda', 'kvadriliardy', 'kvadriliard'], // 10^ 27
85
- 10: ['quintillion', 'quintilliony', 'quintillionů'] // 10^ 30
199
+ // For numbers >= 1,000,000, use scale decomposition
200
+ return buildLargeNumberWords(n)
201
+ }
202
+
203
+ /**
204
+ * Builds words for numbers >= 1,000,000.
205
+ * Uses BigInt division for faster segment extraction.
206
+ *
207
+ * @param {bigint} n - Number >= 1,000,000
208
+ * @returns {string} Czech words
209
+ */
210
+ function buildLargeNumberWords (n) {
211
+ // Extract segments using BigInt division (faster than string slicing)
212
+ // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
213
+ const segmentValues = []
214
+ let temp = n
215
+ while (temp > 0n) {
216
+ segmentValues.push(temp % 1000n)
217
+ temp = temp / 1000n
86
218
  }
87
219
 
88
- /**
89
- * Czech omits "one" before scale words.
90
- * e.g., 1000 is "tisíc" not "jedna tisíc"
91
- */
92
- omitOneBeforeScale = true
93
-
94
- /**
95
- * Cached integer part for decimal separator word selection.
96
- * Set by toWords() before calling super.toWords().
97
- * @private
98
- */
99
- #integerPart = 0n
100
-
101
- /** Returns decimal separator word based on integer part (celá/celé/celých). */
102
- get decimalSeparatorWord () {
103
- if (this.#integerPart === 0n || this.#integerPart === 1n) {
104
- return 'celá'
105
- } else if (this.#integerPart >= 2n && this.#integerPart <= 4n) {
106
- return 'celé'
220
+ // Build result string directly
221
+ let result = ''
222
+
223
+ for (let i = segmentValues.length - 1; i >= 0; i--) {
224
+ const segment = segmentValues[i]
225
+ if (segment === 0n) continue
226
+
227
+ if (result) result += ' '
228
+
229
+ if (i === 0) {
230
+ // Units segment (no scale word) - use form with irregular hundreds
231
+ result += buildSegmentWithHundreds(Number(segment))
107
232
  } else {
108
- return 'celých'
233
+ // Scale word needed
234
+ const forms = PLURAL_FORMS[i]
235
+ if (forms) {
236
+ const scaleWord = pluralize(segment, forms)
237
+
238
+ if (segment === 1n) {
239
+ // Omit "one" before scale words
240
+ result += scaleWord
241
+ } else {
242
+ // Use masculine form for multiplier before scale words
243
+ result += buildSegment(Number(segment)) + ' ' + scaleWord
244
+ }
245
+ } else {
246
+ // Fallback for very large scales without defined forms
247
+ result += buildSegment(Number(segment))
248
+ }
109
249
  }
110
250
  }
111
251
 
112
- constructor (options = {}) {
113
- super(options)
252
+ return result
253
+ }
114
254
 
115
- // Remove the inherited decimalSeparatorWord property so our getter works
116
- delete this.decimalSeparatorWord
255
+ /**
256
+ * Converts decimal digits to Czech words.
257
+ *
258
+ * @param {string} decimalPart - Decimal digits (without the point)
259
+ * @returns {string} Czech words for decimal part
260
+ */
261
+ function decimalPartToWords (decimalPart) {
262
+ let result = ''
263
+
264
+ // Handle leading zeros
265
+ let i = 0
266
+ while (i < decimalPart.length && decimalPart[i] === '0') {
267
+ if (result) result += ' '
268
+ result += ZERO
269
+ i++
117
270
  }
118
271
 
119
- /**
120
- * Override toWords to cache integer part before decimal separator is accessed.
121
- */
122
- toWords (isNegative, integerPart, decimalPart) {
123
- this.#integerPart = integerPart
124
- return super.toWords(isNegative, integerPart, decimalPart)
272
+ // Convert remainder as a single number
273
+ const remainder = decimalPart.slice(i)
274
+ if (remainder) {
275
+ if (result) result += ' '
276
+ result += integerToWords(BigInt(remainder))
125
277
  }
126
278
 
127
- /** Selects Czech plural form: 1 = singular, 2-4 = few, else = many. */
128
- pluralize (n, forms) {
129
- if (n === 1n) {
130
- return forms[0]
131
- }
279
+ return result
280
+ }
132
281
 
133
- // Check if n is in the "few" form range (2-4, 22-24, 32-34, etc., excluding teens)
134
- const lastDigit = n % 10n
135
- const lastTwoDigits = n % 100n
282
+ /**
283
+ * Converts a numeric value to Czech words.
284
+ *
285
+ * This is the main public API. It accepts any valid numeric input
286
+ * (number, string, or bigint) and handles parsing internally.
287
+ *
288
+ * @param {number | string | bigint} value - The numeric value to convert
289
+ * @returns {string} The number in Czech words
290
+ * @throws {TypeError} If value is not a valid numeric type
291
+ * @throws {Error} If value is not a valid number format
292
+ *
293
+ * @example
294
+ * toWords(21) // 'dvacet jedna'
295
+ * toWords(1000) // 'tisíc'
296
+ * toWords(2000) // 'dva tisíce'
297
+ * toWords(5000) // 'pět tisíc'
298
+ */
299
+ function toWords (value) {
300
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
136
301
 
137
- if (lastDigit > 1n && lastDigit < 5n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {
138
- return forms[1]
139
- }
302
+ let result = ''
303
+
304
+ if (isNegative) {
305
+ result = NEGATIVE + ' '
306
+ }
307
+
308
+ result += integerToWords(integerPart)
140
309
 
141
- return forms[2]
310
+ if (decimalPart) {
311
+ const separator = getDecimalSeparator(integerPart)
312
+ result += ' ' + separator + ' ' + decimalPartToWords(decimalPart)
142
313
  }
314
+
315
+ return result
143
316
  }
317
+
318
+ // ============================================================================
319
+ // Public API
320
+ // ============================================================================
321
+
322
+ export { toWords }
@@ -1,15 +1,14 @@
1
1
  /**
2
- * Danish language converter.
2
+ * Converts a numeric value to Danish words.
3
3
  *
4
- * Supports:
5
- * - Vigesimal (base-20) number system
6
- * - Units-before-tens ordering (e.g., "tre-og-tyve" = 23)
7
- * - Optional ordinal numbers via ordFlag option
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Danish words
6
+ * @throws {TypeError} If value is not a valid numeric type
7
+ * @throws {Error} If value is not a valid number format
8
+ *
9
+ * @example
10
+ * toWords(21) // 'enogtyve'
11
+ * toWords(1000) // 'ettusind'
12
+ * toWords(1000000) // 'en millioner'
8
13
  */
9
- export class Danish extends GreedyScaleLanguage {
10
- constructor(options?: {});
11
- scaleWords: (string | bigint)[][];
12
- /** Combines two word-sets with Danish vigesimal and reversal rules. */
13
- combineWordSets(preceding: any, following: any): any;
14
- }
15
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
14
+ export function toWords(value: number | string | bigint): string;