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,101 +1,320 @@
1
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js'
1
+ /**
2
+ * German language converter - Functional Implementation
3
+ *
4
+ * Self-contained module with its own input validation, ready for subpath exports.
5
+ *
6
+ * Key features:
7
+ * - Inverted tens-ones order: "einundzwanzig" (one-and-twenty) for 21-99
8
+ * - Compound words without spaces below million level
9
+ * - Three forms of 1: "eins" (standalone), "ein" (before hundert/tausend), "eine" (before Million+)
10
+ * - Scale pluralization: Million → Millionen, Milliarde → Milliarden
11
+ * - Spaces only around million+ scale words
12
+ * - BigInt modulo for efficient segment extraction
13
+ */
14
+
15
+ import { parseNumericValue } from '../utils/parse-numeric.js'
16
+
17
+ // ============================================================================
18
+ // Vocabulary (module-level constants)
19
+ // ============================================================================
20
+
21
+ // Ones words (1-9), index 0 unused
22
+ const ONES = ['', 'eins', 'zwei', 'drei', 'vier', 'fünf', 'sechs', 'sieben', 'acht', 'neun']
23
+
24
+ // "ein" form for use before hundert/und
25
+ const EIN = 'ein'
26
+
27
+ // Teens (10-19)
28
+ const TEENS = ['zehn', 'elf', 'zwölf', 'dreizehn', 'vierzehn', 'fünfzehn', 'sechzehn', 'siebzehn', 'achtzehn', 'neunzehn']
29
+
30
+ // Tens (20-90)
31
+ const TENS = ['', '', 'zwanzig', 'dreißig', 'vierzig', 'fünfzig', 'sechzig', 'siebzig', 'achtzig', 'neunzig']
32
+
33
+ // Scale words (index 0 = thousand, 1 = million, etc.)
34
+ const SCALES = ['tausend', 'Million', 'Milliarde', 'Billion', 'Billiarde', 'Trillion', 'Trilliarde', 'Quadrillion', 'Quadrilliarde']
35
+
36
+ // Pluralized scale words (million+)
37
+ const SCALES_PLURAL = ['tausend', 'Millionen', 'Milliarden', 'Billionen', 'Billiarden', 'Trillionen', 'Trilliarden', 'Quadrillionen', 'Quadrilliarden']
38
+
39
+ const HUNDRED = 'hundert'
40
+ const ZERO = 'null'
41
+ const NEGATIVE = 'minus'
42
+ const DECIMAL_SEP = 'komma'
43
+
44
+ // ============================================================================
45
+ // Segment Building
46
+ // ============================================================================
2
47
 
3
48
  /**
4
- * German language converter.
49
+ * Builds segment word for 0-999 (standalone form, uses "eins").
50
+ * German inverts ones and tens: "einundzwanzig" = one-and-twenty
5
51
  *
6
- * Supports:
7
- * - Compound formation (no separators between words)
8
- * - Three forms of 1 (eins/ein/eine)
9
- * - Units-before-tens ordering (einundzwanzig = one and twenty)
52
+ * @param {number} n - Number 0-999
53
+ * @returns {string} German words for the segment
10
54
  */
