diff --git a/.gitignore b/.gitignore
index 5a9069c..bea3ffd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,4 @@ lcov
.tern-port
.DS_Store
package-lock.json
+.vs*
\ No newline at end of file
diff --git a/README.md b/README.md
index 66b62ff..81128ae 100644
--- a/README.md
+++ b/README.md
@@ -65,6 +65,8 @@ Currently supported languages are:
| Ukrainian | `uk` |
| Indonesian | `id` |
| Russian | `ru` |
+| Chinese (Traditional) | `zhTW` |
+| Chinese (Simplified) | `zhCN` |
## Contributing
@@ -83,13 +85,15 @@ The following parameters have been used for the currently available languages:
| Parameter | Type | Description | Examples |
|-----------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `useLongScale` | boolean | Indicates if it uses [long or short scale](http://en.wikipedia.org/wiki/Long_and_short_scales). | This differs the meaning of the words `billion`, `trillion` and so on. |
-| `baseSeparator` | string | Separates the base cardinal numbers. | 29 -> twenty`-`eight. Spanish uses the connector " y " |
+| `baseSeparator` | string | Separates the base cardinal numbers. | 29 -> twenty`-`eight. Spanish uses the connector " y " |
| `unitSeparator` | string | Separates the units from the last base cardinal numbers. | 1234 -> one thousand two hundred **and** thirty-four |
+| `joinSeparator` | string | Separates all words, default is `" "` | 100 -> one hundred. Chinese has no spaces, so it uses the connector `""` |
| `allSeparator` | string | Separates all cardinals, not only the last one. | 1125 -> ألف **و**مائة **و**خمسة **و**عشرون |
| `base` | Object | Base cardinals numbers. Numbers that have unique names and are used to build others. | |
| `alternativeBase` | Object | Alternative versions of base cardinals numbers for usage with specific units. These bases will be treated as an extension for the default `base`. | ``` "alternativeBase": { "feminine": {"1":"одна","2":"дві"} } ``` |
-| `units` | Array | A list of number units (string or Object). Gives support to singular, dual an plural units. Check the Object parameters below. | |
+| `units` | Array | A list of number units (string or Object). Gives support to singular, dual an plural units. Check the Object parameters below. | |
| `unitExceptions` | Object | Sometimes grammar exceptions affect the base cardinal joined to the unit. You can set specific exceptions to any base cardinal number. | Converting 1232000 in Spanish: Without Exception (Wrong): -> **uno** millón doscientos treinta y dos mil With Exception: -> **un** millón doscientos treinta y dos mil |
+| `prependZero` | boolean | If `true`, all contiguous strings of 0s (except leading and trailing) are replaced by one "zero" word before the following number word. | 102003 -> 十萬零二千零三 |
### Units parameters
diff --git a/bower.json b/bower.json
index ec275f2..7f453be 100644
--- a/bower.json
+++ b/bower.json
@@ -3,7 +3,7 @@
"description": "Convert numbers to words - their written form",
"homepage": "https://yamadapc.github.io/js-written-number",
"main": "./dist/written-number.js",
- "version": "0.11.0",
+ "version": "0.12.0",
"keywords": [
"numbers",
"words",
diff --git a/lib/i18n/zh-CN.json b/lib/i18n/zh-CN.json
new file mode 100644
index 0000000..c91fd63
--- /dev/null
+++ b/lib/i18n/zh-CN.json
@@ -0,0 +1,39 @@
+{
+ "useLongScale": null,
+ "baseSeparator": "",
+ "unitSeparator": "",
+ "joinSeparator": "",
+ "allSeparator": "",
+ "prependZero": true,
+ "base": {
+ "0": "〇",
+ "1": "一",
+ "2": "二",
+ "3": "三",
+ "4": "四",
+ "5": "五",
+ "6": "六",
+ "7": "七",
+ "8": "八",
+ "9": "九"
+ },
+ "units": {
+ "1": "十",
+ "2": "百",
+ "3": "千",
+ "4": "万",
+ "8": "亿"
+ },
+ "unitExceptions": {
+ "10": "十",
+ "11": "十一",
+ "12": "十二",
+ "13": "十三",
+ "14": "十四",
+ "15": "十五",
+ "16": "十六",
+ "17": "十七",
+ "18": "十八",
+ "19": "十九"
+ }
+}
diff --git a/lib/i18n/zh-TW.json b/lib/i18n/zh-TW.json
new file mode 100644
index 0000000..2cf99e0
--- /dev/null
+++ b/lib/i18n/zh-TW.json
@@ -0,0 +1,40 @@
+{
+ "useLongScale": null,
+ "baseSeparator": "",
+ "unitSeparator": "",
+ "joinSeparator": "",
+ "allSeparator": "",
+ "prependZero": true,
+ "base": {
+ "0": "零",
+ "1": "一",
+ "2": "二",
+ "3": "三",
+ "4": "四",
+ "5": "五",
+ "6": "六",
+ "7": "七",
+ "8": "八",
+ "9": "九"
+ },
+ "units": {
+ "1": "十",
+ "2": "百",
+ "3": "千",
+ "4": "萬",
+ "8": "億",
+ "12": "兆"
+ },
+ "unitExceptions": {
+ "10": "十",
+ "11": "十一",
+ "12": "十二",
+ "13": "十三",
+ "14": "十四",
+ "15": "十五",
+ "16": "十六",
+ "17": "十七",
+ "18": "十八",
+ "19": "十九"
+ }
+}
diff --git a/lib/index.js b/lib/index.js
index 4114399..925afcb 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -2,7 +2,7 @@
exports = module.exports = writtenNumber;
var util = require("./util");
-var languages = ["en", "es", "ar", "az", "pt", "fr", "eo", "it", "vi", "tr", "uk", "ru", "id"];
+var languages = ["en", "es", "ar", "az", "pt", "fr", "eo", "it", "vi", "tr", "uk", "ru", "id", "zh"];
var i18n = {
en: require("./i18n/en.json"),
es: require("./i18n/es.json"),
@@ -19,7 +19,9 @@ var i18n = {
enIndian: require("./i18n/en-indian.json"),
uk: require("./i18n/uk.json"),
ru: require("./i18n/ru.json"),
- id: require("./i18n/id.json")
+ id: require("./i18n/id.json"),
+ zhTW: require("./i18n/zh-TW.json"),
+ zhCN: require("./i18n/zh-CN.json")
};
exports.i18n = i18n;
@@ -36,7 +38,8 @@ for (i = 1; i <= 15; i++) {
writtenNumber.defaults = {
noAnd: false,
alternativeBase: null,
- lang: "en"
+ lang: "en",
+ smallMeans10: false
};
/**
@@ -68,7 +71,11 @@ function writtenNumber(n, options) {
language = i18n[writtenNumber.defaults.lang];
}
-
+
+ if (!language.hasOwnProperty('joinSeparator')) {
+ language.joinSeparator = " ";
+ }
+
var scale = language.useLongScale ? longScale : shortScale;
var units = language.units;
var unit;
@@ -86,19 +93,28 @@ function writtenNumber(n, options) {
}
var baseCardinals = language.base;
- var alternativeBaseCardinals = options.alternativeBase
+ var alternativeBaseCardinals = options.alternativeBase
? language.alternativeBase[options.alternativeBase]
: {};
+ var smallFactor = options.smallMeans10 ? 10 : 100;
if (language.unitExceptions[n]) return language.unitExceptions[n];
if (alternativeBaseCardinals[n]) return alternativeBaseCardinals[n];
if (baseCardinals[n]) return baseCardinals[n];
- if (n < 100)
+ if (n < smallFactor)
return handleSmallerThan100(n, language, unit, baseCardinals, alternativeBaseCardinals, options);
- var m = n % 100;
+ setSmallMeans10(options, baseCardinals, alternativeBaseCardinals);
+ smallFactor = options.smallMeans10 ? 10 : 100;
+
+ var m = n % smallFactor;
var ret = [];
+ if (language.prependZero) {
+ var _n = n, _n10 = Math.pow(10, Math.floor(Math.log10(_n)));
+ var zero = writtenNumber(0, options);
+ }
+
if (m) {
if (
options.noAnd &&
@@ -108,6 +124,9 @@ function writtenNumber(n, options) {
} else {
ret.push(language.unitSeparator + writtenNumber(m, options));
}
+ if (language.prependZero && Math.floor(n / 10) % smallFactor == 0) {
+ ret[0] = zero + ret[0];
+ }
}
var firstSignificant;
@@ -125,6 +144,19 @@ function writtenNumber(n, options) {
if (!r) continue;
firstSignificant = scale[i];
+ var prependedZero = "";
+ if (
+ language.prependZero
+ && i < len - 1
+ && scale[i] < _n10
+ ) {
+ var nextSpaceFloor = Math.floor(_n / scale[i] / (divideBy / 10));
+ var nextUnitFloor = Math.floor(_n / scale[i + 1]);
+ if (
+ (nextSpaceFloor && nextSpaceFloor % 10 == 0)
+ || (nextUnitFloor && nextUnitFloor % 10 == 0)
+ ) prependedZero = zero;
+ }
if (unit.useBaseInstead) {
var shouldUseBaseException =
@@ -153,10 +185,10 @@ function writtenNumber(n, options) {
str = unit.plural && (!unit.avoidInNumberPlural || !m)
? unit.plural
: unit.singular;
-
+
// Languages with dual
str = (r === 2 && unit.dual) ? unit.dual : str;
-
+
// "restrictedPlural" : use plural only for 3 to 10
str = (r > 10 && unit.restrictedPlural) ? unit.singular : str;
}
@@ -185,7 +217,7 @@ function writtenNumber(n, options) {
)
);
n -= r * scale[i];
- ret.push(number + " " + str);
+ ret.push(prependedZero + number + language.joinSeparator + str);
}
var firstSignificantN = firstSignificant * Math.floor(n / firstSignificant);
@@ -201,24 +233,40 @@ function writtenNumber(n, options) {
ret.slice(1)
);
}
-
+
// Languages that have separators for all cardinals.
if (language.allSeparator) {
- for (var j = 0; j < ret.length-1; j++) {
- ret[j] = language.allSeparator + ret[j];
+ for (var j = 0; j < ret.length - 1; j++) {
+ ret[j] = language.allSeparator + ret[j];
}
}
- var result = ret.reverse().join(" ");
+ var result = ret.reverse().join(language.joinSeparator);
return result;
}
function handleSmallerThan100(n, language, unit, baseCardinals, alternativeBaseCardinals, options) {
+ var smallMeans10 = options.smallMeans10;
+ setSmallMeans10(options, baseCardinals, alternativeBaseCardinals);
var dec = Math.floor(n / 10) * 10;
unit = n - dec;
+ var decCardinal = alternativeBaseCardinals[dec] || baseCardinals[dec];
+ if (smallMeans10) {
+ decCardinal = '';
+ } else if (!decCardinal) {
+ decCardinal = writtenNumber(dec, options);
+ }
if (unit) {
return (
- alternativeBaseCardinals[dec] || baseCardinals[dec] + language.baseSeparator + writtenNumber(unit, options)
+ decCardinal + language.baseSeparator + writtenNumber(unit, options)
);
}
- return alternativeBaseCardinals[dec] || baseCardinals[dec];
+ return decCardinal;
}
+
+function setSmallMeans10(options, baseCardinals, alternativeBaseCardinals) {
+ if (options.smallMeans10) return;
+ var f = function (i) {return parseInt(i);};
+ var keys = Object.keys(baseCardinals).map(f);
+ keys = keys.concat(Object.keys(alternativeBaseCardinals).map(f));
+ options.smallMeans10 = Math.max.apply(null, keys) < 10;
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 499f12b..dcf455a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "written-number",
- "version": "0.11.0",
+ "version": "0.12.0",
"description": "Convert numbers to words - their written form.",
"main": "lib/index.js",
"scripts": {
diff --git a/test/index.test.js b/test/index.test.js
index 3175a1e..8bdcec5 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -982,4 +982,264 @@ describe("written-number", function () {
);
});
});
+
+ describe("writtenNumber(n, { lang: 'zhTW', ... })", function () {
+ before(function () {
+ writtenNumber.defaults.lang = "zhTW";
+ });
+
+ it("gets exposed", function () {
+ should.exist(writtenNumber);
+ writtenNumber.should.be.instanceof(Function);
+ });
+
+ it("negative numbers return \"\"", function () {
+ writtenNumber(-3).should.equal("");
+ writtenNumber(-5).should.equal("");
+ });
+
+ it("doesn't blow up weirdly with invalid input", function () {
+ writtenNumber("asdfasdfasdf").should.equal("");
+ writtenNumber("0.as").should.equal("");
+ writtenNumber("0.123").should.equal("零");
+ writtenNumber("0.8").should.equal("一");
+ writtenNumber("2.8").should.equal("三");
+ writtenNumber("asdf.8").should.equal("");
+ writtenNumber("120391938123..").should.equal("");
+ writtenNumber("1000000000.123").should.equal("十億");
+ writtenNumber("1/3").should.equal("");
+ writtenNumber(1 / 3).should.equal("零");
+ writtenNumber("1/2").should.equal("");
+ writtenNumber("1.123/2").should.equal("");
+ });
+
+ it("correctly converts numbers < 10", function () {
+ writtenNumber(3).should.equal("三");
+ writtenNumber(8).should.equal("八");
+ });
+
+ it("correctly converts numbers < 100", function () {
+ writtenNumber(10).should.equal("十");
+ writtenNumber(11).should.equal("十一");
+ writtenNumber(13).should.equal("十三");
+ writtenNumber(20).should.equal("二十");
+ writtenNumber(23).should.equal("二十三");
+ });
+
+ it("correctly converts numbers < 1000", function () {
+ writtenNumber(100).should.equal("一百");
+ writtenNumber(200).should.equal("二百");
+ writtenNumber(321).should.equal("三百二十一");
+ writtenNumber(417).should.equal("四百一十七");
+ writtenNumber(501).should.equal("五百零一");
+ });
+
+ it("correctly converts numbers < 10000", function () {
+ writtenNumber(1000).should.equal("一千");
+ writtenNumber(2003).should.equal("二千零三");
+ writtenNumber(3210).should.equal("三千二百一十");
+ writtenNumber(4011).should.equal("四千零一十一");
+ writtenNumber(5432).should.equal("五千四百三十二");
+ });
+
+ it("correctly converts numbers < 100 000 000", function () {
+ writtenNumber(10000).should.equal("一萬");
+ writtenNumber(20003).should.equal("二萬零三");
+ writtenNumber(32100).should.equal("三萬二千一百");
+ writtenNumber(43210).should.equal("四萬三千二百一十");
+ writtenNumber(54302).should.equal("五萬四千三百零二");
+ writtenNumber(60606).should.equal("六萬零六百零六");
+ writtenNumber(70600).should.equal("七萬零六百");
+ writtenNumber(100000).should.equal("十萬");
+ writtenNumber(110000).should.equal("十一萬");
+ writtenNumber(210003).should.equal("二十一萬零三");
+ writtenNumber(1110000).should.equal("一百一十一萬");
+ writtenNumber(1010101).should.equal("一百零一萬零一百零一");
+ writtenNumber(11011011).should.equal(
+ "一千一百零一萬一千零一十一"
+ );
+ writtenNumber(20304050).should.equal(
+ "二千零三十萬零四千零五十"
+ );
+ });
+
+ it("correctly converts numbers < 1 000 000 000 000", function () {
+ writtenNumber(100000000).should.equal("一億");
+ writtenNumber(200000003).should.equal("二億零三");
+ writtenNumber(321000000).should.equal("三億二千一百萬");
+ writtenNumber(432100000).should.equal("四億三千二百一十萬");
+ writtenNumber(543020000).should.equal("五億四千三百零二萬");
+ writtenNumber(606060606).should.equal(
+ "六億零六百零六萬零六百零六"
+ );
+ writtenNumber(110110110).should.equal(
+ "一億一千零一十一萬零一百一十"
+ );
+ writtenNumber(111111111).should.equal(
+ "一億一千一百一十一萬一千一百一十一"
+ );
+ writtenNumber(1000000000).should.equal("十億");
+ writtenNumber(1100000000).should.equal("十一億");
+ writtenNumber(11100000000).should.equal("一百一十一億");
+ writtenNumber(101100000000).should.equal("一千零一十一億");
+ writtenNumber(101101101011).should.equal(
+ "一千零一十一億零一百一十萬零一千零一十一"
+ );
+ });
+
+ it("correctly converts numbers >= 1 000 000 000 000", function () {
+ writtenNumber(1000000000000).should.equal("一兆");
+ writtenNumber(2000000000003).should.equal("二兆零三");
+ writtenNumber(3210000000000).should.equal("三兆二千一百億");
+ writtenNumber(4321000000000).should.equal("四兆三千二百一十億");
+ writtenNumber(5430200000000).should.equal("五兆四千三百零二億");
+ writtenNumber(6060606060606).should.equal(
+ "六兆零六百零六億零六百零六萬零六百零六"
+ );
+ writtenNumber(1011011011011).should.equal(
+ "一兆零一百一十億零一千一百零一萬一千零一十一"
+ );
+ writtenNumber(1111111111111).should.equal(
+ "一兆一千一百一十一億一千一百一十一萬一千一百一十一"
+ );
+ writtenNumber(10000000000000).should.equal("十兆");
+ writtenNumber(11000000000000).should.equal("十一兆");
+ writtenNumber(111000000000000).should.equal("一百一十一兆");
+ writtenNumber(1011000000000000).should.equal("一千零一十一兆");
+ writtenNumber(1011011010110011).should.equal(
+ "一千零一十一兆零一百一十億零一千零一十一萬零一十一"
+ );
+ });
+ });
+
+ describe("writtenNumber(n, { lang: 'zhCN', ... })", function () {
+ before(function () {
+ writtenNumber.defaults.lang = "zhCN";
+ });
+
+ it("gets exposed", function () {
+ should.exist(writtenNumber);
+ writtenNumber.should.be.instanceof(Function);
+ });
+
+ it("negative numbers return \"\"", function () {
+ writtenNumber(-3).should.equal("");
+ writtenNumber(-5).should.equal("");
+ });
+
+ it("doesn't blow up weirdly with invalid input", function () {
+ writtenNumber("asdfasdfasdf").should.equal("");
+ writtenNumber("0.as").should.equal("");
+ writtenNumber("0.123").should.equal("〇");
+ writtenNumber("0.8").should.equal("一");
+ writtenNumber("2.8").should.equal("三");
+ writtenNumber("asdf.8").should.equal("");
+ writtenNumber("120391938123..").should.equal("");
+ writtenNumber("1000000000.123").should.equal("十亿");
+ writtenNumber("1/3").should.equal("");
+ writtenNumber(1 / 3).should.equal("〇");
+ writtenNumber("1/2").should.equal("");
+ writtenNumber("1.123/2").should.equal("");
+ });
+
+ it("correctly converts numbers < 10", function () {
+ writtenNumber(3).should.equal("三");
+ writtenNumber(8).should.equal("八");
+ });
+
+ it("correctly converts numbers < 100", function () {
+ writtenNumber(10).should.equal("十");
+ writtenNumber(11).should.equal("十一");
+ writtenNumber(13).should.equal("十三");
+ writtenNumber(20).should.equal("二十");
+ writtenNumber(23).should.equal("二十三");
+ });
+
+ it("correctly converts numbers < 1000", function () {
+ writtenNumber(100).should.equal("一百");
+ writtenNumber(200).should.equal("二百");
+ writtenNumber(321).should.equal("三百二十一");
+ writtenNumber(417).should.equal("四百一十七");
+ writtenNumber(501).should.equal("五百〇一");
+ });
+
+ it("correctly converts numbers < 10000", function () {
+ writtenNumber(1000).should.equal("一千");
+ writtenNumber(2003).should.equal("二千〇三");
+ writtenNumber(3210).should.equal("三千二百一十");
+ writtenNumber(4011).should.equal("四千〇一十一");
+ writtenNumber(5432).should.equal("五千四百三十二");
+ });
+
+ it("correctly converts numbers < 100 000 000", function () {
+ writtenNumber(10000).should.equal("一万");
+ writtenNumber(20003).should.equal("二万〇三");
+ writtenNumber(32100).should.equal("三万二千一百");
+ writtenNumber(43210).should.equal("四万三千二百一十");
+ writtenNumber(54302).should.equal("五万四千三百〇二");
+ writtenNumber(60606).should.equal("六万〇六百〇六");
+ writtenNumber(70600).should.equal("七万〇六百");
+ writtenNumber(100000).should.equal("十万");
+ writtenNumber(110000).should.equal("十一万");
+ writtenNumber(210003).should.equal("二十一万〇三");
+ writtenNumber(1110000).should.equal("一百一十一万");
+ writtenNumber(1010101).should.equal("一百〇一万〇一百〇一");
+ writtenNumber(11011011).should.equal(
+ "一千一百〇一万一千〇一十一"
+ );
+ writtenNumber(20304050).should.equal(
+ "二千〇三十万〇四千〇五十"
+ );
+ });
+
+ it("correctly converts numbers < 1 000 000 000 000", function () {
+ writtenNumber(100000000).should.equal("一亿");
+ writtenNumber(200000003).should.equal("二亿〇三");
+ writtenNumber(321000000).should.equal("三亿二千一百万");
+ writtenNumber(432100000).should.equal("四亿三千二百一十万");
+ writtenNumber(543020000).should.equal("五亿四千三百〇二万");
+ writtenNumber(606060606).should.equal(
+ "六亿〇六百〇六万〇六百〇六"
+ );
+ writtenNumber(110110110).should.equal(
+ "一亿一千〇一十一万〇一百一十"
+ );
+ writtenNumber(111111111).should.equal(
+ "一亿一千一百一十一万一千一百一十一"
+ );
+ writtenNumber(1000000000).should.equal("十亿");
+ writtenNumber(1100000000).should.equal("十一亿");
+ writtenNumber(11100000000).should.equal("一百一十一亿");
+ writtenNumber(101100000000).should.equal("一千〇一十一亿");
+ writtenNumber(101101101011).should.equal(
+ "一千〇一十一亿〇一百一十万〇一千〇一十一"
+ );
+ });
+
+ it("correctly converts numbers >= 1 000 000 000 000", function () {
+ writtenNumber(1000000000000).should.equal("一万亿");
+ writtenNumber(2000000000003).should.equal("二万亿〇三");
+ writtenNumber(3210000000000).should.equal("三万二千一百亿");
+ writtenNumber(4321000000000).should.equal("四万三千二百一十亿");
+ writtenNumber(5430200000000).should.equal("五万四千三百〇二亿");
+ writtenNumber(6060606060606).should.equal(
+ "六万〇六百〇六亿〇六百〇六万〇六百〇六"
+ );
+ writtenNumber(1011011011011).should.equal(
+ "一万〇一百一十亿〇一千一百〇一万一千〇一十一"
+ );
+ writtenNumber(1111111111111).should.equal(
+ "一万一千一百一十一亿一千一百一十一万一千一百一十一"
+ );
+ writtenNumber(10000000000000).should.equal("十万亿");
+ writtenNumber(11000000000000).should.equal("十一万亿");
+ writtenNumber(21000000000000).should.equal("二十一万亿");
+ // More digits than this is basically encroaching on
+ // the limits of the simplified number system (which has
+ // no word for trillion). Testing more digits than this
+ // fails, so I'll leave it there. Note that the more
+ // digits do work on the traditional number system,
+ // which does have a word for trillion.
+ });
+ });
});
\ No newline at end of file