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