n2words 3.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 (154) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/languages/am-Latn.js +2 -2
  3. package/dist/languages/am-Latn.js.map +1 -1
  4. package/dist/languages/am.js +2 -2
  5. package/dist/languages/am.js.map +1 -1
  6. package/dist/languages/ar.js +1 -1
  7. package/dist/languages/az.js +2 -2
  8. package/dist/languages/az.js.map +1 -1
  9. package/dist/languages/bn.js +2 -2
  10. package/dist/languages/bn.js.map +1 -1
  11. package/dist/languages/cs.js +2 -2
  12. package/dist/languages/cs.js.map +1 -1
  13. package/dist/languages/da.js +2 -2
  14. package/dist/languages/da.js.map +1 -1
  15. package/dist/languages/de.js +2 -2
  16. package/dist/languages/de.js.map +1 -1
  17. package/dist/languages/el.js +2 -2
  18. package/dist/languages/el.js.map +1 -1
  19. package/dist/languages/en.js +2 -2
  20. package/dist/languages/en.js.map +1 -1
  21. package/dist/languages/es.js +2 -2
  22. package/dist/languages/es.js.map +1 -1
  23. package/dist/languages/fa.js +1 -1
  24. package/dist/languages/fi.js +2 -2
  25. package/dist/languages/fi.js.map +1 -1
  26. package/dist/languages/fil.js +2 -2
  27. package/dist/languages/fil.js.map +1 -1
  28. package/dist/languages/fr-BE.js +2 -2
  29. package/dist/languages/fr-BE.js.map +1 -1
  30. package/dist/languages/fr.js +2 -2
  31. package/dist/languages/fr.js.map +1 -1
  32. package/dist/languages/gu.js +2 -2
  33. package/dist/languages/gu.js.map +1 -1
  34. package/dist/languages/ha.js +2 -2
  35. package/dist/languages/ha.js.map +1 -1
  36. package/dist/languages/hbo.js +2 -2
  37. package/dist/languages/hbo.js.map +1 -1
  38. package/dist/languages/he.js +2 -2
  39. package/dist/languages/he.js.map +1 -1
  40. package/dist/languages/hi.js +2 -2
  41. package/dist/languages/hi.js.map +1 -1
  42. package/dist/languages/hr.js +2 -2
  43. package/dist/languages/hr.js.map +1 -1
  44. package/dist/languages/hu.js +1 -1
  45. package/dist/languages/id.js +2 -2
  46. package/dist/languages/id.js.map +1 -1
  47. package/dist/languages/it.js +2 -2
  48. package/dist/languages/it.js.map +1 -1
  49. package/dist/languages/ja.js +2 -2
  50. package/dist/languages/ja.js.map +1 -1
  51. package/dist/languages/ka.js +3 -0
  52. package/dist/languages/ka.js.map +1 -0
  53. package/dist/languages/kn.js +2 -2
  54. package/dist/languages/kn.js.map +1 -1
  55. package/dist/languages/ko.js +2 -2
  56. package/dist/languages/ko.js.map +1 -1
  57. package/dist/languages/lt.js +2 -2
  58. package/dist/languages/lt.js.map +1 -1
  59. package/dist/languages/lv.js +2 -2
  60. package/dist/languages/lv.js.map +1 -1
  61. package/dist/languages/mr.js +2 -2
  62. package/dist/languages/mr.js.map +1 -1
  63. package/dist/languages/ms.js +2 -2
  64. package/dist/languages/ms.js.map +1 -1
  65. package/dist/languages/nb.js +2 -2
  66. package/dist/languages/nb.js.map +1 -1
  67. package/dist/languages/nl.js +2 -2
  68. package/dist/languages/nl.js.map +1 -1
  69. package/dist/languages/pa.js +2 -2
  70. package/dist/languages/pa.js.map +1 -1
  71. package/dist/languages/pl.js +2 -2
  72. package/dist/languages/pl.js.map +1 -1
  73. package/dist/languages/pt.js +2 -2
  74. package/dist/languages/pt.js.map +1 -1
  75. package/dist/languages/ro.js +1 -1
  76. package/dist/languages/ru.js +2 -2
  77. package/dist/languages/ru.js.map +1 -1
  78. package/dist/languages/sr-Cyrl.js +2 -2
  79. package/dist/languages/sr-Cyrl.js.map +1 -1
  80. package/dist/languages/sr-Latn.js +2 -2
  81. package/dist/languages/sr-Latn.js.map +1 -1
  82. package/dist/languages/sv.js +2 -2
  83. package/dist/languages/sv.js.map +1 -1
  84. package/dist/languages/sw.js +1 -1
  85. package/dist/languages/ta.js +2 -2
  86. package/dist/languages/ta.js.map +1 -1
  87. package/dist/languages/te.js +2 -2
  88. package/dist/languages/te.js.map +1 -1
  89. package/dist/languages/th.js +1 -1
  90. package/dist/languages/tr.js +2 -2
  91. package/dist/languages/tr.js.map +1 -1
  92. package/dist/languages/uk.js +2 -2
  93. package/dist/languages/uk.js.map +1 -1
  94. package/dist/languages/ur.js +2 -2
  95. package/dist/languages/ur.js.map +1 -1
  96. package/dist/languages/vi.js +2 -2
  97. package/dist/languages/vi.js.map +1 -1
  98. package/dist/languages/yo.js +3 -0
  99. package/dist/languages/yo.js.map +1 -0
  100. package/dist/languages/zh-Hans.js +1 -1
  101. package/dist/languages/zh-Hant.js +1 -1
  102. package/dist/n2words.js +2 -2
  103. package/dist/n2words.js.map +1 -1
  104. package/lib/languages/am-Latn.js +4 -9
  105. package/lib/languages/am.js +4 -9
  106. package/lib/languages/az.js +4 -9
  107. package/lib/languages/bn.js +51 -30
  108. package/lib/languages/cs.js +9 -26
  109. package/lib/languages/da.js +6 -13
  110. package/lib/languages/de.js +21 -33
  111. package/lib/languages/el.js +6 -13
  112. package/lib/languages/en.js +44 -58
  113. package/lib/languages/es.js +10 -30
  114. package/lib/languages/fi.js +6 -13
  115. package/lib/languages/fil.js +6 -17
  116. package/lib/languages/fr-BE.js +17 -26
  117. package/lib/languages/fr.js +18 -31
  118. package/lib/languages/gu.js +43 -30
  119. package/lib/languages/ha.js +4 -9
  120. package/lib/languages/hbo.js +7 -25
  121. package/lib/languages/he.js +7 -18
  122. package/lib/languages/hi.js +55 -30
  123. package/lib/languages/hr.js +61 -55
  124. package/lib/languages/id.js +8 -13
  125. package/lib/languages/it.js +64 -99
  126. package/lib/languages/ja.js +8 -20
  127. package/lib/languages/ka.d.ts +17 -0
  128. package/lib/languages/ka.js +291 -0
  129. package/lib/languages/kn.js +43 -30
  130. package/lib/languages/ko.js +6 -13
  131. package/lib/languages/lt.js +6 -15
  132. package/lib/languages/lv.js +6 -15
  133. package/lib/languages/mr.js +43 -30
  134. package/lib/languages/ms.js +8 -13
  135. package/lib/languages/nb.js +13 -23
  136. package/lib/languages/nl.js +11 -29
  137. package/lib/languages/pa.js +32 -37
  138. package/lib/languages/pl.js +7 -20
  139. package/lib/languages/pt.js +17 -31
  140. package/lib/languages/ru.js +64 -59
  141. package/lib/languages/sr-Cyrl.js +58 -52
  142. package/lib/languages/sr-Latn.js +58 -52
  143. package/lib/languages/sv.js +14 -24
  144. package/lib/languages/ta.js +55 -42
  145. package/lib/languages/te.js +33 -41
  146. package/lib/languages/tr.js +7 -18
  147. package/lib/languages/uk.js +61 -55
  148. package/lib/languages/ur.js +32 -37
  149. package/lib/languages/vi.js +23 -43
  150. package/lib/languages/yo.d.ts +7 -0
  151. package/lib/languages/yo.js +303 -0
  152. package/lib/n2words.d.ts +3 -1
  153. package/lib/n2words.js +4 -0
  154. package/package.json +1 -1