11
- export class German extends GreedyScaleLanguage {
12
- negativeWord = 'minus'
13
- decimalSeparatorWord = 'komma'
14
- zeroWord = 'null'
15
-
16
- scaleWords = [
17
- [1_000_000_000_000_000_000_000_000_000n, 'Quadrilliarde'],
18
- [1_000_000_000_000_000_000_000_000n, 'Quadrillion'],
19
- [1_000_000_000_000_000_000_000n, 'Trilliarde'],
20
- [1_000_000_000_000_000_000n, 'Trillion'],
21
- [1_000_000_000_000_000n, 'Billiarde'],
22
- [1_000_000_000_000n, 'Billion'],
23
- [1_000_000_000n, 'Milliarde'],
24
- [1_000_000n, 'Million'],
25
- [1000n, 'tausend'],
26
- [100n, 'hundert'],
27
- [90n, 'neunzig'],
28
- [80n, 'achtzig'],
29
- [70n, 'siebzig'],
30
- [60n, 'sechzig'],
31
- [50n, 'fünfzig'],
32
- [40n, 'vierzig'],
33
- [30n, 'dreißig'],
34
- [20n, 'zwanzig'],
35
- [19n, 'neunzehn'],
36
- [18n, 'achtzehn'],
37
- [17n, 'siebzehn'],
38
- [16n, 'sechzehn'],
39
- [15n, 'fünfzehn'],
40
- [14n, 'vierzehn'],
41
- [13n, 'dreizehn'],
42
- [12n, 'zwölf'],
43
- [11n, 'elf'],
44
- [10n, 'zehn'],
45
- [9n, 'neun'],
46
- [8n, 'acht'],
47
- [7n, 'sieben'],
48
- [6n, 'sechs'],
49
- [5n, 'fünf'],
50
- [4n, 'vier'],
51
- [3n, 'drei'],
52
- [2n, 'zwei'],
53
- [1n, 'eins'],
54
- [0n, 'null']
55
- ]
56
-
57
- /** Combines two word-sets with German compound formation and reversal rules. */
58
- combineWordSets (preceding, following) {
59
- let precedingWord = Object.keys(preceding)[0]
60
- let followingWord = Object.keys(following)[0]
61
- const precedingValue = Object.values(preceding)[0]
62
- const followingValue = Object.values(following)[0]
63
-
64
- // Handle form of 1: "eins" → "ein(e)" in certain contexts
65
- if (precedingValue === 1n) {
66
- if (followingValue === 100n || followingValue === 1000n) {
67
- return { [`ein${followingWord}`]: followingValue }
68
- }
69
- if (followingValue < 1_000_000n) {
70
- return following
71
- }
72
- precedingWord = 'eine'
55
+ function buildSegment (n) {
56
+ if (n === 0) return ''
57
+
58
+ const ones = n % 10
59
+ const tens = Math.trunc(n / 10) % 10
60
+ const hundreds = Math.trunc(n / 100)
61
+
62
+ let result = ''
63
+
64
+ // Hundreds: "ein" before hundert, not "eins"
65
+ if (hundreds > 0) {
66
+ result += (hundreds === 1 ? EIN : ONES[hundreds]) + HUNDRED
67
+ }
68
+
69
+ // Tens and ones
70
+ if (tens === 1) {
71
+ // Teens
72
+ result += TEENS[ones]
73
+ } else if (tens >= 2 && ones > 0) {
74
+ // Inverted: "einundzwanzig" (one-and-twenty)
75
+ // Use "ein" before "und", not "eins"
76
+ result += (ones === 1 ? EIN : ONES[ones]) + 'und' + TENS[tens]
77
+ } else if (tens >= 2) {
78
+ // Just tens
79
+ result += TENS[tens]
80
+ } else if (ones > 0) {
81
+ // Just ones (no tens, possibly after hundreds)
82
+ // Use "eins" for standalone/after hundreds
83
+ result += ONES[ones]
84
+ }
85
+
86
+ return result
87
+ }
88
+
89
+ /**
90
+ * Builds segment word for use before "tausend".
91
+ * Uses "ein" instead of "eins" for 1.
92
+ *
93
+ * @param {number} n - Number 0-999
94
+ * @returns {string} German words for thousand context
95
+ */
96
+ function buildSegmentForThousand (n) {
97
+ if (n === 0) return ''
98
+ if (n === 1) return EIN // "eintausend"
99
+
100
+ const ones = n % 10
101
+ const tens = Math.trunc(n / 10) % 10
102
+ const hundreds = Math.trunc(n / 100)
103
+
104
+ let result = ''
105
+
106
+ if (hundreds > 0) {
107
+ result += (hundreds === 1 ? EIN : ONES[hundreds]) + HUNDRED
108
+ }
109
+
110
+ if (tens === 1) {
111
+ result += TEENS[ones]
112
+ } else if (tens >= 2 && ones > 0) {
113
+ result += (ones === 1 ? EIN : ONES[ones]) + 'und' + TENS[tens]
114
+ } else if (tens >= 2) {
115
+ result += TENS[tens]
116
+ } else if (ones > 0 && hundreds > 0) {
117
+ result += ONES[ones]
118
+ } else if (ones > 0) {
119
+ result += ONES[ones]
120
+ }
121
+
122
+ return result
123
+ }
124
+
125
+ // ============================================================================
126
+ // Conversion Functions
127
+ // ============================================================================
128
+
129
+ /**
130
+ * Converts a non-negative integer to German words.
131
+ *
132
+ * @param {bigint} n - Non-negative integer to convert
133
+ * @returns {string} German words
134
+ */
135
+ function integerToWords (n) {
136
+ if (n === 0n) return ZERO
137
+
138
+ // Fast path: numbers < 1000
139
+ if (n < 1000n) {
140
+ return buildSegment(Number(n))
141
+ }
142
+
143
+ // Fast path: numbers < 1,000,000
144
+ if (n < 1_000_000n) {
145
+ const thousands = Number(n / 1000n)
146
+ const remainder = Number(n % 1000n)
147
+
148
+ // Compound: "eintausendzweihundert" (no spaces)
149
+ let result = buildSegmentForThousand(thousands) + SCALES[0]
150
+
151
+ if (remainder > 0) {
152
+ result += buildSegment(remainder)
73
153
  }
74
154
 
75
- if (followingValue > precedingValue) {
76
- // Multiply: apply pluralization rules for millions
77
- if (followingValue >= 1_000_000n) {
78
- if (precedingValue > 1n) {
79
- followingWord += followingWord.at(-1) === 'e' ? 'n' : 'en'
155
+ return result
156
+ }
157
+
158
+ // For numbers >= 1,000,000, use scale decomposition
159
+ return buildLargeNumberWords(n)
160
+ }
161
+
162
+ /**
163
+ * Builds words for numbers >= 1,000,000.
164
+ *
165
+ * @param {bigint} n - Number >= 1,000,000
166
+ * @returns {string} German words
167
+ */
168
+ function buildLargeNumberWords (n) {
169
+ const numStr = n.toString()
170
+ const len = numStr.length
171
+
172
+ // Build segments of 3 digits from right to left
173
+ const segments = []
174
+ const segmentSize = 3
175
+
176
+ const remainderLen = len % segmentSize
177
+ let pos = 0
178
+ if (remainderLen > 0) {
179
+ segments.push(Number(numStr.slice(0, remainderLen)))
180
+ pos = remainderLen
181
+ }
182
+ while (pos < len) {
183
+ segments.push(Number(numStr.slice(pos, pos + segmentSize)))
184
+ pos += segmentSize
185
+ }
186
+
187
+ // Convert segments to words
188
+ const parts = []
189
+ let scaleIndex = segments.length - 1
190
+
191
+ for (let i = 0; i < segments.length; i++) {
192
+ const segment = segments[i]
193
+
194
+ if (segment !== 0) {
195
+ if (scaleIndex === 0) {
196
+ // Units segment (no scale word)
197
+ parts.push({ words: buildSegment(segment), isScale: false, scaleLevel: 0 })
198
+ } else if (scaleIndex === 1) {
199
+ // Thousands: compound without space
200
+ const segWords = buildSegmentForThousand(segment)
201
+ parts.push({ words: segWords + SCALES[0], isScale: false, scaleLevel: 1 })
202
+ } else {
203
+ // Million+ : space around scale word
204
+ let segWords
205
+ if (segment === 1) {
206
+ segWords = 'eine' // "eine Million"
207
+ } else {
208
+ segWords = buildSegment(segment)
80
209
  }
81
- precedingWord += ' '
210
+ const scaleWord = segment === 1 ? SCALES[scaleIndex - 1] : SCALES_PLURAL[scaleIndex - 1]
211
+ parts.push({ words: segWords, isScale: false, scaleLevel: scaleIndex })
212
+ parts.push({ words: scaleWord, isScale: true, scaleLevel: scaleIndex })
82
213
  }
83
- return { [`${precedingWord}${followingWord}`]: precedingValue * followingValue }
84
214
  }
85
215
 
86
- // Add: handle special case of tens + units
87
- if (followingValue < 10n && precedingValue > 10n && precedingValue < 100n) {
88
- // German reverses tens and units (einundzwanzig = one and twenty)
89
- if (followingValue === 1n) {
90
- followingWord = 'ein'
216
+ scaleIndex--
217
+ }
218
+
219
+ // Join with German spacing rules: space around million+ scale words
220
+ return joinGermanParts(parts)
221
+ }
222
+
223
+ /**
224
+ * Joins parts with German spacing rules.
225
+ * Spaces only around million+ scale words.
226
+ *
227
+ * @param {Array} parts - Parts with metadata
228
+ * @returns {string} Joined string
229
+ */
230
+ function joinGermanParts (parts) {
231
+ if (parts.length === 0) return ZERO
232
+
233
+ let result = ''
234
+
235
+ for (let i = 0; i < parts.length; i++) {
236
+ const part = parts[i]
237
+ const prevPart = i > 0 ? parts[i - 1] : null
238
+
239
+ // Add space before if:
240
+ // - Current is a million+ scale word
241
+ // - Previous was a million+ scale word
242
+ if (i > 0) {
243
+ const needsSpace = part.isScale || (prevPart && prevPart.isScale)
244
+ if (needsSpace) {
245
+ result += ' '
91
246
  }
92
- const temp = followingWord
93
- followingWord = precedingWord
94
- precedingWord = `${temp}und`
95
- } else if (precedingValue >= 1_000_000n) {
96
- precedingWord += ' '
97
247
  }
98
248
 
99
- return { [`${precedingWord}${followingWord}`]: precedingValue + followingValue }
249
+ result += part.words
250
+ }
251
+
252
+ return result
253
+ }
254
+
255
+ /**
256
+ * Converts decimal digits to German words.
257
+ *
258
+ * @param {string} decimalPart - Decimal digits (without the point)
259
+ * @returns {string} German 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++
270
+ }
271
+
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))
277
+ }
278
+
279
+ return result
280
+ }
281
+
282
+ /**
283
+ * Converts a numeric value to German 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 German 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) // 'einundzwanzig'
295
+ * toWords(1000) // 'eintausend'
296
+ * toWords(1000000) // 'eine Million'
297
+ */
298
+ function toWords (value) {
299
+ const { isNegative, integerPart, decimalPart } = parseNumericValue(value)
300
+
301
+ let result = ''
302
+
303
+ if (isNegative) {
304
+ result = NEGATIVE + ' '
100
305
  }
306
+
307
+ result += integerToWords(integerPart)
308
+
309
+ if (decimalPart) {
310
+ result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)
311
+ }
312
+
313
+ return result
101
314
  }
315
+
316
+ // ============================================================================
317
+ // Public API
318
+ // ============================================================================
319
+
320
+ export { toWords }
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Greek language converter.
2
+ * Converts a numeric value to Greek words.
3
3
  *
4
- * Supports:
5
- * - Space-separated number composition
6
- * - Implicit "one" (ένα) omission before scale words
7
- * - Digit-by-digit decimal reading
4
+ * @param {number | string | bigint} value - The numeric value to convert
5
+ * @returns {string} The number in Greek 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) // 'είκοσι ένα'
11
+ * toWords(1000) // 'χίλια'
12
+ * toWords('3.14') // 'τρία κόμμα ένα τέσσερα'
8
13
  */
9
- export class Greek extends GreedyScaleLanguage {
10
- scaleWords: (string | bigint)[][];
11
- /** Combines two word-sets with Greek space-separation rules. */
12
- combineWordSets(preceding: any, following: any): any;
13
- }
14
- import { GreedyScaleLanguage } from '../classes/greedy-scale-language.js';
14
+ export function toWords(value: number | string | bigint): string;