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,110 +1,330 @@
1
- import { SlavicLanguage } from '../classes/slavic-language.js'
2
-
3
1
  /**
4
- * Polish language converter.
2
+ * Polish 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
- * - Polish-specific declension patterns
9
- * - Distinctive Polish phonology and orthography
10
+ * Polish-specific rules (handled in precomputation):
11
+ * - Three-form pluralization: 1 = singular, 2-4 = few, 5+ = many
12
+ * - Gender agreement (masculine/feminine for numbers < 1000)
13
+ * - Omit "jeden" before scale words (tysiąc, milion, etc.)
14
+ * - Irregular hundreds: dwieście, trzysta, czterysta, pięćset...
15
+ * - Long scale with -ard forms: miliard, biliard, tryliard
10
16
  */
11
- export class Polish extends SlavicLanguage {
12
- negativeWord = 'minus'
13
- decimalSeparatorWord = 'przecinek'
14
- zeroWord = 'zero'
15
-
16
- onesWords = {
17
- 1: 'jeden',
18
- 2: 'dwa',
19
- 3: 'trzy',
20
- 4: 'cztery',
21
- 5: 'pięć',
22
- 6: 'sześć',
23
- 7: 'siedem',
24
- 8: 'osiem',
25
- 9: 'dziewięć'
17
+
18
+ import { parseNumericValue } from '../utils/parse-numeric.js'
19
+ import { validateOptions } from '../utils/validate-options.js'
20
+
21
+ // ============================================================================
22
+ // Vocabulary (module-level constants)
23
+ // ============================================================================
24
+
25
+ const ONES_MASC = ['', 'jeden', 'dwa', 'trzy', 'cztery', 'pięć', 'sześć', 'siedem', 'osiem', 'dziewięć']
26
+ const ONES_FEM = ['', 'jedna', 'dwie', 'trzy', 'cztery', 'pięć', 'sześć', 'siedem', 'osiem', 'dziewięć']
27
+
28
+ const TEENS = ['dziesięć', 'jedenaście', 'dwanaście', 'trzynaście', 'czternaście', 'piętnaście', 'szesnaście', 'siedemnaście', 'osiemnaście', 'dziewiętnaście']
29
+
30
+ const TENS = ['', '', 'dwadzieścia', 'trzydzieści', 'czterdzieści', 'pięćdziesiąt', 'sześćdziesiąt', 'siedemdziesiąt', 'osiemdziesiąt', 'dziewięćdziesiąt']
31
+
32
+ // Irregular hundreds
33
+ const HUNDREDS = ['', 'sto', 'dwieście', 'trzysta', 'czterysta', 'pięćset', 'sześćset', 'siedemset', 'osiemset', 'dziewięćset']
34
+
35
+ // Scale words: [singular, few (2-4), many (5+)]
36
+ const PLURAL_FORMS = {
37
+ 1: ['tysiąc', 'tysiące', 'tysięcy'],
38
+ 2: ['milion', 'miliony', 'milionów'],
39
+ 3: ['miliard', 'miliardy', 'miliardów'],
40
+ 4: ['bilion', 'biliony', 'bilionów'],
41
+ 5: ['biliard', 'biliardy', 'biliardów'],
42
+ 6: ['trylion', 'tryliony', 'trylionów'],
43
+ 7: ['tryliard', 'tryliardy', 'tryliardów'],
44
+ 8: ['kwadrylion', 'kwadryliony', 'kwadrylionów'],
45
+ 9: ['kwaryliard', 'kwadryliardy', 'kwadryliardów'],
46
+ 10: ['kwintylion', 'kwintyliony', 'kwintylionów']
47
+ }
48
+
49
+ const ZERO = 'zero'
50
+ const NEGATIVE = 'minus'
51
+ const DECIMAL_SEP = 'przecinek'
52
+
53
+ // ============================================================================
54
+ // Precomputed Lookup Tables (built once at module load)
55
+ // ============================================================================
56
+
57
+ /**
58
+ * Builds segment word for 0-999 (masculine form).
59
+ * @param {number} n - Segment value
60
+ * @returns {string} Polish word
61
+ */
62
+ function buildSegment (n) {
63
+ if (n === 0) return ''
64
+
65
+ const ones = n % 10
66
+ const tens = Math.floor(n / 10) % 10
67
+ const hundreds = Math.floor(n / 100)
68
+
69
+ const parts = []
70
+
71
+ // Hundreds
72
+ if (hundreds > 0) {
73
+ parts.push(HUNDREDS[hundreds])
26
74
  }
27
75
 
28
- onesFeminineWords = {
29
- 1: 'jedna',
30
- 2: 'dwie',
31
- 3: 'trzy',
32
- 4: 'cztery',
33
- 5: 'pięć',
34
- 6: 'sześć',
35
- 7: 'siedem',
36
- 8: 'osiem',
37
- 9: 'dziewięć'
76
+ // Tens and ones
77
+ if (tens === 1) {
78
+ // Teens (10-19)
79
+ parts.push(TEENS[ones])
80
+ } else {
81
+ if (tens >= 2) {
82
+ parts.push(TENS[tens])
83
+ }
84
+ if (ones > 0) {
85
+ parts.push(ONES_MASC[ones])
86
+ }
38
87
  }
39
88
 
40
- teensWords = {
41
- 0: 'dziesięć',
42
- 1: 'jedenaście',
43
- 2: 'dwanaście',
44
- 3: 'trzynaście',
45
- 4: 'czternaście',
46
- 5: 'piętnaście',
47
- 6: 'szesnaście',
48
- 7: 'siedemnaście',
49
- 8: 'osiemnaście',
50
- 9: 'dziewiętnaście'
89
+ return parts.join(' ')
90
+ }
91
+
92
+ /**
93
+ * Builds segment word for 0-999 (feminine form - only differs in ones).
94
+ * @param {number} n - Segment value
95
+ * @returns {string} Polish word
96
+ */
97
+ function buildSegmentFeminine (n) {
98
+ if (n === 0) return ''
99
+
100
+ const ones = n % 10
101
+ const tens = Math.floor(n / 10) % 10
102
+ const hundreds = Math.floor(n / 100)
103
+
104
+ const parts = []
105
+
106
+ // Hundreds
107
+ if (hundreds > 0) {
108
+ parts.push(HUNDREDS[hundreds])
51
109
  }
52
110
 
53
- twentiesWords = {
54
- 2: 'dwadzieścia',
55
- 3: 'trzydzieści',
56
- 4: 'czterdzieści',
57
- 5: 'pięćdziesiąt',
58
- 6: 'sześćdziesiąt',
59
- 7: 'siedemdziesiąt',
60
- 8: 'osiemdziesiąt',
61
- 9: 'dziewięćdziesiąt'
111
+ // Tens and ones - feminine for ones only
112
+ if (tens === 1) {
113
+ parts.push(TEENS[ones])
114
+ } else {
115
+ if (tens >= 2) {
116
+ parts.push(TENS[tens])
117
+ }
118
+ if (ones > 0) {
119
+ parts.push(ONES_FEM[ones])
120
+ }
121
+ }
122
+
123
+ return parts.join(' ')
124
+ }
125
+
126
+ // Precompute all 1000 segment words (0-999)
127
+ const SEGMENTS_MASC = new Array(1000)
128
+ const SEGMENTS_FEM = new Array(1000)
129
+ for (let i = 0; i < 1000; i++) {
130
+ SEGMENTS_MASC[i] = buildSegment(i)
131
+ SEGMENTS_FEM[i] = buildSegmentFeminine(i)
132
+ }
133
+
134
+ // ============================================================================
135
+ // Helper Functions
136
+ // ============================================================================
137
+
138
+ /**
139
+ * Polish pluralization: 1 = singular, 2-4 = few, else = many.
140
+ * Special case: 11-19 always use many form.
141
+ *
142
+ * @param {bigint} n - Number to pluralize
143
+ * @param {string[]} forms - [singular, few, many]
144
+ * @returns {string} Correct plural form
145
+ */
146
+ function pluralize (n, forms) {
147
+ if (n === 1n) {
148
+ return forms[0]
62
149
  }
63
150
 
64
- hundredsWords = {
65
- 1: 'sto',
66
- 2: 'dwieście',
67
- 3: 'trzysta',
68
- 4: 'czterysta',
69
- 5: 'pięćset',
70
- 6: 'sześćset',
71
- 7: 'siedemset',
72
- 8: 'osiemset',
73
- 9: 'dziewięćset'
151
+ const lastDigit = n % 10n
152
+ const lastTwoDigits = n % 100n
153
+
154
+ // Teens (11-19) always use many form
155
+ // 2-4 use few form (but not 12-14)
156
+ if (lastDigit >= 2n && lastDigit <= 4n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {
157
+ return forms[1]
74
158
  }
75
159
 
76
- pluralForms = {
77
- 1: ['tysiąc', 'tysiące', 'tysięcy'], // 10^ 3
78
- 2: ['milion', 'miliony', 'milionów'], // 10^ 6
79
- 3: ['miliard', 'miliardy', 'miliardów'], // 10^ 9
80
- 4: ['bilion', 'biliony', 'bilionów'], // 10^ 12
81
- 5: ['biliard', 'biliardy', 'biliardów'], // 10^ 15
82
- 6: ['trylion', 'tryliony', 'trylionów'], // 10^ 18
83
- 7: ['tryliard', 'tryliardy', 'tryliardów'], // 10^ 21
84
- 8: ['kwadrylion', 'kwadryliony', 'kwadrylionów'], // 10^ 24
85
- 9: ['kwaryliard', 'kwadryliardy', 'kwadryliardów'], // 10^ 27
86
- 10: ['kwintylion', 'kwintyliony', 'kwintylionów'] // 10^ 30
160
+ return forms[2]
161
+ }
162
+
163
+ // ============================================================================
164
+ // Conversion Functions
165
+ // ============================================================================
166
+
167
+ /**
168
+ * Converts a non-negative integer to Polish words.
169
+ *
170
+ * @param {bigint} n - Non-negative integer to convert
171
+ * @param {Object} options - Conversion options
172
+ * @returns {string} Polish words
173
+ */
174
+ function integerToWords (n, options = {}) {
175
+ if (n === 0n) return ZERO
176
+
177
+ // Fast path: numbers < 1000 (direct lookup)
178
+ if (n < 1000n) {
179
+ const segments = options.gender === 'feminine' ? SEGMENTS_FEM : SEGMENTS_MASC
180
+ return segments[Number(n)]
87
181
  }
88
182
 
89
- /**
90
- * Polish omits "one" before scale words.
91
- * e.g., 1000 is "tysiąc" not "jeden tysiąc"
92
- */
93
- omitOneBeforeScale = true
183
+ // Fast path: numbers < 1,000,000 (thousands)
184
+ if (n < 1_000_000n) {
185
+ const thousands = Number(n / 1000n)
186
+ const remainder = Number(n % 1000n)
187
+
188
+ const scaleWord = pluralize(BigInt(thousands), PLURAL_FORMS[1])
189
+
190
+ let result
191
+ if (thousands === 1) {
192
+ // Omit "jeden" before tysiąc
193
+ result = scaleWord
194
+ } else {
195
+ result = SEGMENTS_MASC[thousands] + ' ' + scaleWord
196
+ }
94
197
 
95
- /** Implements Polish-specific three-form pluralization rules. */
96
- pluralize (n, forms) {
97
- if (n === 1n) {
98
- return forms[0]
198
+ if (remainder > 0) {
199
+ result += ' ' + SEGMENTS_MASC[remainder]
99
200
  }
100
201
 
101
- const lastDigit = n % 10n
102
- const lastTwoDigits = n % 100n
202
+ return result
203
+ }
204
+
205
+ // For numbers >= 1,000,000, use scale decomposition
206
+ return buildLargeNumberWords(n, options)
207
+ }
103
208
 
104
- if (lastDigit < 5n && lastDigit > 1n && (lastTwoDigits < 10n || lastTwoDigits > 20n)) {
105
- return forms[1]
209
+ /**
210
+ * Builds words for numbers >= 1,000,000.
211
+ * Uses BigInt division for faster segment extraction.
212
+ *
213
+ * @param {bigint} n - Number >= 1,000,000
214
+ * @param {Object} options - Conversion options
215
+ * @returns {string} Polish words
216
+ */
217
+ function buildLargeNumberWords (n, options) {
218
+ // Extract segments using BigInt division (faster than string slicing)
219
+ // Segments stored least-significant first (index 0 = ones, 1 = thousands, etc.)
220
+ const segmentValues = []
221
+ let temp = n
222
+ while (temp > 0n) {
223
+ segmentValues.push(temp % 1000n)
224
+ temp = temp / 1000n
225
+ }
226
+
227
+ // Build result string directly
228
+ let result = ''
229
+
230
+ for (let i = segmentValues.length - 1; i >= 0; i--) {
231
+ const segment = segmentValues[i]
232
+ if (segment === 0n) continue
233
+
234
+ const segmentWord = SEGMENTS_MASC[Number(segment)]
235
+
236
+ if (result) result += ' '
237
+
238
+ if (i === 0) {
239
+ // Units segment
240
+ result += segmentWord
241
+ } else {
242
+ // Scale word needed
243
+ const forms = PLURAL_FORMS[i]
244
+ if (forms) {
245
+ const scaleWord = pluralize(segment, forms)
246
+
247
+ if (segment === 1n) {
248
+ // Omit "jeden" before scale words
249
+ result += scaleWord
250
+ } else {
251
+ result += segmentWord + ' ' + scaleWord
252
+ }
253
+ }
106
254
  }
255
+ }
107
256
 
108
- return forms[2]
257
+ return result
258
+ }
259
+
260
+ /**
261
+ * Converts decimal digits to Polish words.
262
+ *
263
+ * @param {string} decimalPart - Decimal digits (without the point)
264
+ * @param {Object} options - Conversion options
265
+ * @returns {string} Polish words for decimal part
266
+ */
267
+ function decimalPartToWords (decimalPart, options) {
268
+ let result = ''
269
+
270
+ // Handle leading zeros
271
+ let i = 0
272
+ while (i < decimalPart.length && decimalPart[i] === '0') {
273
+ if (result) result += ' '
274
+ result += ZERO
275
+ i++
276
+ }
277
+
278
+ // Convert remainder as a single number
279
+ const remainder = decimalPart.slice(i)
280
+ if (remainder) {
281
+ if (result) result += ' '
282
+ result += integerToWords(BigInt(remainder), options)
283
+ }
284
+
285
+ return result
286
+ }
287
+
288
+ /**
289
+ * Converts a numeric value to Polish words.
290
+ *
291
+ * This is the main public API. It accepts any valid numeric input
292
+ * (number, string, or bigint) and handles parsing internally.
293
+ *
294
+ * @param {number | string | bigint} value - The numeric value to convert
295
+ * @param {Object} [options] - Conversion options
296
+ * @param {string} [options.gender='masculine'] - Gender for numbers < 1000
297
+ * @returns {string} The number in Polish words
298
+ * @throws {TypeError} If value is not a valid numeric type
299
+ * @throws {Error} If value is not a valid number format
300
+ *
301
+ * @example
302
+ * toWords(1) // 'jeden'
303
+ * toWords(1, { gender: 'feminine' }) // 'jedna'
304
+ * toWords(1000) // 'tysiąc'
305
+ * toWords(2000) // 'dwa tysiące'
306
+ */
307
+ function toWords (value, options) {
308
+ options = validateOptions(options)
309
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
310
+
311
+ let result = ''
312
+
313
+ if (isNegative) {
314
+ result = NEGATIVE + ' '
109
315
  }
316
+
317
+ result += integerToWords(integerPart, options)
318
+
319
+ if (decimalPart) {
320
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, options)
321
+ }
322
+
323
+ return result
110
324
  }
325
+
326
+ // ============================================================================
327
+ // Public API
328
+ // ============================================================================
329
+
330
+ export { toWords }
@@ -1,29 +1,17 @@
1
1
  /**
2
- * (European) Portuguese language converter.
2
+ * Converts a numeric value to Portuguese words.
3
3
  *
4
- * Supports:
5
- * - Gender-aware hundreds (duzentos, trezentos)
6
- * - "e" (and) conjunction for number combinations
7
- * - Post-processing to normalize word flow
4
+ * This is the main public API. It accepts any valid numeric input
5
+ * (number, string, or bigint) and handles parsing internally.
6
+ *
7
+ * @param {number | string | bigint} value - The numeric value to convert
8
+ * @returns {string} The number in Portuguese words
9
+ * @throws {TypeError} If value is not a valid numeric type
10
+ * @throws {Error} If value is not a valid number format
11
+ *
12
+ * @example
13
+ * toWords(21) // 'vinte e um'
14
+ * toWords(100) // 'cem'
15
+ * toWords(1000000) // 'um milhão'
8
16
  */
9
- export class Portuguese extends GreedyScaleLanguage {
10
- static POSTCLEAN_REGEX: RegExp;
11
- scaleWords: (string | bigint)[][];
12
- hundredsWords: {
13
- 1: string;
14
- 2: string;
15
- 3: string;
16
- 4: string;
17
- 5: string;
18
- 6: string;
19
- 7: string;
20
- 8: string;
21
- 9: string;
22
- };
23
- finalizeWords(words: any): any;
24
- /** Combines two word-sets according to Portuguese grammar rules. */
25
- combineWordSets(preceding: any, following: any): {
26
- [x: string]: any;
27
- };
28
- }
29
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
17
+ export function toWords(value: number | string | bigint): string;