@@ -1,3 +1,3 @@
1
- /*! n2words/vi v3.0.0 | MIT License | github.com/forzagreen/n2words */
2
- var e,n;e=this,n=function(e){"use strict";function n(e){const n="-"===e[0];n&&(e=e.slice(1));const t=e.indexOf(".");if(-1===t)return{isNegative:n,integerPart:BigInt(e)};const i=e.slice(0,t)||"0",r=e.slice(t+1);return{isNegative:n,integerPart:BigInt(i),decimalPart:r}}function t(e){const[n,t]=e.toLowerCase().split("e"),i=parseInt(t,10),r=n.indexOf("."),o=-1===r?n:n.slice(0,r)+n.slice(r+1),l=(-1===r?n.length:r)+i;return l>=o.length?o+"0".repeat(l-o.length):l<=0?"0."+"0".repeat(-l)+o:o.slice(0,l)+"."+o.slice(l)}const i=["không","một","hai","ba","bốn","năm","sáu","bảy","tám","chín"],r=["","nghìn","triệu","tỷ","nghìn tỷ","trăm nghìn tỷ","Quintillion","Sextillion","Septillion","Octillion","Nonillion","Decillion","Undecillion","Duodecillion","Tredecillion","Quattuordecillion","Sexdecillion","Septendecillion","Octodecillion","Novemdecillion","Vigintillion"],o="không";function l(e){if(0===e)return i[0];if(e<10)return i[e];if(e<20){const n=e-10;return 0===n?"mười":5===n?"mười lăm":"mười "+i[n]}const n=e%10,t=Math.floor(e/10),r=i[t]+" mươi";return 0===n?r:1===n?r+" mốt":5===n?r+" lăm":r+" "+i[n]}function u(e){if(0===e)return"";const n=Math.floor(e/100),t=e%100;let r="";return n>0&&(r=i[n]+" trăm"),t>0&&(t<10?r?(r+=" lẻ ",r+=5===t?"năm":i[t]):r=i[t]:(r&&(r+=" "),r+=s[t])),r}const s=new Array(100);for(let e=0;e<100;e++)s[e]=l(e);const c=new Array(1e3);for(let e=0;e<1e3;e++)c[e]=u(e);const f=new Array(100);f[0]="";for(let e=1;e<10;e++)f[e]="lẻ "+(5===e?"năm":i[e]);for(let e=10;e<100;e++)f[e]="lẻ "+s[e];function g(e){if(0n===e)return o;if(e<100n)return s[Number(e)];if(e<1000n)return c[Number(e)];if(e<1000000n){const n=Number(e/1000n),t=Number(e%1000n),i=c[n]+" "+r[1];return 0===t?i:t<100?i+" "+f[t]:i+" "+c[t]}return function(e){const n=e.toString(),t=n.length,i=[],l=t%3;let u=0;for(l>0&&(i.push(Number(n.slice(0,l))),u=l);u<t;)i.push(Number(n.slice(u,u+3))),u+=3;const s=[];let g=i.length-1;for(let e=0;e<i.length;e++){const n=i[e];if(0!==n){const e=c[n];e&&s.push(g>0?e+" "+r[g]:e)}g--}const a=s.length;if(0===a)return o;if(1===a)return s[0];const m=i[i.length-1];if(m>0&&m<=99){let e=s[0];for(let n=1;n<a-1;n++)e+=" "+s[n];return e+" "+f[m]}let d=s[0];for(let e=1;e<a;e++)d+=" "+s[e];return d}(e)}e.vi=function(e){const{isNegative:i,integerPart:r,decimalPart:l}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:n(function(e){const n=e.toString();return n.includes("e")||n.includes("E")?t(n):n}(e))}if("string"===i)return n(function(e){const n=e.trim();if(0===n.length||Number.isNaN(Number(n)))throw new Error(`Invalid number format: "${e}"`);return n.includes("e")||n.includes("E")?t(n):n}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e);let u="";return i&&(u="âm "),u+=g(r),l&&(u+=" phẩy "+function(e){let n="",t=0;for(;t<e.length&&"0"===e[t];)n&&(n+=" "),n+=o,t++;const i=e.slice(t);return i&&(n&&(n+=" "),n+=g(BigInt(i))),n}(l)),u}},"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
1
+ /*! n2words/vi v3.1.0 | MIT License | github.com/forzagreen/n2words */
2
+ var e,n;e=this,n=function(e){"use strict";function n(e){const n="-"===e[0];n&&(e=e.slice(1));const t=e.indexOf(".");if(-1===t)return{isNegative:n,integerPart:BigInt(e)};const i=e.slice(0,t)||"0",r=e.slice(t+1);return{isNegative:n,integerPart:BigInt(i),decimalPart:r}}function t(e){const[n,t]=e.toLowerCase().split("e"),i=parseInt(t,10),r=n.indexOf("."),o=-1===r?n:n.slice(0,r)+n.slice(r+1),l=(-1===r?n.length:r)+i;return l>=o.length?o+"0".repeat(l-o.length):l<=0?"0."+"0".repeat(-l)+o:o.slice(0,l)+"."+o.slice(l)}const i=["không","một","hai","ba","bốn","năm","sáu","bảy","tám","chín"],r=["","nghìn","triệu","tỷ","nghìn tỷ","trăm nghìn tỷ","Quintillion","Sextillion","Septillion","Octillion","Nonillion","Decillion","Undecillion","Duodecillion","Tredecillion","Quattuordecillion","Sexdecillion","Septendecillion","Octodecillion","Novemdecillion","Vigintillion"],o="không";function l(e){if(0===e)return i[0];if(e<10)return i[e];if(e<20){const n=e-10;return 0===n?"mười":5===n?"mười lăm":"mười "+i[n]}const n=e%10,t=Math.floor(e/10),r=i[t]+" mươi";return 0===n?r:1===n?r+" mốt":5===n?r+" lăm":r+" "+i[n]}function u(e){if(0===e)return"";const n=Math.floor(e/100),t=e%100;let r="";return n>0&&(r=i[n]+" trăm"),t>0&&(t<10?r?(r+=" lẻ ",r+=5===t?"năm":i[t]):r=i[t]:(r&&(r+=" "),r+=l(t))),r}function c(e){return 0===e?"":e<10?"lẻ "+(5===e?"năm":i[e]):"lẻ "+l(e)}function s(e){if(0n===e)return o;if(e<100n)return l(Number(e));if(e<1000n)return u(Number(e));if(e<1000000n){const n=Number(e/1000n),t=Number(e%1000n),i=u(n)+" "+r[1];return 0===t?i:t<100?i+" "+c(t):i+" "+u(t)}return function(e){const n=e.toString(),t=n.length,i=[],l=t%3;let s=0;for(l>0&&(i.push(Number(n.slice(0,l))),s=l);s<t;)i.push(Number(n.slice(s,s+3))),s+=3;const f=[];let g=i.length-1;for(let e=0;e<i.length;e++){const n=i[e];if(0!==n){const e=u(n);e&&f.push(g>0?e+" "+r[g]:e)}g--}const a=f.length;if(0===a)return o;if(1===a)return f[0];const m=i[i.length-1];if(m>0&&m<=99){let e=f[0];for(let n=1;n<a-1;n++)e+=" "+f[n];return e+" "+c(m)}let d=f[0];for(let e=1;e<a;e++)d+=" "+f[e];return d}(e)}e.vi=function(e){const{isNegative:i,integerPart:r,decimalPart:l}=function(e){const i=typeof e;if("bigint"===i)return e<0n?{isNegative:!0,integerPart:-e}:{isNegative:!1,integerPart:e};if("number"===i){if(!Number.isFinite(e))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(e)?e<0?{isNegative:!0,integerPart:BigInt(-e)}:{isNegative:!1,integerPart:BigInt(e)}:n(function(e){const n=e.toString();return n.includes("e")||n.includes("E")?t(n):n}(e))}if("string"===i)return n(function(e){const n=e.trim();if(0===n.length||Number.isNaN(Number(n)))throw new Error(`Invalid number format: "${e}"`);return n.includes("e")||n.includes("E")?t(n):n}(e));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${i}`)}(e);let u="";return i&&(u="âm "),u+=s(r),l&&(u+=" phẩy "+function(e){let n="",t=0;for(;t<e.length&&"0"===e[t];)n&&(n+=" "),n+=o,t++;const i=e.slice(t);return i&&(n&&(n+=" "),n+=s(BigInt(i))),n}(l)),u}},"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).n2words=e.n2words||{});
3
3
  //# sourceMappingURL=vi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vi.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/vi.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Vietnamese language converter - Functional Implementation v2\n *\n * A performance-optimized number-to-words converter using precomputed lookup tables.\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Key optimization: Precompute all segment values (0-999) at module load.\n * This eliminates all per-call string manipulation for segment conversion.\n *\n * Vietnamese-specific rules (handled in precomputation):\n * - Special pronunciation: \"lăm\" for 5 in tens position, \"mốt\" for final 1\n * - \"Lẻ\" (odd/extra) marker when tens place is zero after hundreds/scales\n * - Short scale system with Vietnamese words (nghìn, triệu, tỷ)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Base vocabulary for building lookup tables\nconst ONES = ['không', 'một', 'hai', 'ba', 'bốn', 'năm', 'sáu', 'bảy', 'tám', 'chín']\n\n// Scale words indexed by scale level (0 = units, 1 = thousands, etc.)\nconst SCALES = [\n '', 'nghìn', 'triệu', 'tỷ', 'nghìn tỷ', 'trăm nghìn tỷ',\n 'Quintillion', 'Sextillion', 'Septillion', 'Octillion',\n 'Nonillion', 'Decillion', 'Undecillion', 'Duodecillion',\n 'Tredecillion', 'Quattuordecillion', 'Sexdecillion',\n 'Septendecillion', 'Octodecillion', 'Novemdecillion', 'Vigintillion'\n]\n\nconst HUNDRED = 'trăm'\nconst ZERO = 'không'\nconst NEGATIVE = 'âm'\nconst DECIMAL_SEP = 'phẩy'\nconst LE = 'lẻ' // \"odd/extra\" marker for gaps\n\n// Special forms\nconst MOT_FINAL = 'mốt' // 1 in tens position (21, 31, etc.)\nconst LAM = 'lăm' // 5 in tens position (25, 35, etc.)\n\n// ============================================================================\n// Precomputed Lookup Tables (built once at module load)\n// ============================================================================\n\n/**\n * Builds word for 0-99 with special forms (mốt, lăm).\n * Only used during table construction.\n */\nfunction buildBelowHundred (n) {\n if (n === 0) return ONES[0]\n if (n < 10) return ONES[n]\n\n // Teens: 10-19\n if (n < 20) {\n const ones = n - 10\n if (ones === 0) return 'mười'\n if (ones === 5) return 'mười lăm'\n return 'mười ' + ONES[ones]\n }\n\n // 20-99\n const ones = n % 10\n const tens = Math.floor(n / 10)\n const tensWord = ONES[tens] + ' mươi'\n\n if (ones === 0) return tensWord\n if (ones === 1) return tensWord + ' ' + MOT_FINAL\n if (ones === 5) return tensWord + ' ' + LAM\n return tensWord + ' ' + ONES[ones]\n}\n\n/**\n * Builds segment word for 0-999.\n * Only used during table construction.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const hundreds = Math.floor(n / 100)\n const remainder = n % 100\n\n let result = ''\n\n if (hundreds > 0) {\n result = ONES[hundreds] + ' ' + HUNDRED\n }\n\n if (remainder > 0) {\n if (remainder < 10) {\n // Single digit after hundreds needs \"lẻ\"\n if (result) {\n result += ' ' + LE + ' '\n // Use \"năm\" not \"lăm\" after lẻ\n result += remainder === 5 ? 'năm' : ONES[remainder]\n } else {\n result = ONES[remainder]\n }\n } else {\n // 10-99 after hundreds\n if (result) result += ' '\n result += BELOW_100[remainder]\n }\n }\n\n return result\n}\n\n// Precompute all 100 below-hundred words (0-99)\n// BELOW_100[n] gives the Vietnamese word for n\nconst BELOW_100 = new Array(100)\nfor (let i = 0; i < 100; i++) {\n BELOW_100[i] = buildBelowHundred(i)\n}\n\n// Precompute all 1000 segment words (0-999)\n// SEGMENTS[n] gives the Vietnamese word for n within a segment\nconst SEGMENTS = new Array(1000)\nfor (let i = 0; i < 1000; i++) {\n SEGMENTS[i] = buildSegment(i)\n}\n\n// Precompute \"lẻ\" prefixed versions for small remainders after scale words\n// LE_SEGMENTS[n] gives \"lẻ X\" for n in range 1-99\nconst LE_SEGMENTS = new Array(100)\nLE_SEGMENTS[0] = ''\nfor (let i = 1; i < 10; i++) {\n // Use \"năm\" not \"lăm\" after lẻ\n LE_SEGMENTS[i] = LE + ' ' + (i === 5 ? 'năm' : ONES[i])\n}\nfor (let i = 10; i < 100; i++) {\n LE_SEGMENTS[i] = LE + ' ' + BELOW_100[i]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Vietnamese words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Vietnamese words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 100 (direct lookup)\n if (n < 100n) {\n return BELOW_100[Number(n)]\n }\n\n // Fast path: numbers < 1000 (direct lookup)\n if (n < 1000n) {\n return SEGMENTS[Number(n)]\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n const thousandsWords = SEGMENTS[thousands] + ' ' + SCALES[1]\n\n if (remainder === 0) {\n return thousandsWords\n }\n\n // Check if remainder needs \"lẻ\" marker (< 100)\n if (remainder < 100) {\n return thousandsWords + ' ' + LE_SEGMENTS[remainder]\n }\n\n return thousandsWords + ' ' + SEGMENTS[remainder]\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Vietnamese words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n if (segment !== 0) {\n const words = SEGMENTS[segment]\n if (words) {\n if (scaleIndex > 0) {\n parts.push(words + ' ' + SCALES[scaleIndex])\n } else {\n parts.push(words)\n }\n }\n }\n scaleIndex--\n }\n\n // Join with \"lẻ\" logic for small remainders\n const partsLen = parts.length\n if (partsLen === 0) return ZERO\n if (partsLen === 1) return parts[0]\n\n // Check if final segment needs \"lẻ\" marker (remainder <= 99 after scale word)\n const lastSegment = segments[segments.length - 1]\n if (lastSegment > 0 && lastSegment <= 99) {\n // Last segment is small (no hundreds), needs \"lẻ\" after scale word\n let result = parts[0]\n for (let i = 1; i < partsLen - 1; i++) {\n result += ' ' + parts[i]\n }\n return result + ' ' + LE_SEGMENTS[lastSegment]\n }\n\n // Join with spaces\n let result = parts[0]\n for (let i = 1; i < partsLen; i++) {\n result += ' ' + parts[i]\n }\n return result\n}\n\n/**\n * Converts decimal digits to Vietnamese words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Vietnamese words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Vietnamese words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Vietnamese words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // 'bốn mươi hai'\n * toWords(101) // 'một trăm lẻ một'\n * toWords(1000000) // 'một triệu'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","SCALES","ZERO","buildBelowHundred","n","ones","tens","Math","floor","tensWord","buildSegment","hundreds","remainder","result","BELOW_100","Array","i","SEGMENTS","LE_SEGMENTS","LE","integerToWords","Number","thousands","thousandsWords","numStr","toString","len","segments","remainderLen","pos","push","parts","scaleIndex","segment","words","partsLen","lastSegment","buildLargeNumberWords","value","type","isFinite","Error","isSafeInteger","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","NEGATIVE","decimalPartToWords"],"mappings":"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"}
1
+ {"version":3,"file":"vi.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/vi.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Vietnamese language converter - Functional Implementation\n *\n * Self-contained module with its own input validation, ready for subpath exports.\n *\n * Vietnamese-specific rules:\n * - Special pronunciation: \"lăm\" for 5 in tens position, \"mốt\" for final 1\n * - \"Lẻ\" (odd/extra) marker when tens place is zero after hundreds/scales\n * - Short scale system with Vietnamese words (nghìn, triệu, tỷ)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Base vocabulary for building lookup tables\nconst ONES = ['không', 'một', 'hai', 'ba', 'bốn', 'năm', 'sáu', 'bảy', 'tám', 'chín']\n\n// Scale words indexed by scale level (0 = units, 1 = thousands, etc.)\nconst SCALES = [\n '', 'nghìn', 'triệu', 'tỷ', 'nghìn tỷ', 'trăm nghìn tỷ',\n 'Quintillion', 'Sextillion', 'Septillion', 'Octillion',\n 'Nonillion', 'Decillion', 'Undecillion', 'Duodecillion',\n 'Tredecillion', 'Quattuordecillion', 'Sexdecillion',\n 'Septendecillion', 'Octodecillion', 'Novemdecillion', 'Vigintillion'\n]\n\nconst HUNDRED = 'trăm'\nconst ZERO = 'không'\nconst NEGATIVE = 'âm'\nconst DECIMAL_SEP = 'phẩy'\nconst LE = 'lẻ' // \"odd/extra\" marker for gaps\n\n// Special forms\nconst MOT_FINAL = 'mốt' // 1 in tens position (21, 31, etc.)\nconst LAM = 'lăm' // 5 in tens position (25, 35, etc.)\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds word for 0-99 with special forms (mốt, lăm).\n */\nfunction buildBelowHundred (n) {\n if (n === 0) return ONES[0]\n if (n < 10) return ONES[n]\n\n // Teens: 10-19\n if (n < 20) {\n const ones = n - 10\n if (ones === 0) return 'mười'\n if (ones === 5) return 'mười lăm'\n return 'mười ' + ONES[ones]\n }\n\n // 20-99\n const ones = n % 10\n const tens = Math.floor(n / 10)\n const tensWord = ONES[tens] + ' mươi'\n\n if (ones === 0) return tensWord\n if (ones === 1) return tensWord + ' ' + MOT_FINAL\n if (ones === 5) return tensWord + ' ' + LAM\n return tensWord + ' ' + ONES[ones]\n}\n\n/**\n * Builds segment word for 0-999.\n */\nfunction buildSegment (n) {\n if (n === 0) return ''\n\n const hundreds = Math.floor(n / 100)\n const remainder = n % 100\n\n let result = ''\n\n if (hundreds > 0) {\n result = ONES[hundreds] + ' ' + HUNDRED\n }\n\n if (remainder > 0) {\n if (remainder < 10) {\n // Single digit after hundreds needs \"lẻ\"\n if (result) {\n result += ' ' + LE + ' '\n // Use \"năm\" not \"lăm\" after lẻ\n result += remainder === 5 ? 'năm' : ONES[remainder]\n } else {\n result = ONES[remainder]\n }\n } else {\n // 10-99 after hundreds\n if (result) result += ' '\n result += buildBelowHundred(remainder)\n }\n }\n\n return result\n}\n\n/**\n * Builds \"lẻ\" prefixed word for small remainders (1-99) after scale words.\n */\nfunction buildLeSegment (n) {\n if (n === 0) return ''\n if (n < 10) {\n // Use \"năm\" not \"lăm\" after lẻ\n return LE + ' ' + (n === 5 ? 'năm' : ONES[n])\n }\n return LE + ' ' + buildBelowHundred(n)\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts a non-negative integer to Vietnamese words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Vietnamese words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 100\n if (n < 100n) {\n return buildBelowHundred(Number(n))\n }\n\n // Fast path: numbers < 1000\n if (n < 1000n) {\n return buildSegment(Number(n))\n }\n\n // Fast path: numbers < 1,000,000 (thousands)\n if (n < 1_000_000n) {\n const thousands = Number(n / 1000n)\n const remainder = Number(n % 1000n)\n\n const thousandsWords = buildSegment(thousands) + ' ' + SCALES[1]\n\n if (remainder === 0) {\n return thousandsWords\n }\n\n // Check if remainder needs \"lẻ\" marker (< 100)\n if (remainder < 100) {\n return thousandsWords + ' ' + buildLeSegment(remainder)\n }\n\n return thousandsWords + ' ' + buildSegment(remainder)\n }\n\n // For numbers >= 1,000,000, use scale decomposition\n return buildLargeNumberWords(n)\n}\n\n/**\n * Builds words for numbers >= 1,000,000.\n *\n * @param {bigint} n - Number >= 1,000,000\n * @returns {string} Vietnamese words\n */\nfunction buildLargeNumberWords (n) {\n const numStr = n.toString()\n const len = numStr.length\n\n // Build segments of 3 digits from right to left\n const segments = []\n const segmentSize = 3\n\n const remainderLen = len % segmentSize\n let pos = 0\n if (remainderLen > 0) {\n segments.push(Number(numStr.slice(0, remainderLen)))\n pos = remainderLen\n }\n while (pos < len) {\n segments.push(Number(numStr.slice(pos, pos + segmentSize)))\n pos += segmentSize\n }\n\n // Convert segments to words\n const parts = []\n let scaleIndex = segments.length - 1\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i]\n if (segment !== 0) {\n const words = buildSegment(segment)\n if (words) {\n if (scaleIndex > 0) {\n parts.push(words + ' ' + SCALES[scaleIndex])\n } else {\n parts.push(words)\n }\n }\n }\n scaleIndex--\n }\n\n // Join with \"lẻ\" logic for small remainders\n const partsLen = parts.length\n if (partsLen === 0) return ZERO\n if (partsLen === 1) return parts[0]\n\n // Check if final segment needs \"lẻ\" marker (remainder <= 99 after scale word)\n const lastSegment = segments[segments.length - 1]\n if (lastSegment > 0 && lastSegment <= 99) {\n // Last segment is small (no hundreds), needs \"lẻ\" after scale word\n let result = parts[0]\n for (let i = 1; i < partsLen - 1; i++) {\n result += ' ' + parts[i]\n }\n return result + ' ' + buildLeSegment(lastSegment)\n }\n\n // Join with spaces\n let result = parts[0]\n for (let i = 1; i < partsLen; i++) {\n result += ' ' + parts[i]\n }\n return result\n}\n\n/**\n * Converts decimal digits to Vietnamese words.\n *\n * @param {string} decimalPart - Decimal digits (without the point)\n * @returns {string} Vietnamese words for decimal part\n */\nfunction decimalPartToWords (decimalPart) {\n let result = ''\n\n // Handle leading zeros\n let i = 0\n while (i < decimalPart.length && decimalPart[i] === '0') {\n if (result) result += ' '\n result += ZERO\n i++\n }\n\n // Convert remainder as a single number\n const remainder = decimalPart.slice(i)\n if (remainder) {\n if (result) result += ' '\n result += integerToWords(BigInt(remainder))\n }\n\n return result\n}\n\n/**\n * Converts a numeric value to Vietnamese words.\n *\n * This is the main public API. It accepts any valid numeric input\n * (number, string, or bigint) and handles parsing internally.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Vietnamese words\n * @throws {TypeError} If value is not a valid numeric type\n * @throws {Error} If value is not a valid number format\n *\n * @example\n * toWords(42) // 'bốn mươi hai'\n * toWords(101) // 'một trăm lẻ một'\n * toWords(1000000) // 'một triệu'\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","SCALES","ZERO","buildBelowHundred","n","ones","tens","Math","floor","tensWord","buildSegment","hundreds","remainder","result","buildLeSegment","LE","integerToWords","Number","thousands","thousandsWords","numStr","toString","len","segments","remainderLen","pos","push","parts","scaleIndex","i","segment","words","partsLen","lastSegment","buildLargeNumberWords","value","type","isFinite","Error","isSafeInteger","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","NEGATIVE","decimalPartToWords"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /*! n2words/yo v3.1.0 | MIT License | github.com/forzagreen/n2words */
2
+ var n,e;n=this,e=function(n){"use strict";function e(n){const e="-"===n[0];e&&(n=n.slice(1));const t=n.indexOf(".");if(-1===t)return{isNegative:e,integerPart:BigInt(n)};const r=n.slice(0,t)||"0",i=n.slice(t+1);return{isNegative:e,integerPart:BigInt(r),decimalPart:i}}function t(n){const[e,t]=n.toLowerCase().split("e"),r=parseInt(t,10),i=e.indexOf("."),o=-1===i?e:e.slice(0,i)+e.slice(i+1),l=(-1===i?e.length:i)+r;return l>=o.length?o+"0".repeat(l-o.length):l<=0?"0."+"0".repeat(-l)+o:o.slice(0,l)+"."+o.slice(l)}const r=["","ọ̀kan","èjì","ẹ̀ta","ẹ̀rin","àrùn","ẹ̀fà","èje","ẹ̀jọ","ẹ̀sán","ẹ̀wá"],i=["","ọ̀kànlá","èjìlá","ẹ̀talá","ẹ̀rinlá"],o=["àrùndínlógún","ẹ̀rìndínlógún","ẹ̀tadínlógún","èjìdínlógún","ọ̀kàndínlógún"],l={20:"ogún",30:"ọgbọ̀n",40:"ogójì",50:"àádọ́ta",60:"ogóta",70:"àádọ́rin",80:"ogórin",90:"àádọ́rùn",100:"ọgọ́rùn"},u={20:"lógún",30:"lọgbọ̀n",40:"lógójì",50:"láàádọ́ta",60:"lógóta",70:"láàádọ́rin",80:"lógórin",90:"láàádọ́rùn",100:"lọ́gọ́rùn"},s={20:"dínlógún",30:"dínlọgbọ̀n",40:"dínlógójì",50:"dínláàádọ́ta",60:"dínlógóta",70:"dínláàádọ́rin",80:"dínlógórin",90:"dínláàádọ́rùn",100:"dínlọ́gọ́rùn"},g="ọgọ́rùn",a="igba",f="irinwó",d="ẹgbẹ̀rún",c="mílíọ̀nù",b="ó lé";function m(n){if(0===n)return"";if(n<=10)return r[n];if(n<=14)return i[n-10];if(n<=19)return o[n-15];if(n%10==0)return l[n];const e=10*Math.floor(n/10),t=n%10;return t<=4?r[t]+" lé "+u[e]:r[10-t]+s[e+10]}function N(n){if(n<100)return m(n);const e=Math.floor(n/100),t=n%100;let i;return 2===e&&0===t?a:4===e&&0===t?f:(i=1===e?g:2===e?a:4===e?f:r[e]+" "+g,t>0&&(i+=" ó lé "+m(t)),i)}function p(n){if(0n===n)return"òdo";if(n<100n)return m(Number(n));if(n<1000n)return N(Number(n));const e=[];let t=n;if(t>=1000000n){const n=t/1000000n;t%=1000000n,e.push(1n===n?c+" kan":c+" "+p(n))}if(t>=1000n){const n=t/1000n;t%=1000n,e.push(1n===n?d+" kan":10n===n?"ẹgbàárùn":20n===n?"ọ̀kẹ́":n<100n?d+" "+m(Number(n)):d+" "+N(Number(n)))}return t>0n&&e.push(t<100n?e.length>0?b+" "+m(Number(t)):m(Number(t)):e.length>0?b+" "+N(Number(t)):N(Number(t))),e.join(", ")}n.yo=function(n){const{isNegative:i,integerPart:o,decimalPart:l}=function(n){const r=typeof n;if("bigint"===r)return n<0n?{isNegative:!0,integerPart:-n}:{isNegative:!1,integerPart:n};if("number"===r){if(!Number.isFinite(n))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(n)?n<0?{isNegative:!0,integerPart:BigInt(-n)}:{isNegative:!1,integerPart:BigInt(n)}:e(function(n){const e=n.toString();return e.includes("e")||e.includes("E")?t(e):e}(n))}if("string"===r)return e(function(n){const e=n.trim();if(0===e.length||Number.isNaN(Number(e)))throw new Error(`Invalid number format: "${n}"`);return e.includes("e")||e.includes("E")?t(e):e}(n));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${r}`)}(n);let u="";return i&&(u="àìní "),u+=p(o),l&&(u+=" àmì "+function(n){const e=[];for(const t of n){const n=parseInt(t,10);e.push(0===n?"òdo":r[n])}return e.join(" ")}(l)),u}},"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((n="undefined"!=typeof globalThis?globalThis:n||self).n2words=n.n2words||{});
3
+ //# sourceMappingURL=yo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yo.js","sources":["../../lib/utils/parse-numeric.js","../../lib/languages/yo.js"],"sourcesContent":["/**\n * Numeric value parsing utility.\n * Transforms user input (number, string, or bigint) into normalized components.\n * @module parse-numeric\n */\n\n/**\n * Parses a numeric value into its components.\n * @param {number|string|bigint} value\n * @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}}\n * @throws {TypeError} If value is not number, string, or bigint\n * @throws {Error} If value is not a valid number format\n */\nexport function parseNumericValue (value) {\n const type = typeof value\n\n // BigInt: simplest case\n if (type === 'bigint') {\n return value < 0n\n ? { isNegative: true, integerPart: -value }\n : { isNegative: false, integerPart: value }\n }\n\n // Number: fast path for safe integers\n if (type === 'number') {\n if (!Number.isFinite(value)) {\n throw new Error('Number must be finite (NaN and Infinity are not supported)')\n }\n if (Number.isSafeInteger(value)) {\n return value < 0\n ? { isNegative: true, integerPart: BigInt(-value) }\n : { isNegative: false, integerPart: BigInt(value) }\n }\n return parseNumericString(numberToString(value))\n }\n\n // String input\n if (type === 'string') {\n return parseNumericString(normalizeString(value))\n }\n\n throw new TypeError(\n `Invalid value type: expected number, string, or bigint, received ${type}`\n )\n}\n\n/**\n * Converts a number to decimal string, expanding scientific notation if needed.\n */\nfunction numberToString (value) {\n const str = value.toString()\n return (str.includes('e') || str.includes('E'))\n ? expandScientificNotation(str)\n : str\n}\n\n/**\n * Validates and normalizes a string numeric input.\n */\nfunction normalizeString (value) {\n const trimmed = value.trim()\n if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {\n throw new Error(`Invalid number format: \"${value}\"`)\n }\n return (trimmed.includes('e') || trimmed.includes('E'))\n ? expandScientificNotation(trimmed)\n : trimmed\n}\n\n/**\n * Parses a normalized numeric string into components.\n */\nfunction parseNumericString (str) {\n const isNegative = str[0] === '-'\n if (isNegative) str = str.slice(1)\n\n const dotIndex = str.indexOf('.')\n if (dotIndex === -1) {\n return { isNegative, integerPart: BigInt(str) }\n }\n\n const integerStr = str.slice(0, dotIndex) || '0'\n const decimalPart = str.slice(dotIndex + 1)\n return { isNegative, integerPart: BigInt(integerStr), decimalPart }\n}\n\n/**\n * Expands scientific notation to decimal form (e.g., \"1e21\" → \"1000...\").\n */\nfunction expandScientificNotation (str) {\n const [mantissa, expStr] = str.toLowerCase().split('e')\n const exp = parseInt(expStr, 10)\n\n const dotIndex = mantissa.indexOf('.')\n const digits = dotIndex === -1\n ? mantissa\n : mantissa.slice(0, dotIndex) + mantissa.slice(dotIndex + 1)\n const integerLength = dotIndex === -1 ? mantissa.length : dotIndex\n const newDotPosition = integerLength + exp\n\n if (newDotPosition >= digits.length) {\n return digits + '0'.repeat(newDotPosition - digits.length)\n }\n if (newDotPosition <= 0) {\n return '0.' + '0'.repeat(-newDotPosition) + digits\n }\n return digits.slice(0, newDotPosition) + '.' + digits.slice(newDotPosition)\n}\n","/**\n * Yoruba language converter - Functional Implementation\n *\n * A number-to-words converter for Yoruba (yo), a Niger-Congo language\n * spoken by ~45 million people in Nigeria, Benin, and Togo.\n *\n * Yoruba uses a complex vigesimal (base-20) system with:\n * - Additive patterns: 1-4 added to decade (lé = \"plus\")\n * - Subtractive patterns: 5-9 subtracted from next decade (dín = \"minus\")\n * - Odd decades (30,50,70,90) formed by subtracting 10 from next even decade\n * - Even decades (20,40,60,80,100) are multiples of 20\n *\n * Examples:\n * - 21 = ọ̀kan lé lógún (1 + 20)\n * - 15 = àrùndínlógún (20 - 5)\n * - 50 = àádọ́ta (60 - 10)\n * - 45 = àrùndínláàádọ́ta (50 - 5)\n */\n\nimport { parseNumericValue } from '../utils/parse-numeric.js'\n\n// ============================================================================\n// Vocabulary (module-level constants)\n// ============================================================================\n\n// Basic numbers 1-10\nconst ONES = [\n '',\n 'ọ̀kan', // 1\n 'èjì', // 2\n 'ẹ̀ta', // 3\n 'ẹ̀rin', // 4\n 'àrùn', // 5\n 'ẹ̀fà', // 6\n 'èje', // 7\n 'ẹ̀jọ', // 8\n 'ẹ̀sán', // 9\n 'ẹ̀wá' // 10\n]\n\n// Numbers 11-14 (additive: X + 10, using \"lá\")\nconst TEENS_ADD = [\n '',\n 'ọ̀kànlá', // 11 = 1 + 10\n 'èjìlá', // 12 = 2 + 10\n 'ẹ̀talá', // 13 = 3 + 10\n 'ẹ̀rinlá' // 14 = 4 + 10\n]\n\n// Numbers 15-19 (subtractive: 20 - X, using \"dín\")\nconst TEENS_SUB = [\n 'àrùndínlógún', // 15 = 20 - 5\n 'ẹ̀rìndínlógún', // 16 = 20 - 4\n 'ẹ̀tadínlógún', // 17 = 20 - 3\n 'èjìdínlógún', // 18 = 20 - 2\n 'ọ̀kàndínlógún' // 19 = 20 - 1\n]\n\n// Decades (base-20 structure)\n// Even decades are multiples of 20\n// Odd decades subtract 10 from next even decade\nconst DECADES = {\n 20: 'ogún', // 20 = 20 × 1\n 30: 'ọgbọ̀n', // 30 = 20 + 10 (special word)\n 40: 'ogójì', // 40 = 20 × 2\n 50: 'àádọ́ta', // 50 = 60 - 10\n 60: 'ogóta', // 60 = 20 × 3\n 70: 'àádọ́rin', // 70 = 80 - 10\n 80: 'ogórin', // 80 = 20 × 4\n 90: 'àádọ́rùn', // 90 = 100 - 10\n 100: 'ọgọ́rùn' // 100 = 20 × 5\n}\n\n// Prefixes for adding to decades (lé lógún, lé lọgbọ̀n, etc.)\nconst DECADE_ADD_SUFFIX = {\n 20: 'lógún',\n 30: 'lọgbọ̀n',\n 40: 'lógójì',\n 50: 'láàádọ́ta',\n 60: 'lógóta',\n 70: 'láàádọ́rin',\n 80: 'lógórin',\n 90: 'láàádọ́rùn',\n 100: 'lọ́gọ́rùn'\n}\n\n// Prefixes for subtracting from decades (dín lógójì, etc.)\nconst DECADE_SUB_SUFFIX = {\n 20: 'dínlógún',\n 30: 'dínlọgbọ̀n',\n 40: 'dínlógójì',\n 50: 'dínláàádọ́ta',\n 60: 'dínlógóta',\n 70: 'dínláàádọ́rin',\n 80: 'dínlógórin',\n 90: 'dínláàádọ́rùn',\n 100: 'dínlọ́gọ́rùn'\n}\n\n// Scale words\nconst HUNDRED = 'ọgọ́rùn'\nconst TWO_HUNDRED = 'igba' // 200 (special word, historically 200 cowries)\nconst FOUR_HUNDRED = 'irinwó' // 400 (20 × 20)\nconst THOUSAND = 'ẹgbẹ̀rún' // 1000\nconst TEN_THOUSAND = 'ẹgbàárùn' // 10,000 (special)\nconst TWENTY_THOUSAND = 'ọ̀kẹ́' // 20,000 (bag of cowries)\nconst MILLION = 'mílíọ̀nù' // million (loanword)\n\nconst ZERO = 'òdo'\nconst NEGATIVE = 'àìní'\nconst DECIMAL_SEP = 'àmì'\nconst AND = 'ó lé' // \"and\" / \"plus\" connector\n\n// ============================================================================\n// Segment Building\n// ============================================================================\n\n/**\n * Builds word for numbers 0-99\n */\nfunction buildUnder100 (n) {\n if (n === 0) return ''\n if (n <= 10) return ONES[n]\n\n // 11-14: additive from 10\n if (n <= 14) return TEENS_ADD[n - 10]\n\n // 15-19: subtractive from 20\n if (n <= 19) return TEENS_SUB[n - 15]\n\n // Exact decades\n if (n % 10 === 0) return DECADES[n]\n\n const decade = Math.floor(n / 10) * 10\n const unit = n % 10\n\n // 1-4 are added to current decade (21 = 1 + 20, not 1 + 30)\n if (unit <= 4) {\n return ONES[unit] + ' lé ' + DECADE_ADD_SUFFIX[decade]\n }\n\n // 5-9 are subtracted from next decade\n const nextDecade = decade + 10\n const subtractAmount = 10 - unit\n return ONES[subtractAmount] + DECADE_SUB_SUFFIX[nextDecade]\n}\n\n// ============================================================================\n// Conversion Functions\n// ============================================================================\n\n/**\n * Converts hundreds (100-999)\n */\nfunction convertHundreds (n) {\n if (n < 100) return buildUnder100(n)\n\n const hundreds = Math.floor(n / 100)\n const remainder = n % 100\n\n let result\n\n // Special cases for 200 and 400\n if (hundreds === 2 && remainder === 0) {\n return TWO_HUNDRED\n }\n if (hundreds === 4 && remainder === 0) {\n return FOUR_HUNDRED\n }\n\n // Build hundreds\n if (hundreds === 1) {\n result = HUNDRED\n } else if (hundreds === 2) {\n result = TWO_HUNDRED\n } else if (hundreds === 4) {\n result = FOUR_HUNDRED\n } else {\n // Other hundreds: X ọgọ́rùn\n result = ONES[hundreds] + ' ' + HUNDRED\n }\n\n if (remainder > 0) {\n result += ' ' + AND + ' ' + buildUnder100(remainder)\n }\n\n return result\n}\n\n/**\n * Converts a non-negative integer to Yoruba words.\n *\n * @param {bigint} n - Non-negative integer to convert\n * @returns {string} Yoruba words\n */\nfunction integerToWords (n) {\n if (n === 0n) return ZERO\n\n // Fast path: numbers < 100\n if (n < 100n) {\n return buildUnder100(Number(n))\n }\n\n // Numbers < 1000\n if (n < 1000n) {\n return convertHundreds(Number(n))\n }\n\n // Build from segments\n const parts = []\n let remaining = n\n\n // Millions\n if (remaining >= 1_000_000n) {\n const millions = remaining / 1_000_000n\n remaining = remaining % 1_000_000n\n if (millions === 1n) {\n parts.push(MILLION + ' kan')\n } else {\n parts.push(MILLION + ' ' + integerToWords(millions))\n }\n }\n\n // Thousands\n if (remaining >= 1000n) {\n const thousands = remaining / 1000n\n remaining = remaining % 1000n\n\n if (thousands === 1n) {\n parts.push(THOUSAND + ' kan')\n } else if (thousands === 10n) {\n parts.push(TEN_THOUSAND)\n } else if (thousands === 20n) {\n parts.push(TWENTY_THOUSAND)\n } else if (thousands < 100n) {\n parts.push(THOUSAND + ' ' + buildUnder100(Number(thousands)))\n } else {\n parts.push(THOUSAND + ' ' + convertHundreds(Number(thousands)))\n }\n }\n\n // Hundreds and below\n if (remaining > 0n) {\n if (remaining < 100n) {\n if (parts.length > 0) {\n parts.push(AND + ' ' + buildUnder100(Number(remaining)))\n } else {\n parts.push(buildUnder100(Number(remaining)))\n }\n } else {\n if (parts.length > 0) {\n parts.push(AND + ' ' + convertHundreds(Number(remaining)))\n } else {\n parts.push(convertHundreds(Number(remaining)))\n }\n }\n }\n\n return parts.join(', ')\n}\n\n/**\n * Converts decimal digits to Yoruba words.\n *\n * @param {string} decimalPart - Decimal digits\n * @returns {string} Yoruba words for decimal\n */\nfunction decimalPartToWords (decimalPart) {\n const parts = []\n\n for (const digit of decimalPart) {\n const d = parseInt(digit, 10)\n parts.push(d === 0 ? ZERO : ONES[d])\n }\n\n return parts.join(' ')\n}\n\n/**\n * Converts a numeric value to Yoruba words.\n *\n * @param {number | string | bigint} value - The numeric value to convert\n * @returns {string} The number in Yoruba words\n */\nfunction toWords (value) {\n const { isNegative, integerPart, decimalPart } = parseNumericValue(value)\n\n let result = ''\n\n if (isNegative) {\n result = NEGATIVE + ' '\n }\n\n result += integerToWords(integerPart)\n\n if (decimalPart) {\n result += ' ' + DECIMAL_SEP + ' ' + decimalPartToWords(decimalPart)\n }\n\n return result\n}\n\nexport { toWords }\n"],"names":["parseNumericString","str","isNegative","slice","dotIndex","indexOf","integerPart","BigInt","integerStr","decimalPart","expandScientificNotation","mantissa","expStr","toLowerCase","split","exp","parseInt","digits","newDotPosition","length","repeat","ONES","TEENS_ADD","TEENS_SUB","DECADES","DECADE_ADD_SUFFIX","DECADE_SUB_SUFFIX","HUNDRED","TWO_HUNDRED","FOUR_HUNDRED","THOUSAND","MILLION","AND","buildUnder100","n","decade","Math","floor","unit","convertHundreds","hundreds","remainder","result","integerToWords","Number","parts","remaining","millions","push","thousands","join","value","type","isFinite","Error","isSafeInteger","toString","includes","numberToString","trimmed","trim","isNaN","normalizeString","TypeError","parseNumericValue","NEGATIVE","digit","d","decimalPartToWords"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- /*! n2words/zh-Hans v3.0.0 | MIT License | github.com/forzagreen/n2words */
1
+ /*! n2words/zh-Hans v3.1.0 | MIT License | github.com/forzagreen/n2words */
2
2
  var n,e;n=this,e=function(n){"use strict";function e(n){const e="-"===n[0];e&&(n=n.slice(1));const t=n.indexOf(".");if(-1===t)return{isNegative:e,integerPart:BigInt(n)};const r=n.slice(0,t)||"0",i=n.slice(t+1);return{isNegative:e,integerPart:BigInt(r),decimalPart:i}}function t(n){const[e,t]=n.toLowerCase().split("e"),r=parseInt(t,10),i=e.indexOf("."),o=-1===i?e:e.slice(0,i)+e.slice(i+1),u=(-1===i?e.length:i)+r;return u>=o.length?o+"0".repeat(u-o.length):u<=0?"0."+"0".repeat(-u)+o:o.slice(0,u)+"."+o.slice(u)}const r=["零","一","二","三","四","五","六","七","八","九"],i=["零","壹","贰","叁","肆","伍","陆","柒","捌","玖"],o="零";function u(n,e,t,r,i){if(0n===n)return"";let u="",s=!1;const c=n/1000n,f=n%1000n;c>0n&&(u=e[Number(c)]+i,s=f>0n&&f<100n);const l=f/100n,a=f%100n;l>0n?(s&&(u+=o),u+=e[Number(l)]+r,s=a>0n&&a<10n):c>0n&&a>0n&&(s=!0);const g=a/10n,d=a%10n;return g>0n?(s&&(u+=o),u+=e[Number(g)]+t,s=!1):(l>0n||c>0n)&&d>0n&&(s=!0),d>0n&&(s&&(u+=o),u+=e[Number(d)]),u}function s(n,e,t,r,i){if(0n===n)return"";if(n>=10000n){const s=n/10000n,c=n%10000n;let f=u(s,e,t,r,i)+"万";return c>0n&&((s%10n==0n||c<1000n)&&(f+=o),f+=u(c,e,t,r,i)),f}return u(n,e,t,r,i)}n.zhHans=function(n,u){u=function(n){if(void 0===n)return{};if(function(n){if(null===n||"object"!=typeof n)return!1;const e=Object.getPrototypeOf(n);return null===e||e===Object.prototype}(n))return n;throw new TypeError("Invalid options: expected plain object or undefined, got "+typeof n)}(u);const{isNegative:c,integerPart:f,decimalPart:l}=function(n){const r=typeof n;if("bigint"===r)return n<0n?{isNegative:!0,integerPart:-n}:{isNegative:!1,integerPart:n};if("number"===r){if(!Number.isFinite(n))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(n)?n<0?{isNegative:!0,integerPart:BigInt(-n)}:{isNegative:!1,integerPart:BigInt(n)}:e(function(n){const e=n.toString();return e.includes("e")||e.includes("E")?t(e):e}(n))}if("string"===r)return e(function(n){const e=n.trim();if(0===e.length||Number.isNaN(Number(e)))throw new Error(`Invalid number format: "${n}"`);return e.includes("e")||e.includes("E")?t(e):e}(n));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${r}`)}(n),a=!1!==u.formal;let g=c?"负":"";return g+=function(n,e=!0){if(0n===n)return o;const t=e?i:r,u=e?"拾":"十",c=e?"佰":"百",f=e?"仟":"千";if(n>=100000000n){const e=n%100000000n;let r=s(n/100000000n,t,u,c,f)+"亿";return e>0n&&(e<10000000n&&(r+=o),r+=s(e,t,u,c,f)),r}return s(n,t,u,c,f)}(f,a),l&&(g+="点"+function(n,e){let t="";for(let r=0;r<n.length;r++)t+=e[Number(n[r])];return t}(l,a?i:r)),g}},"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((n="undefined"!=typeof globalThis?globalThis:n||self).n2words=n.n2words||{});
3
3
  //# sourceMappingURL=zh-Hans.js.map
@@ -1,3 +1,3 @@
1
- /*! n2words/zh-Hant v3.0.0 | MIT License | github.com/forzagreen/n2words */
1
+ /*! n2words/zh-Hant v3.1.0 | MIT License | github.com/forzagreen/n2words */
2
2
  var n,e;n=this,e=function(n){"use strict";function e(n){const e="-"===n[0];e&&(n=n.slice(1));const t=n.indexOf(".");if(-1===t)return{isNegative:e,integerPart:BigInt(n)};const r=n.slice(0,t)||"0",i=n.slice(t+1);return{isNegative:e,integerPart:BigInt(r),decimalPart:i}}function t(n){const[e,t]=n.toLowerCase().split("e"),r=parseInt(t,10),i=e.indexOf("."),o=-1===i?e:e.slice(0,i)+e.slice(i+1),s=(-1===i?e.length:i)+r;return s>=o.length?o+"0".repeat(s-o.length):s<=0?"0."+"0".repeat(-s)+o:o.slice(0,s)+"."+o.slice(s)}const r=["零","一","二","三","四","五","六","七","八","九"],i=["零","壹","貳","參","肆","伍","陸","柒","捌","玖"],o="零";n.zhHant=function(n,s){s=function(n){if(void 0===n)return{};if(function(n){if(null===n||"object"!=typeof n)return!1;const e=Object.getPrototypeOf(n);return null===e||e===Object.prototype}(n))return n;throw new TypeError("Invalid options: expected plain object or undefined, got "+typeof n)}(s);const{isNegative:u,integerPart:c,decimalPart:f}=function(n){const r=typeof n;if("bigint"===r)return n<0n?{isNegative:!0,integerPart:-n}:{isNegative:!1,integerPart:n};if("number"===r){if(!Number.isFinite(n))throw new Error("Number must be finite (NaN and Infinity are not supported)");return Number.isSafeInteger(n)?n<0?{isNegative:!0,integerPart:BigInt(-n)}:{isNegative:!1,integerPart:BigInt(n)}:e(function(n){const e=n.toString();return e.includes("e")||e.includes("E")?t(e):e}(n))}if("string"===r)return e(function(n){const e=n.trim();if(0===e.length||Number.isNaN(Number(e)))throw new Error(`Invalid number format: "${n}"`);return e.includes("e")||e.includes("E")?t(e):e}(n));throw new TypeError(`Invalid value type: expected number, string, or bigint, received ${r}`)}(n),p=!1!==s.formal;let a="";return u&&(a="負"),a+=function(n,e=!0){if(0n===n)return o;const t=e?i:r,s=e?"拾":"十",u=e?"佰":"百",c=e?"仟":"千";function f(n){if(0n===n)return"";const e=[];let r=!1;const i=n/1000n,f=n%1000n;i>0n&&(e.push(t[Number(i)]+c),r=f>0n&&f<100n);const p=f/100n,a=f%100n;p>0n?(r&&(e.push(o),r=!1),e.push(t[Number(p)]+u),r=a>0n&&a<10n):i>0n&&a>0n&&(r=!0);const l=a/10n,g=a%10n;return l>0n?(r&&(e.push(o),r=!1),e.push(t[Number(l)]+s)):(p>0n||i>0n)&&g>0n&&(r=!0),g>0n&&(r&&e.push(o),e.push(t[Number(g)])),e.join("")}function p(n){if(0n===n)return"";const e=[];if(n>=10000n){const t=n/10000n,r=n%10000n;e.push(f(t)+"萬"),r>0n&&((t%10n==0n||r<1000n)&&e.push(o),e.push(f(r)))}else e.push(f(n));return e.join("")}const a=[];if(n>=100000000n){const e=n%100000000n,t=p(n/100000000n);a.push(t+"億"),e>0n&&(e<10000000n&&a.push(o),a.push(p(e)))}else a.push(p(n));return a.join("")}(c,p),f&&(a+="點"+function(n,e=!0){const t=e?i:r,o=[];for(const e of n)o.push(t[Number(e)]);return o}(f,p).join("")),a}},"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((n="undefined"!=typeof globalThis?globalThis:n||self).n2words=n.n2words||{});
3
3
  //# sourceMappingURL=zh-Hant.js.map