n2words 3.1.0 → 4.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.
- package/CHANGELOG.md +44 -0
- package/LICENSE +1 -1
- package/README.md +64 -184
- package/dist/am-ET.js +2 -0
- package/dist/am-ET.umd.js +2 -0
- package/dist/am-Latn-ET.js +2 -0
- package/dist/am-Latn-ET.umd.js +2 -0
- package/dist/ar-SA.js +2 -0
- package/dist/ar-SA.umd.js +2 -0
- package/dist/az-AZ.js +2 -0
- package/dist/az-AZ.umd.js +2 -0
- package/dist/bn-BD.js +2 -0
- package/dist/bn-BD.umd.js +2 -0
- package/dist/cs-CZ.js +2 -0
- package/dist/cs-CZ.umd.js +2 -0
- package/dist/da-DK.js +2 -0
- package/dist/da-DK.umd.js +2 -0
- package/dist/de-DE.js +2 -0
- package/dist/de-DE.umd.js +2 -0
- package/dist/el-GR.js +2 -0
- package/dist/el-GR.umd.js +2 -0
- package/dist/en-AU.js +2 -0
- package/dist/en-AU.umd.js +2 -0
- package/dist/en-BD.js +2 -0
- package/dist/en-BD.umd.js +2 -0
- package/dist/en-CA.js +2 -0
- package/dist/en-CA.umd.js +2 -0
- package/dist/en-GB.js +2 -0
- package/dist/en-GB.umd.js +2 -0
- package/dist/en-GH.js +2 -0
- package/dist/en-GH.umd.js +2 -0
- package/dist/en-IE.js +2 -0
- package/dist/en-IE.umd.js +2 -0
- package/dist/en-IN.js +2 -0
- package/dist/en-IN.umd.js +2 -0
- package/dist/en-KE.js +2 -0
- package/dist/en-KE.umd.js +2 -0
- package/dist/en-MY.js +2 -0
- package/dist/en-MY.umd.js +2 -0
- package/dist/en-NG.js +2 -0
- package/dist/en-NG.umd.js +2 -0
- package/dist/en-NZ.js +2 -0
- package/dist/en-NZ.umd.js +2 -0
- package/dist/en-PH.js +2 -0
- package/dist/en-PH.umd.js +2 -0
- package/dist/en-PK.js +2 -0
- package/dist/en-PK.umd.js +2 -0
- package/dist/en-SG.js +2 -0
- package/dist/en-SG.umd.js +2 -0
- package/dist/en-US.js +2 -0
- package/dist/en-US.umd.js +2 -0
- package/dist/en-ZA.js +2 -0
- package/dist/en-ZA.umd.js +2 -0
- package/dist/es-ES.js +2 -0
- package/dist/es-ES.umd.js +2 -0
- package/dist/es-MX.js +2 -0
- package/dist/es-MX.umd.js +2 -0
- package/dist/es-US.js +2 -0
- package/dist/es-US.umd.js +2 -0
- package/dist/fa-IR.js +2 -0
- package/dist/fa-IR.umd.js +2 -0
- package/dist/fi-FI.js +2 -0
- package/dist/fi-FI.umd.js +2 -0
- package/dist/fil-PH.js +2 -0
- package/dist/fil-PH.umd.js +2 -0
- package/dist/fr-BE.js +2 -0
- package/dist/fr-BE.umd.js +2 -0
- package/dist/fr-FR.js +2 -0
- package/dist/fr-FR.umd.js +2 -0
- package/dist/gu-IN.js +2 -0
- package/dist/gu-IN.umd.js +2 -0
- package/dist/ha-NG.js +2 -0
- package/dist/ha-NG.umd.js +2 -0
- package/dist/hbo-IL.js +2 -0
- package/dist/hbo-IL.umd.js +2 -0
- package/dist/he-IL.js +2 -0
- package/dist/he-IL.umd.js +2 -0
- package/dist/hi-IN.js +2 -0
- package/dist/hi-IN.umd.js +2 -0
- package/dist/hr-HR.js +2 -0
- package/dist/hr-HR.umd.js +2 -0
- package/dist/hu-HU.js +2 -0
- package/dist/hu-HU.umd.js +2 -0
- package/dist/id-ID.js +2 -0
- package/dist/id-ID.umd.js +2 -0
- package/dist/it-IT.js +2 -0
- package/dist/it-IT.umd.js +2 -0
- package/dist/ja-JP.js +2 -0
- package/dist/ja-JP.umd.js +2 -0
- package/dist/ka-GE.js +2 -0
- package/dist/ka-GE.umd.js +2 -0
- package/dist/kn-IN.js +2 -0
- package/dist/kn-IN.umd.js +2 -0
- package/dist/ko-KR.js +2 -0
- package/dist/ko-KR.umd.js +2 -0
- package/dist/lt-LT.js +2 -0
- package/dist/lt-LT.umd.js +2 -0
- package/dist/lv-LV.js +2 -0
- package/dist/lv-LV.umd.js +2 -0
- package/dist/mr-IN.js +2 -0
- package/dist/mr-IN.umd.js +2 -0
- package/dist/ms-MY.js +2 -0
- package/dist/ms-MY.umd.js +2 -0
- package/dist/nb-NO.js +2 -0
- package/dist/nb-NO.umd.js +2 -0
- package/dist/nl-NL.js +2 -0
- package/dist/nl-NL.umd.js +2 -0
- package/dist/pa-IN.js +2 -0
- package/dist/pa-IN.umd.js +2 -0
- package/dist/pl-PL.js +2 -0
- package/dist/pl-PL.umd.js +2 -0
- package/dist/pt-PT.js +2 -0
- package/dist/pt-PT.umd.js +2 -0
- package/dist/ro-RO.js +2 -0
- package/dist/ro-RO.umd.js +2 -0
- package/dist/ru-RU.js +2 -0
- package/dist/ru-RU.umd.js +2 -0
- package/dist/sr-Cyrl-RS.js +2 -0
- package/dist/sr-Cyrl-RS.umd.js +2 -0
- package/dist/sr-Latn-RS.js +2 -0
- package/dist/sr-Latn-RS.umd.js +2 -0
- package/dist/sv-SE.js +2 -0
- package/dist/sv-SE.umd.js +2 -0
- package/dist/sw-KE.js +2 -0
- package/dist/sw-KE.umd.js +2 -0
- package/dist/ta-IN.js +2 -0
- package/dist/ta-IN.umd.js +2 -0
- package/dist/te-IN.js +2 -0
- package/dist/te-IN.umd.js +2 -0
- package/dist/th-TH.js +2 -0
- package/dist/th-TH.umd.js +2 -0
- package/dist/tr-TR.js +2 -0
- package/dist/tr-TR.umd.js +2 -0
- package/dist/uk-UA.js +2 -0
- package/dist/uk-UA.umd.js +2 -0
- package/dist/ur-PK.js +2 -0
- package/dist/ur-PK.umd.js +2 -0
- package/dist/vi-VN.js +2 -0
- package/dist/vi-VN.umd.js +2 -0
- package/dist/yo-NG.js +2 -0
- package/dist/yo-NG.umd.js +2 -0
- package/dist/zh-Hans-CN.js +2 -0
- package/dist/zh-Hans-CN.umd.js +2 -0
- package/dist/zh-Hant-TW.js +2 -0
- package/dist/zh-Hant-TW.umd.js +2 -0
- package/package.json +47 -82
- package/src/am-ET.d.ts +40 -0
- package/src/am-ET.js +269 -0
- package/src/am-Latn-ET.d.ts +35 -0
- package/src/am-Latn-ET.js +264 -0
- package/src/ar-SA.d.ts +49 -0
- package/{lib/languages/ar.js → src/ar-SA.js} +177 -15
- package/src/az-AZ.d.ts +37 -0
- package/src/az-AZ.js +312 -0
- package/src/bn-BD.d.ts +36 -0
- package/{lib/languages/bn.js → src/bn-BD.js} +110 -6
- package/src/cs-CZ.d.ts +49 -0
- package/{lib/languages/cs.js → src/cs-CZ.js} +240 -14
- package/src/da-DK.d.ts +44 -0
- package/{lib/languages/da.js → src/da-DK.js} +131 -11
- package/src/de-DE.d.ts +57 -0
- package/src/de-DE.js +603 -0
- package/src/el-GR.d.ts +40 -0
- package/src/el-GR.js +418 -0
- package/src/en-AU.d.ts +47 -0
- package/src/en-AU.js +423 -0
- package/src/en-BD.d.ts +49 -0
- package/src/en-BD.js +415 -0
- package/src/en-CA.d.ts +63 -0
- package/src/en-CA.js +518 -0
- package/src/en-GB.d.ts +56 -0
- package/src/en-GB.js +469 -0
- package/src/en-GH.d.ts +11 -0
- package/src/en-GH.js +345 -0
- package/src/en-IE.d.ts +56 -0
- package/src/en-IE.js +479 -0
- package/src/en-IN.d.ts +49 -0
- package/src/en-IN.js +415 -0
- package/src/en-KE.d.ts +11 -0
- package/src/en-KE.js +345 -0
- package/src/en-MY.d.ts +11 -0
- package/src/en-MY.js +347 -0
- package/src/en-NG.d.ts +56 -0
- package/src/en-NG.js +479 -0
- package/src/en-NZ.d.ts +11 -0
- package/{lib/languages/en.js → src/en-NZ.js} +164 -31
- package/src/en-PH.d.ts +11 -0
- package/src/en-PH.js +345 -0
- package/src/en-PK.d.ts +49 -0
- package/src/en-PK.js +415 -0
- package/src/en-SG.d.ts +11 -0
- package/src/en-SG.js +345 -0
- package/src/en-US.d.ts +63 -0
- package/src/en-US.js +516 -0
- package/src/en-ZA.d.ts +56 -0
- package/src/en-ZA.js +478 -0
- package/src/es-ES.d.ts +65 -0
- package/src/es-ES.js +541 -0
- package/src/es-MX.d.ts +58 -0
- package/{lib/languages/es.js → src/es-MX.js} +228 -18
- package/src/es-US.d.ts +58 -0
- package/src/es-US.js +446 -0
- package/src/fa-IR.d.ts +38 -0
- package/src/fa-IR.js +246 -0
- package/src/fi-FI.d.ts +46 -0
- package/{lib/languages/fi.js → src/fi-FI.js} +152 -11
- package/src/fil-PH.d.ts +37 -0
- package/{lib/languages/fil.js → src/fil-PH.js} +144 -8
- package/src/fr-BE.d.ts +49 -0
- package/{lib/languages → src}/fr-BE.js +175 -13
- package/src/fr-FR.d.ts +63 -0
- package/{lib/languages/fr.js → src/fr-FR.js} +182 -16
- package/src/gu-IN.d.ts +35 -0
- package/{lib/languages/gu.js → src/gu-IN.js} +115 -6
- package/src/ha-NG.d.ts +37 -0
- package/{lib/languages/ha.js → src/ha-NG.js} +107 -8
- package/src/hbo-IL.d.ts +39 -0
- package/{lib/languages/hbo.js → src/hbo-IL.js} +211 -19
- package/src/he-IL.d.ts +37 -0
- package/src/he-IL.js +537 -0
- package/src/hi-IN.d.ts +36 -0
- package/{lib/languages/hi.js → src/hi-IN.js} +116 -6
- package/src/hr-HR.d.ts +42 -0
- package/src/hr-HR.js +463 -0
- package/src/hu-HU.d.ts +38 -0
- package/{lib/languages/hu.js → src/hu-HU.js} +164 -6
- package/src/id-ID.d.ts +38 -0
- package/{lib/languages/id.js → src/id-ID.js} +99 -8
- package/src/it-IT.d.ts +59 -0
- package/{lib/languages/it.js → src/it-IT.js} +179 -15
- package/src/ja-JP.d.ts +49 -0
- package/{lib/languages/ja.js → src/ja-JP.js} +111 -12
- package/src/ka-GE.d.ts +44 -0
- package/{lib/languages/ka.js → src/ka-GE.js} +113 -11
- package/src/kn-IN.d.ts +35 -0
- package/{lib/languages/kn.js → src/kn-IN.js} +115 -6
- package/src/ko-KR.d.ts +45 -0
- package/{lib/languages/ko.js → src/ko-KR.js} +94 -12
- package/src/lt-LT.d.ts +49 -0
- package/src/lt-LT.js +543 -0
- package/src/lv-LV.d.ts +49 -0
- package/src/lv-LV.js +595 -0
- package/src/mr-IN.d.ts +36 -0
- package/{lib/languages/mr.js → src/mr-IN.js} +116 -6
- package/src/ms-MY.d.ts +37 -0
- package/{lib/languages/ms.js → src/ms-MY.js} +111 -8
- package/src/nb-NO.d.ts +44 -0
- package/{lib/languages/nb.js → src/nb-NO.js} +153 -11
- package/src/nl-NL.d.ts +54 -0
- package/{lib/languages/nl.js → src/nl-NL.js} +260 -18
- package/src/pa-IN.d.ts +36 -0
- package/{lib/languages/pa.js → src/pa-IN.js} +116 -6
- package/src/pl-PL.d.ts +55 -0
- package/src/pl-PL.js +585 -0
- package/src/pt-PT.d.ts +45 -0
- package/{lib/languages/pt.js → src/pt-PT.js} +234 -12
- package/src/ro-RO.d.ts +44 -0
- package/{lib/languages/ro.js → src/ro-RO.js} +212 -18
- package/src/ru-RU.d.ts +50 -0
- package/src/ru-RU.js +535 -0
- package/src/sr-Cyrl-RS.d.ts +49 -0
- package/src/sr-Cyrl-RS.js +503 -0
- package/src/sr-Latn-RS.d.ts +49 -0
- package/src/sr-Latn-RS.js +503 -0
- package/src/sv-SE.d.ts +44 -0
- package/{lib/languages/sv.js → src/sv-SE.js} +136 -11
- package/src/sw-KE.d.ts +37 -0
- package/{lib/languages/sw.js → src/sw-KE.js} +117 -6
- package/src/ta-IN.d.ts +35 -0
- package/{lib/languages/ta.js → src/ta-IN.js} +109 -6
- package/src/te-IN.d.ts +35 -0
- package/{lib/languages/te.js → src/te-IN.js} +115 -6
- package/src/th-TH.d.ts +38 -0
- package/{lib/languages/th.js → src/th-TH.js} +99 -6
- package/src/tr-TR.d.ts +48 -0
- package/{lib/languages/tr.js → src/tr-TR.js} +168 -23
- package/src/uk-UA.d.ts +42 -0
- package/src/uk-UA.js +463 -0
- package/src/ur-PK.d.ts +36 -0
- package/{lib/languages/ur.js → src/ur-PK.js} +116 -6
- package/src/utils/expand-scientific.d.ts +32 -0
- package/src/utils/expand-scientific.js +65 -0
- package/src/utils/parse-cardinal.d.ts +14 -0
- package/{lib/utils/parse-numeric.js → src/utils/parse-cardinal.js} +14 -44
- package/src/utils/parse-currency.d.ts +14 -0
- package/src/utils/parse-currency.js +91 -0
- package/src/utils/parse-ordinal.d.ts +10 -0
- package/src/utils/parse-ordinal.js +103 -0
- package/src/vi-VN.d.ts +48 -0
- package/{lib/languages/vi.js → src/vi-VN.js} +102 -11
- package/src/yo-NG.d.ts +37 -0
- package/{lib/languages/yo.js → src/yo-NG.js} +109 -9
- package/src/zh-Hans-CN.d.ts +48 -0
- package/{lib/languages/zh-Hans.js → src/zh-Hans-CN.js} +140 -8
- package/src/zh-Hant-TW.d.ts +50 -0
- package/{lib/languages/zh-Hant.js → src/zh-Hant-TW.js} +139 -8
- package/dist/languages/am-Latn.js +0 -3
- package/dist/languages/am-Latn.js.map +0 -1
- package/dist/languages/am.js +0 -3
- package/dist/languages/am.js.map +0 -1
- package/dist/languages/ar.js +0 -3
- package/dist/languages/ar.js.map +0 -1
- package/dist/languages/az.js +0 -3
- package/dist/languages/az.js.map +0 -1
- package/dist/languages/bn.js +0 -3
- package/dist/languages/bn.js.map +0 -1
- package/dist/languages/cs.js +0 -3
- package/dist/languages/cs.js.map +0 -1
- package/dist/languages/da.js +0 -3
- package/dist/languages/da.js.map +0 -1
- package/dist/languages/de.js +0 -3
- package/dist/languages/de.js.map +0 -1
- package/dist/languages/el.js +0 -3
- package/dist/languages/el.js.map +0 -1
- package/dist/languages/en.js +0 -3
- package/dist/languages/en.js.map +0 -1
- package/dist/languages/es.js +0 -3
- package/dist/languages/es.js.map +0 -1
- package/dist/languages/fa.js +0 -3
- package/dist/languages/fa.js.map +0 -1
- package/dist/languages/fi.js +0 -3
- package/dist/languages/fi.js.map +0 -1
- package/dist/languages/fil.js +0 -3
- package/dist/languages/fil.js.map +0 -1
- package/dist/languages/fr-BE.js +0 -3
- package/dist/languages/fr-BE.js.map +0 -1
- package/dist/languages/fr.js +0 -3
- package/dist/languages/fr.js.map +0 -1
- package/dist/languages/gu.js +0 -3
- package/dist/languages/gu.js.map +0 -1
- package/dist/languages/ha.js +0 -3
- package/dist/languages/ha.js.map +0 -1
- package/dist/languages/hbo.js +0 -3
- package/dist/languages/hbo.js.map +0 -1
- package/dist/languages/he.js +0 -3
- package/dist/languages/he.js.map +0 -1
- package/dist/languages/hi.js +0 -3
- package/dist/languages/hi.js.map +0 -1
- package/dist/languages/hr.js +0 -3
- package/dist/languages/hr.js.map +0 -1
- package/dist/languages/hu.js +0 -3
- package/dist/languages/hu.js.map +0 -1
- package/dist/languages/id.js +0 -3
- package/dist/languages/id.js.map +0 -1
- package/dist/languages/it.js +0 -3
- package/dist/languages/it.js.map +0 -1
- package/dist/languages/ja.js +0 -3
- package/dist/languages/ja.js.map +0 -1
- package/dist/languages/ka.js +0 -3
- package/dist/languages/ka.js.map +0 -1
- package/dist/languages/kn.js +0 -3
- package/dist/languages/kn.js.map +0 -1
- package/dist/languages/ko.js +0 -3
- package/dist/languages/ko.js.map +0 -1
- package/dist/languages/lt.js +0 -3
- package/dist/languages/lt.js.map +0 -1
- package/dist/languages/lv.js +0 -3
- package/dist/languages/lv.js.map +0 -1
- package/dist/languages/mr.js +0 -3
- package/dist/languages/mr.js.map +0 -1
- package/dist/languages/ms.js +0 -3
- package/dist/languages/ms.js.map +0 -1
- package/dist/languages/nb.js +0 -3
- package/dist/languages/nb.js.map +0 -1
- package/dist/languages/nl.js +0 -3
- package/dist/languages/nl.js.map +0 -1
- package/dist/languages/pa.js +0 -3
- package/dist/languages/pa.js.map +0 -1
- package/dist/languages/pl.js +0 -3
- package/dist/languages/pl.js.map +0 -1
- package/dist/languages/pt.js +0 -3
- package/dist/languages/pt.js.map +0 -1
- package/dist/languages/ro.js +0 -3
- package/dist/languages/ro.js.map +0 -1
- package/dist/languages/ru.js +0 -3
- package/dist/languages/ru.js.map +0 -1
- package/dist/languages/sr-Cyrl.js +0 -3
- package/dist/languages/sr-Cyrl.js.map +0 -1
- package/dist/languages/sr-Latn.js +0 -3
- package/dist/languages/sr-Latn.js.map +0 -1
- package/dist/languages/sv.js +0 -3
- package/dist/languages/sv.js.map +0 -1
- package/dist/languages/sw.js +0 -3
- package/dist/languages/sw.js.map +0 -1
- package/dist/languages/ta.js +0 -3
- package/dist/languages/ta.js.map +0 -1
- package/dist/languages/te.js +0 -3
- package/dist/languages/te.js.map +0 -1
- package/dist/languages/th.js +0 -3
- package/dist/languages/th.js.map +0 -1
- package/dist/languages/tr.js +0 -3
- package/dist/languages/tr.js.map +0 -1
- package/dist/languages/uk.js +0 -3
- package/dist/languages/uk.js.map +0 -1
- package/dist/languages/ur.js +0 -3
- package/dist/languages/ur.js.map +0 -1
- package/dist/languages/vi.js +0 -3
- package/dist/languages/vi.js.map +0 -1
- package/dist/languages/yo.js +0 -3
- package/dist/languages/yo.js.map +0 -1
- package/dist/languages/zh-Hans.js +0 -3
- package/dist/languages/zh-Hans.js.map +0 -1
- package/dist/languages/zh-Hant.js +0 -3
- package/dist/languages/zh-Hant.js.map +0 -1
- package/dist/n2words.js +0 -3
- package/dist/n2words.js.map +0 -1
- package/lib/languages/am-Latn.d.ts +0 -7
- package/lib/languages/am-Latn.js +0 -159
- package/lib/languages/am.d.ts +0 -7
- package/lib/languages/am.js +0 -159
- package/lib/languages/ar.d.ts +0 -17
- package/lib/languages/az.d.ts +0 -7
- package/lib/languages/az.js +0 -171
- package/lib/languages/bn.d.ts +0 -7
- package/lib/languages/cs.d.ts +0 -18
- package/lib/languages/da.d.ts +0 -14
- package/lib/languages/de.d.ts +0 -17
- package/lib/languages/de.js +0 -320
- package/lib/languages/el.d.ts +0 -14
- package/lib/languages/el.js +0 -236
- package/lib/languages/en.d.ts +0 -17
- package/lib/languages/es.d.ts +0 -21
- package/lib/languages/fa.d.ts +0 -7
- package/lib/languages/fa.js +0 -134
- package/lib/languages/fi.d.ts +0 -14
- package/lib/languages/fil.d.ts +0 -7
- package/lib/languages/fr-BE.d.ts +0 -11
- package/lib/languages/fr.d.ts +0 -21
- package/lib/languages/gu.d.ts +0 -7
- package/lib/languages/ha.d.ts +0 -7
- package/lib/languages/hbo.d.ts +0 -13
- package/lib/languages/he.d.ts +0 -13
- package/lib/languages/he.js +0 -265
- package/lib/languages/hi.d.ts +0 -7
- package/lib/languages/hr.d.ts +0 -11
- package/lib/languages/hr.js +0 -224
- package/lib/languages/hu.d.ts +0 -7
- package/lib/languages/id.d.ts +0 -7
- package/lib/languages/it.d.ts +0 -19
- package/lib/languages/ja.d.ts +0 -17
- package/lib/languages/ka.d.ts +0 -17
- package/lib/languages/kn.d.ts +0 -7
- package/lib/languages/ko.d.ts +0 -14
- package/lib/languages/lt.d.ts +0 -18
- package/lib/languages/lt.js +0 -301
- package/lib/languages/lv.d.ts +0 -18
- package/lib/languages/lv.js +0 -312
- package/lib/languages/mr.d.ts +0 -7
- package/lib/languages/ms.d.ts +0 -7
- package/lib/languages/nb.d.ts +0 -14
- package/lib/languages/nl.d.ts +0 -26
- package/lib/languages/pa.d.ts +0 -7
- package/lib/languages/pl.d.ts +0 -22
- package/lib/languages/pl.js +0 -317
- package/lib/languages/pt.d.ts +0 -17
- package/lib/languages/ro.d.ts +0 -18
- package/lib/languages/ru.d.ts +0 -11
- package/lib/languages/ru.js +0 -245
- package/lib/languages/sr-Cyrl.d.ts +0 -11
- package/lib/languages/sr-Cyrl.js +0 -221
- package/lib/languages/sr-Latn.d.ts +0 -11
- package/lib/languages/sr-Latn.js +0 -221
- package/lib/languages/sv.d.ts +0 -14
- package/lib/languages/sw.d.ts +0 -7
- package/lib/languages/ta.d.ts +0 -7
- package/lib/languages/te.d.ts +0 -7
- package/lib/languages/th.d.ts +0 -7
- package/lib/languages/tr.d.ts +0 -18
- package/lib/languages/uk.d.ts +0 -11
- package/lib/languages/uk.js +0 -224
- package/lib/languages/ur.d.ts +0 -7
- package/lib/languages/vi.d.ts +0 -17
- package/lib/languages/yo.d.ts +0 -7
- package/lib/languages/zh-Hans.d.ts +0 -11
- package/lib/languages/zh-Hant.d.ts +0 -11
- package/lib/n2words.d.ts +0 -55
- package/lib/n2words.js +0 -126
- package/lib/utils/parse-numeric.d.ts +0 -17
- /package/{lib → src}/utils/is-plain-object.d.ts +0 -0
- /package/{lib → src}/utils/is-plain-object.js +0 -0
- /package/{lib → src}/utils/validate-options.d.ts +0 -0
- /package/{lib → src}/utils/validate-options.js +0 -0
package/src/ru-RU.js
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Russian (Russia) language converter
|
|
3
|
+
*
|
|
4
|
+
* CLDR: ru-RU | Russian as used in Russia
|
|
5
|
+
*
|
|
6
|
+
* Key features:
|
|
7
|
+
* - Three-form pluralization (one/few/many)
|
|
8
|
+
* - Gender: thousands are feminine, millions+ are masculine
|
|
9
|
+
* - Irregular hundreds (двести, триста, etc.)
|
|
10
|
+
* - Long scale naming
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { parseCardinalValue } from './utils/parse-cardinal.js'
|
|
14
|
+
import { parseCurrencyValue } from './utils/parse-currency.js'
|
|
15
|
+
import { parseOrdinalValue } from './utils/parse-ordinal.js'
|
|
16
|
+
import { validateOptions } from './utils/validate-options.js'
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Vocabulary
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
const ONES_MASC = ['', 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']
|
|
23
|
+
const ONES_FEM = ['', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять']
|
|
24
|
+
|
|
25
|
+
const TEENS = ['десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать']
|
|
26
|
+
const TENS = ['', '', 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто']
|
|
27
|
+
|
|
28
|
+
// Irregular hundreds
|
|
29
|
+
const HUNDREDS = ['', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот']
|
|
30
|
+
|
|
31
|
+
const ZERO = 'ноль'
|
|
32
|
+
const NEGATIVE = 'минус'
|
|
33
|
+
const DECIMAL_SEP = 'запятая'
|
|
34
|
+
|
|
35
|
+
// Scale words: [singular, few, many]
|
|
36
|
+
// Thousands (index 0) are feminine, rest are masculine
|
|
37
|
+
const SCALE_FORMS = [
|
|
38
|
+
['тысяча', 'тысячи', 'тысяч'],
|
|
39
|
+
['миллион', 'миллиона', 'миллионов'],
|
|
40
|
+
['миллиард', 'миллиарда', 'миллиардов'],
|
|
41
|
+
['триллион', 'триллиона', 'триллионов'],
|
|
42
|
+
['квадриллион', 'квадриллиона', 'квадриллионов'],
|
|
43
|
+
['квинтиллион', 'квинтиллиона', 'квинтиллионов'],
|
|
44
|
+
['секстиллион', 'секстиллиона', 'секстиллионов'],
|
|
45
|
+
['септиллион', 'септиллиона', 'септиллионов'],
|
|
46
|
+
['октиллион', 'октиллиона', 'октиллионов'],
|
|
47
|
+
['нониллион', 'нониллиона', 'нониллионов']
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Ordinal Vocabulary (masculine nominative)
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
// Ordinal ones: первый, второй, третий...
|
|
55
|
+
const ORDINAL_ONES = ['', 'первый', 'второй', 'третий', 'четвёртый', 'пятый', 'шестой', 'седьмой', 'восьмой', 'девятый']
|
|
56
|
+
|
|
57
|
+
// Ordinal teens: десятый, одиннадцатый...
|
|
58
|
+
const ORDINAL_TEENS = ['десятый', 'одиннадцатый', 'двенадцатый', 'тринадцатый', 'четырнадцатый', 'пятнадцатый', 'шестнадцатый', 'семнадцатый', 'восемнадцатый', 'девятнадцатый']
|
|
59
|
+
|
|
60
|
+
// Ordinal tens: двадцатый, тридцатый...
|
|
61
|
+
const ORDINAL_TENS = ['', '', 'двадцатый', 'тридцатый', 'сороковой', 'пятидесятый', 'шестидесятый', 'семидесятый', 'восьмидесятый', 'девяностый']
|
|
62
|
+
|
|
63
|
+
// Ordinal hundreds: сотый, двухсотый...
|
|
64
|
+
const ORDINAL_HUNDREDS = ['', 'сотый', 'двухсотый', 'трёхсотый', 'четырёхсотый', 'пятисотый', 'шестисотый', 'семисотый', 'восьмисотый', 'девятисотый']
|
|
65
|
+
|
|
66
|
+
// Ordinal scale words (тысячный, миллионный, etc.)
|
|
67
|
+
const ORDINAL_SCALES = [
|
|
68
|
+
'тысячный',
|
|
69
|
+
'миллионный',
|
|
70
|
+
'миллиардный',
|
|
71
|
+
'триллионный',
|
|
72
|
+
'квадриллионный',
|
|
73
|
+
'квинтиллионный',
|
|
74
|
+
'секстиллионный',
|
|
75
|
+
'септиллионный',
|
|
76
|
+
'октиллионный',
|
|
77
|
+
'нониллионный'
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
// Prefixes for compound ordinal thousands (двух-, трёх-, etc. + тысячный)
|
|
81
|
+
const THOUSAND_PREFIXES = ['', '', 'двух', 'трёх', 'четырёх', 'пяти', 'шести', 'семи', 'восьми', 'девяти']
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Currency Vocabulary (Russian Ruble)
|
|
85
|
+
// ============================================================================
|
|
86
|
+
|
|
87
|
+
// Ruble: masculine, [singular, few, many]
|
|
88
|
+
const RUBLE_FORMS = ['рубль', 'рубля', 'рублей']
|
|
89
|
+
|
|
90
|
+
// Kopeck: feminine, [singular, few, many]
|
|
91
|
+
const KOPECK_FORMS = ['копейка', 'копейки', 'копеек']
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Segment Building
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
function pluralize (n, forms) {
|
|
98
|
+
const num = typeof n === 'bigint' ? Number(n) : n
|
|
99
|
+
const lastDigit = num % 10
|
|
100
|
+
const lastTwoDigits = num % 100
|
|
101
|
+
|
|
102
|
+
if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
|
|
103
|
+
return forms[2]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (lastDigit === 1) return forms[0]
|
|
107
|
+
if (lastDigit >= 2 && lastDigit <= 4) return forms[1]
|
|
108
|
+
return forms[2]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildSegmentMasc (n) {
|
|
112
|
+
if (n === 0) return ''
|
|
113
|
+
|
|
114
|
+
const onesDigit = n % 10
|
|
115
|
+
const tensDigit = Math.trunc(n / 10) % 10
|
|
116
|
+
const hundredsDigit = Math.trunc(n / 100)
|
|
117
|
+
|
|
118
|
+
const parts = []
|
|
119
|
+
|
|
120
|
+
if (hundredsDigit > 0) {
|
|
121
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (tensDigit > 1) {
|
|
125
|
+
parts.push(TENS[tensDigit])
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (tensDigit === 1) {
|
|
129
|
+
parts.push(TEENS[onesDigit])
|
|
130
|
+
} else if (onesDigit > 0) {
|
|
131
|
+
parts.push(ONES_MASC[onesDigit])
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return parts.join(' ')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function buildSegmentFem (n) {
|
|
138
|
+
if (n === 0) return ''
|
|
139
|
+
|
|
140
|
+
const onesDigit = n % 10
|
|
141
|
+
const tensDigit = Math.trunc(n / 10) % 10
|
|
142
|
+
const hundredsDigit = Math.trunc(n / 100)
|
|
143
|
+
|
|
144
|
+
const parts = []
|
|
145
|
+
|
|
146
|
+
if (hundredsDigit > 0) {
|
|
147
|
+
parts.push(HUNDREDS[hundredsDigit])
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (tensDigit > 1) {
|
|
151
|
+
parts.push(TENS[tensDigit])
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (tensDigit === 1) {
|
|
155
|
+
parts.push(TEENS[onesDigit])
|
|
156
|
+
} else if (onesDigit > 0) {
|
|
157
|
+
parts.push(ONES_FEM[onesDigit])
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return parts.join(' ')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// Conversion Functions
|
|
165
|
+
// ============================================================================
|
|
166
|
+
|
|
167
|
+
function integerToWords (n, gender) {
|
|
168
|
+
if (n === 0n) return ZERO
|
|
169
|
+
|
|
170
|
+
const feminine = gender === 'feminine'
|
|
171
|
+
|
|
172
|
+
if (n < 1000n) {
|
|
173
|
+
return feminine ? buildSegmentFem(Number(n)) : buildSegmentMasc(Number(n))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (n < 1_000_000n) {
|
|
177
|
+
const thousands = Number(n / 1000n)
|
|
178
|
+
const remainder = Number(n % 1000n)
|
|
179
|
+
|
|
180
|
+
// Thousands are always feminine in Russian
|
|
181
|
+
const thousandsWord = buildSegmentFem(thousands)
|
|
182
|
+
const scaleWord = pluralize(thousands, SCALE_FORMS[0])
|
|
183
|
+
|
|
184
|
+
let result = thousandsWord + ' ' + scaleWord
|
|
185
|
+
|
|
186
|
+
if (remainder > 0) {
|
|
187
|
+
result += ' ' + (feminine ? buildSegmentFem(remainder) : buildSegmentMasc(remainder))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return result
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return buildLargeNumberWords(n, gender)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function buildLargeNumberWords (n, gender) {
|
|
197
|
+
const feminine = gender === 'feminine'
|
|
198
|
+
const numStr = n.toString()
|
|
199
|
+
const len = numStr.length
|
|
200
|
+
|
|
201
|
+
const segments = []
|
|
202
|
+
const segmentSize = 3
|
|
203
|
+
|
|
204
|
+
const remainderLen = len % segmentSize
|
|
205
|
+
let pos = 0
|
|
206
|
+
if (remainderLen > 0) {
|
|
207
|
+
segments.push(Number(numStr.slice(0, remainderLen)))
|
|
208
|
+
pos = remainderLen
|
|
209
|
+
}
|
|
210
|
+
while (pos < len) {
|
|
211
|
+
segments.push(Number(numStr.slice(pos, pos + segmentSize)))
|
|
212
|
+
pos += segmentSize
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const parts = []
|
|
216
|
+
let scaleIndex = segments.length - 1
|
|
217
|
+
|
|
218
|
+
for (let i = 0; i < segments.length; i++) {
|
|
219
|
+
const segment = segments[i]
|
|
220
|
+
|
|
221
|
+
if (segment !== 0) {
|
|
222
|
+
if (scaleIndex === 0) {
|
|
223
|
+
parts.push(feminine ? buildSegmentFem(segment) : buildSegmentMasc(segment))
|
|
224
|
+
} else {
|
|
225
|
+
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
226
|
+
const scaleWord = pluralize(segment, scaleForms)
|
|
227
|
+
// Thousands (scaleIndex=1) are feminine, others masculine
|
|
228
|
+
const isFeminine = scaleIndex === 1
|
|
229
|
+
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
230
|
+
parts.push(segmentWord + ' ' + scaleWord)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
scaleIndex--
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return parts.join(' ')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function decimalPartToWords (decimalPart, gender) {
|
|
241
|
+
let result = ''
|
|
242
|
+
let i = 0
|
|
243
|
+
|
|
244
|
+
while (i < decimalPart.length && decimalPart[i] === '0') {
|
|
245
|
+
if (result) result += ' '
|
|
246
|
+
result += ZERO
|
|
247
|
+
i++
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const remainder = decimalPart.slice(i)
|
|
251
|
+
if (remainder) {
|
|
252
|
+
if (result) result += ' '
|
|
253
|
+
result += integerToWords(BigInt(remainder), gender)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return result
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Converts a numeric value to Russian words.
|
|
261
|
+
*
|
|
262
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
263
|
+
* @param {Object} [options] - Optional configuration
|
|
264
|
+
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
265
|
+
* @returns {string} The number in Russian words
|
|
266
|
+
*/
|
|
267
|
+
function toCardinal (value, options) {
|
|
268
|
+
options = validateOptions(options)
|
|
269
|
+
const { isNegative, integerPart, decimalPart } = parseCardinalValue(value)
|
|
270
|
+
|
|
271
|
+
// Apply option defaults
|
|
272
|
+
const { gender = 'masculine' } = options
|
|
273
|
+
|
|
274
|
+
let result = ''
|
|
275
|
+
|
|
276
|
+
if (isNegative) {
|
|
277
|
+
result = NEGATIVE + ' '
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
result += integerToWords(integerPart, gender)
|
|
281
|
+
|
|
282
|
+
if (decimalPart) {
|
|
283
|
+
result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart, gender)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return result
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ============================================================================
|
|
290
|
+
// ORDINAL: toOrdinal(value)
|
|
291
|
+
// ============================================================================
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Builds ordinal for a 0-99 segment when it's the final (ordinal) part.
|
|
295
|
+
* Returns ordinal form: "первый", "двадцать первый", etc.
|
|
296
|
+
*
|
|
297
|
+
* @param {number} n - Number 0-99
|
|
298
|
+
* @returns {string} Ordinal words
|
|
299
|
+
*/
|
|
300
|
+
function buildOrdinalTensOnes (n) {
|
|
301
|
+
if (n === 0) return ''
|
|
302
|
+
|
|
303
|
+
const onesDigit = n % 10
|
|
304
|
+
const tensDigit = Math.trunc(n / 10)
|
|
305
|
+
|
|
306
|
+
if (tensDigit === 0) {
|
|
307
|
+
// Single digit: первый, второй, etc.
|
|
308
|
+
return ORDINAL_ONES[onesDigit]
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (tensDigit === 1) {
|
|
312
|
+
// Teens: десятый, одиннадцатый, etc.
|
|
313
|
+
return ORDINAL_TEENS[onesDigit]
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Tens >= 20
|
|
317
|
+
if (onesDigit === 0) {
|
|
318
|
+
// Round tens: двадцатый, тридцатый, etc.
|
|
319
|
+
return ORDINAL_TENS[tensDigit]
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Compound: двадцать первый, тридцать второй, etc.
|
|
323
|
+
return TENS[tensDigit] + ' ' + ORDINAL_ONES[onesDigit]
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Converts a positive integer to Russian ordinal words (masculine nominative).
|
|
328
|
+
*
|
|
329
|
+
* In Russian ordinals, only the LAST component becomes ordinal.
|
|
330
|
+
* E.g., 121 = "сто двадцать первый" (one hundred twenty first)
|
|
331
|
+
*
|
|
332
|
+
* @param {bigint} n - Positive integer to convert
|
|
333
|
+
* @returns {string} Ordinal Russian words
|
|
334
|
+
*/
|
|
335
|
+
function integerToOrdinal (n) {
|
|
336
|
+
// Fast path: numbers < 100
|
|
337
|
+
if (n < 100n) {
|
|
338
|
+
return buildOrdinalTensOnes(Number(n))
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Fast path: numbers < 1000
|
|
342
|
+
if (n < 1000n) {
|
|
343
|
+
const num = Number(n)
|
|
344
|
+
const hundredsDigit = Math.trunc(num / 100)
|
|
345
|
+
const remainder = num % 100
|
|
346
|
+
|
|
347
|
+
if (remainder === 0) {
|
|
348
|
+
// Exact hundreds: сотый, двухсотый, etc.
|
|
349
|
+
return ORDINAL_HUNDREDS[hundredsDigit]
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Has remainder: cardinal hundreds + ordinal remainder
|
|
353
|
+
return HUNDREDS[hundredsDigit] + ' ' + buildOrdinalTensOnes(remainder)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Fast path: numbers < 1,000,000
|
|
357
|
+
if (n < 1_000_000n) {
|
|
358
|
+
const thousands = Number(n / 1000n)
|
|
359
|
+
const remainder = Number(n % 1000n)
|
|
360
|
+
|
|
361
|
+
if (remainder === 0) {
|
|
362
|
+
// Exact thousands: тысячный, двухтысячный, etc.
|
|
363
|
+
if (thousands === 1) {
|
|
364
|
+
return ORDINAL_SCALES[0] // тысячный
|
|
365
|
+
}
|
|
366
|
+
if (thousands < 10) {
|
|
367
|
+
return THOUSAND_PREFIXES[thousands] + ORDINAL_SCALES[0]
|
|
368
|
+
}
|
|
369
|
+
// For larger thousands, use cardinal + тысячный
|
|
370
|
+
return buildSegmentFem(thousands) + ' ' + ORDINAL_SCALES[0]
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Has remainder: cardinal thousands + ordinal remainder
|
|
374
|
+
const thousandsWord = buildSegmentFem(thousands)
|
|
375
|
+
const scaleWord = pluralize(thousands, SCALE_FORMS[0])
|
|
376
|
+
return thousandsWord + ' ' + scaleWord + ' ' + integerToOrdinal(BigInt(remainder))
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// For numbers >= 1,000,000, use scale decomposition
|
|
380
|
+
return buildLargeOrdinal(n)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Builds ordinal words for numbers >= 1,000,000.
|
|
385
|
+
* All segments except the final one are cardinal; final segment is ordinal.
|
|
386
|
+
*
|
|
387
|
+
* @param {bigint} n - Number >= 1,000,000
|
|
388
|
+
* @returns {string} Ordinal Russian words
|
|
389
|
+
*/
|
|
390
|
+
function buildLargeOrdinal (n) {
|
|
391
|
+
const numStr = n.toString()
|
|
392
|
+
const len = numStr.length
|
|
393
|
+
|
|
394
|
+
// Extract segments (most-significant first)
|
|
395
|
+
const segments = []
|
|
396
|
+
const segmentSize = 3
|
|
397
|
+
|
|
398
|
+
const remainderLen = len % segmentSize
|
|
399
|
+
let pos = 0
|
|
400
|
+
if (remainderLen > 0) {
|
|
401
|
+
segments.push(Number(numStr.slice(0, remainderLen)))
|
|
402
|
+
pos = remainderLen
|
|
403
|
+
}
|
|
404
|
+
while (pos < len) {
|
|
405
|
+
segments.push(Number(numStr.slice(pos, pos + segmentSize)))
|
|
406
|
+
pos += segmentSize
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Find the last non-zero segment
|
|
410
|
+
let lastNonZeroIdx = segments.length - 1
|
|
411
|
+
while (lastNonZeroIdx >= 0 && segments[lastNonZeroIdx] === 0) {
|
|
412
|
+
lastNonZeroIdx--
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const parts = []
|
|
416
|
+
let scaleIndex = segments.length - 1
|
|
417
|
+
|
|
418
|
+
for (let i = 0; i < segments.length; i++) {
|
|
419
|
+
const segment = segments[i]
|
|
420
|
+
|
|
421
|
+
if (segment !== 0) {
|
|
422
|
+
const isLastNonZero = (i === lastNonZeroIdx)
|
|
423
|
+
|
|
424
|
+
if (scaleIndex === 0) {
|
|
425
|
+
// Units position (no scale)
|
|
426
|
+
if (isLastNonZero) {
|
|
427
|
+
parts.push(integerToOrdinal(BigInt(segment)))
|
|
428
|
+
} else {
|
|
429
|
+
parts.push(buildSegmentMasc(segment))
|
|
430
|
+
}
|
|
431
|
+
} else {
|
|
432
|
+
// Has scale word
|
|
433
|
+
if (isLastNonZero) {
|
|
434
|
+
// This scale position is the final ordinal
|
|
435
|
+
if (segment === 1) {
|
|
436
|
+
parts.push(ORDINAL_SCALES[scaleIndex - 1])
|
|
437
|
+
} else {
|
|
438
|
+
// Use cardinal segment + ordinal scale
|
|
439
|
+
const isFeminine = scaleIndex === 1 // thousands are feminine
|
|
440
|
+
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
441
|
+
parts.push(segmentWord + ' ' + ORDINAL_SCALES[scaleIndex - 1])
|
|
442
|
+
}
|
|
443
|
+
} else {
|
|
444
|
+
// Not the final segment: use cardinal
|
|
445
|
+
const scaleForms = SCALE_FORMS[scaleIndex - 1]
|
|
446
|
+
const scaleWord = pluralize(segment, scaleForms)
|
|
447
|
+
const isFeminine = scaleIndex === 1
|
|
448
|
+
const segmentWord = isFeminine ? buildSegmentFem(segment) : buildSegmentMasc(segment)
|
|
449
|
+
parts.push(segmentWord + ' ' + scaleWord)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
scaleIndex--
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return parts.join(' ')
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Converts a numeric value to Russian ordinal words (masculine nominative).
|
|
462
|
+
*
|
|
463
|
+
* @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
|
|
464
|
+
* @returns {string} The number as ordinal words (e.g., "первый", "сорок второй")
|
|
465
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
466
|
+
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* toOrdinal(1) // 'первый'
|
|
470
|
+
* toOrdinal(2) // 'второй'
|
|
471
|
+
* toOrdinal(3) // 'третий'
|
|
472
|
+
* toOrdinal(21) // 'двадцать первый'
|
|
473
|
+
* toOrdinal(42) // 'сорок второй'
|
|
474
|
+
* toOrdinal(100) // 'сотый'
|
|
475
|
+
* toOrdinal(101) // 'сто первый'
|
|
476
|
+
* toOrdinal(1000) // 'тысячный'
|
|
477
|
+
*/
|
|
478
|
+
function toOrdinal (value) {
|
|
479
|
+
const integerPart = parseOrdinalValue(value)
|
|
480
|
+
return integerToOrdinal(integerPart)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ============================================================================
|
|
484
|
+
// CURRENCY: toCurrency(value, options?)
|
|
485
|
+
// ============================================================================
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Converts a numeric value to Russian currency words (Russian Ruble).
|
|
489
|
+
*
|
|
490
|
+
* @param {number | string | bigint} value - The currency amount to convert
|
|
491
|
+
* @param {Object} [options] - Optional configuration
|
|
492
|
+
* @param {boolean} [options.and=true] - Use "и" between rubles and kopecks
|
|
493
|
+
* @returns {string} The amount in Russian currency words
|
|
494
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
495
|
+
* @throws {Error} If value is not a valid number format
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* toCurrency(42.50) // 'сорок два рубля и пятьдесят копеек'
|
|
499
|
+
* toCurrency(1) // 'один рубль'
|
|
500
|
+
* toCurrency(0.99) // 'девяносто девять копеек'
|
|
501
|
+
* toCurrency(0.01) // 'одна копейка'
|
|
502
|
+
* toCurrency(42.50, { and: false }) // 'сорок два рубля пятьдесят копеек'
|
|
503
|
+
*/
|
|
504
|
+
function toCurrency (value, options) {
|
|
505
|
+
options = validateOptions(options)
|
|
506
|
+
const { isNegative, dollars: rubles, cents: kopecks } = parseCurrencyValue(value)
|
|
507
|
+
const { and: useAnd = true } = options
|
|
508
|
+
|
|
509
|
+
// Build result
|
|
510
|
+
let result = ''
|
|
511
|
+
if (isNegative) result = NEGATIVE + ' '
|
|
512
|
+
|
|
513
|
+
// Rubles part (masculine) - show if non-zero, or if no kopecks
|
|
514
|
+
if (rubles > 0n || kopecks === 0n) {
|
|
515
|
+
result += integerToWords(rubles, 'masculine')
|
|
516
|
+
result += ' ' + pluralize(rubles, RUBLE_FORMS)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Kopecks part (feminine)
|
|
520
|
+
if (kopecks > 0n) {
|
|
521
|
+
if (rubles > 0n) {
|
|
522
|
+
result += useAnd ? ' и ' : ' '
|
|
523
|
+
}
|
|
524
|
+
result += integerToWords(kopecks, 'feminine')
|
|
525
|
+
result += ' ' + pluralize(kopecks, KOPECK_FORMS)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return result
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// ============================================================================
|
|
532
|
+
// Exports
|
|
533
|
+
// ============================================================================
|
|
534
|
+
|
|
535
|
+
export { toCardinal, toOrdinal, toCurrency }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a numeric value to Serbian (Cyrillic) words.
|
|
3
|
+
*
|
|
4
|
+
* @param {number | string | bigint} value - The numeric value to convert
|
|
5
|
+
* @param {Object} [options] - Optional configuration
|
|
6
|
+
* @param {('masculine'|'feminine')} [options.gender='masculine'] - Grammatical gender
|
|
7
|
+
* @returns {string} The number in Serbian Cyrillic words
|
|
8
|
+
*/
|
|
9
|
+
export function toCardinal(value: number | string | bigint, options?: {
|
|
10
|
+
gender?: "masculine" | "feminine" | undefined;
|
|
11
|
+
}): string;
|
|
12
|
+
/**
|
|
13
|
+
* Converts a numeric value to Serbian ordinal words (masculine nominative).
|
|
14
|
+
*
|
|
15
|
+
* @param {number | string | bigint} value - The numeric value to convert (must be a positive integer)
|
|
16
|
+
* @returns {string} The number as ordinal words (e.g., "први", "четрдесет други")
|
|
17
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
18
|
+
* @throws {RangeError} If value is negative, zero, or has a decimal part
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* toOrdinal(1) // 'први'
|
|
22
|
+
* toOrdinal(2) // 'други'
|
|
23
|
+
* toOrdinal(3) // 'трећи'
|
|
24
|
+
* toOrdinal(21) // 'двадесет први'
|
|
25
|
+
* toOrdinal(42) // 'четрдесет други'
|
|
26
|
+
* toOrdinal(100) // 'стоти'
|
|
27
|
+
* toOrdinal(1000) // 'хиљадити'
|
|
28
|
+
*/
|
|
29
|
+
export function toOrdinal(value: number | string | bigint): string;
|
|
30
|
+
/**
|
|
31
|
+
* Converts a numeric value to Serbian currency words (Serbian Dinar).
|
|
32
|
+
*
|
|
33
|
+
* @param {number | string | bigint} value - The currency amount to convert
|
|
34
|
+
* @param {Object} [options] - Optional configuration
|
|
35
|
+
* @param {boolean} [options.and=true] - Use "и" between dinars and para
|
|
36
|
+
* @returns {string} The amount in Serbian currency words
|
|
37
|
+
* @throws {TypeError} If value is not a valid numeric type
|
|
38
|
+
* @throws {Error} If value is not a valid number format
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* toCurrency(42.50) // 'четрдесет два динара и педесет пара'
|
|
42
|
+
* toCurrency(1) // 'један динар'
|
|
43
|
+
* toCurrency(0.99) // 'деведесет девет пара'
|
|
44
|
+
* toCurrency(0.01) // 'једна пара'
|
|
45
|
+
* toCurrency(42.50, { and: false }) // 'четрдесет два динара педесет пара'
|
|
46
|
+
*/
|
|
47
|
+
export function toCurrency(value: number | string | bigint, options?: {
|
|
48
|
+
and?: boolean | undefined;
|
|
49
|
+
}): string;
|