nhb-toolbox 4.13.1 → 4.13.3
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 +5 -0
- package/dist/cjs/pluralize/Pluralizer.js +47 -22
- package/dist/cjs/pluralize/rules.js +100 -75
- package/dist/cjs/string/convert.js +2 -0
- package/dist/dts/pluralize/Pluralizer.d.ts +16 -9
- package/dist/dts/pluralize/Pluralizer.d.ts.map +1 -1
- package/dist/dts/pluralize/rules.d.ts +5 -0
- package/dist/dts/pluralize/rules.d.ts.map +1 -1
- package/dist/dts/string/convert.d.ts +2 -0
- package/dist/dts/string/convert.d.ts.map +1 -1
- package/dist/esm/pluralize/Pluralizer.js +48 -23
- package/dist/esm/pluralize/rules.js +99 -74
- package/dist/esm/string/convert.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,11 @@ All notable changes to the package will be documented here.
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [4.13.3] - 2025-07-22
|
|
10
|
+
|
|
11
|
+
- **Updated** pluralization/uncountable rules, case restoration method and fixed other bugs in `pluralizer`.
|
|
12
|
+
- **Updated** docs for `pluralizer`, `Pluralizer` and `formatUnitWithPlural`.
|
|
13
|
+
|
|
9
14
|
## [4.13.1] - 2025-07-22
|
|
10
15
|
|
|
11
16
|
- **Updated** docs in [README](README.md) for `pluralizer`.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pluralizer = exports.Pluralizer = void 0;
|
|
4
|
+
const primitives_1 = require("../guards/primitives");
|
|
4
5
|
const utilities_1 = require("../number/utilities");
|
|
5
6
|
const rules_1 = require("./rules");
|
|
6
7
|
/**
|
|
@@ -12,6 +13,11 @@ const rules_1 = require("./rules");
|
|
|
12
13
|
* - Automatically loads common irregular forms and uncountable nouns.
|
|
13
14
|
* - Supports options for count-based pluralization, allowing for inclusive formatting.
|
|
14
15
|
* - This class is useful for applications that need to handle natural language processing, such as chatbots, content management systems, or any text processing tasks that require accurate pluralization.
|
|
16
|
+
*
|
|
17
|
+
* @remarks For simpler pluralization (plural with only 's'), please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/formatUnitWithPlural formatUnitWithPlural} instead.
|
|
18
|
+
*
|
|
19
|
+
* @remarks For ready to use instance, please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/pluralizer pluralizer} instead.
|
|
20
|
+
*
|
|
15
21
|
* @example
|
|
16
22
|
* const pluralizer = new Pluralizer();
|
|
17
23
|
* pluralizer.pluralize('child'); // "children"
|
|
@@ -26,7 +32,7 @@ class Pluralizer {
|
|
|
26
32
|
#irregularPlurals = {};
|
|
27
33
|
/**
|
|
28
34
|
* Initializes the Pluralizer with default rules and exceptions.
|
|
29
|
-
* Automatically loads irregular
|
|
35
|
+
* Automatically loads irregular, pluralization and singular rules along with pre-defined uncountable nouns.
|
|
30
36
|
*/
|
|
31
37
|
constructor() {
|
|
32
38
|
this.#loadRules();
|
|
@@ -40,21 +46,38 @@ class Pluralizer {
|
|
|
40
46
|
rules_1.pluralRules.forEach(([rule, replacement]) => {
|
|
41
47
|
this.addPluralRule(rule, replacement);
|
|
42
48
|
});
|
|
49
|
+
// ! Load singular rules
|
|
50
|
+
rules_1.singularRules.forEach(([rule, replacement]) => {
|
|
51
|
+
this.addSingularRule(rule, replacement);
|
|
52
|
+
});
|
|
43
53
|
// ! Load uncountables
|
|
44
54
|
rules_1.uncountables.forEach((word) => {
|
|
45
55
|
this.addUncountable(word);
|
|
46
56
|
});
|
|
47
57
|
}
|
|
48
|
-
#restoreCase(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
#restoreCase(original, transformed) {
|
|
59
|
+
let result = '';
|
|
60
|
+
for (let i = 0; i < transformed.length; i++) {
|
|
61
|
+
const origChar = original[i];
|
|
62
|
+
if (origChar) {
|
|
63
|
+
if (origChar.toUpperCase() === origChar &&
|
|
64
|
+
origChar.toLowerCase() !== origChar) {
|
|
65
|
+
result += transformed[i].toUpperCase();
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
result += transformed[i].toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
result += transformed[i].toLowerCase();
|
|
73
|
+
}
|
|
53
74
|
}
|
|
54
|
-
return
|
|
75
|
+
return result;
|
|
55
76
|
}
|
|
56
77
|
#sanitizeWord(word, rules) {
|
|
57
|
-
if (!
|
|
78
|
+
if (!(0, primitives_1.isNonEmptyString)(word))
|
|
79
|
+
return '';
|
|
80
|
+
if (this.#isUncountable(word)) {
|
|
58
81
|
return word;
|
|
59
82
|
}
|
|
60
83
|
for (let i = rules.length - 1; i >= 0; i--) {
|
|
@@ -70,7 +93,7 @@ class Pluralizer {
|
|
|
70
93
|
* Supports both string and RegExp entries.
|
|
71
94
|
*/
|
|
72
95
|
#isUncountable(word) {
|
|
73
|
-
const lower = word
|
|
96
|
+
const lower = word?.toLowerCase();
|
|
74
97
|
for (const entry of this.#uncountables) {
|
|
75
98
|
if (typeof entry === 'string') {
|
|
76
99
|
if (entry === lower)
|
|
@@ -94,7 +117,7 @@ class Pluralizer {
|
|
|
94
117
|
this.#pluralRules.push([rule, replacement]);
|
|
95
118
|
}
|
|
96
119
|
/**
|
|
97
|
-
* Add a new singularization rule.
|
|
120
|
+
* * Add a new singularization rule.
|
|
98
121
|
* @param rule Pattern to match plural words.
|
|
99
122
|
* @param replacement Replacement pattern for singular form.
|
|
100
123
|
* @example
|
|
@@ -104,23 +127,23 @@ class Pluralizer {
|
|
|
104
127
|
this.#singularRules.push([rule, replacement]);
|
|
105
128
|
}
|
|
106
129
|
addUncountable(word) {
|
|
107
|
-
this.#uncountables.add(typeof word === 'string' ? word
|
|
130
|
+
this.#uncountables.add(typeof word === 'string' ? word?.toLowerCase() : word);
|
|
108
131
|
}
|
|
109
132
|
/**
|
|
110
|
-
* Add a word or pattern that should never change between singular and plural.
|
|
133
|
+
* * Add a word or pattern that should never change between singular and plural.
|
|
111
134
|
* @param word A word or regex pattern.
|
|
112
135
|
* @example
|
|
113
136
|
* pluralizer.addUncountable('fish');
|
|
114
137
|
* pluralizer.addUncountable(/pok[eé]mon$/i);
|
|
115
138
|
*/
|
|
116
139
|
addIrregular(single, plural) {
|
|
117
|
-
const singleLower = single
|
|
118
|
-
const pluralLower = plural
|
|
140
|
+
const singleLower = single?.toLowerCase();
|
|
141
|
+
const pluralLower = plural?.toLowerCase();
|
|
119
142
|
this.#irregularSingles[singleLower] = pluralLower;
|
|
120
143
|
this.#irregularPlurals[pluralLower] = singleLower;
|
|
121
144
|
}
|
|
122
145
|
/**
|
|
123
|
-
* Get the proper singular or plural form based on count.
|
|
146
|
+
* * Get the proper singular or plural form based on optional count.
|
|
124
147
|
* @param word Target word to pluralize or singularize.
|
|
125
148
|
* @param options Optional count and inclusive formatting.
|
|
126
149
|
* @returns The transformed word.
|
|
@@ -137,14 +160,14 @@ class Pluralizer {
|
|
|
137
160
|
return this.toPlural(word);
|
|
138
161
|
}
|
|
139
162
|
/**
|
|
140
|
-
* Convert a word to its plural form.
|
|
163
|
+
* * Convert a word to its plural form.
|
|
141
164
|
* @param word Singular form of the word.
|
|
142
165
|
* @returns Plural form of the word.
|
|
143
166
|
* @example
|
|
144
167
|
* pluralizer.toPlural('analysis'); // "analyses"
|
|
145
168
|
*/
|
|
146
169
|
toPlural(word) {
|
|
147
|
-
if (!word)
|
|
170
|
+
if (!(0, primitives_1.isNonEmptyString)(word))
|
|
148
171
|
return '';
|
|
149
172
|
const lower = word.toLowerCase();
|
|
150
173
|
if (this.#isUncountable(word))
|
|
@@ -155,14 +178,14 @@ class Pluralizer {
|
|
|
155
178
|
return this.#restoreCase(word, this.#sanitizeWord(lower, this.#pluralRules));
|
|
156
179
|
}
|
|
157
180
|
/**
|
|
158
|
-
* Convert a word to its singular form.
|
|
181
|
+
* * Convert a word to its singular form.
|
|
159
182
|
* @param word Plural form of the word.
|
|
160
183
|
* @returns Singular form of the word.
|
|
161
184
|
* @example
|
|
162
185
|
* pluralizer.toSingular('geese'); // "goose"
|
|
163
186
|
*/
|
|
164
187
|
toSingular(word) {
|
|
165
|
-
if (!word)
|
|
188
|
+
if (!(0, primitives_1.isNonEmptyString)(word))
|
|
166
189
|
return '';
|
|
167
190
|
const lower = word.toLowerCase();
|
|
168
191
|
if (this.#isUncountable(word))
|
|
@@ -173,7 +196,7 @@ class Pluralizer {
|
|
|
173
196
|
return this.#restoreCase(word, this.#sanitizeWord(lower, this.#singularRules));
|
|
174
197
|
}
|
|
175
198
|
/**
|
|
176
|
-
* Check if a given word is plural.
|
|
199
|
+
* * Check if a given word is plural.
|
|
177
200
|
* @param word Word to check.
|
|
178
201
|
* @returns True if the word is plural, otherwise false.
|
|
179
202
|
* @example
|
|
@@ -188,7 +211,7 @@ class Pluralizer {
|
|
|
188
211
|
return this.toSingular(lower) !== lower;
|
|
189
212
|
}
|
|
190
213
|
/**
|
|
191
|
-
* Check if a given word is singular.
|
|
214
|
+
* * Check if a given word is singular.
|
|
192
215
|
* @param word Word to check.
|
|
193
216
|
* @returns True if the word is singular, otherwise false.
|
|
194
217
|
* @example
|
|
@@ -205,11 +228,13 @@ class Pluralizer {
|
|
|
205
228
|
}
|
|
206
229
|
exports.Pluralizer = Pluralizer;
|
|
207
230
|
/**
|
|
208
|
-
* Default shared instance of {@link Pluralizer}.
|
|
231
|
+
* Default shared instance of {@link https://nhb-toolbox.vercel.app/docs/classes/Pluralizer Pluralizer}.
|
|
209
232
|
*
|
|
210
233
|
* - _Use this when you don’t need multiple configurations._
|
|
211
234
|
* - _It comes preloaded with standard pluralization rules, irregular forms, and uncountable nouns._
|
|
212
235
|
*
|
|
236
|
+
* @remarks For simpler pluralization (plural with only 's'), please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/formatUnitWithPlural formatUnitWithPlural} instead.
|
|
237
|
+
*
|
|
213
238
|
* * Handles English word pluralization and singularization with support for irregular forms and uncountable nouns.
|
|
214
239
|
*
|
|
215
240
|
* - Provides methods to convert words between singular and plural forms, check if a word is plural or singular, and manage custom pluralization rules.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.uncountables = exports.pluralRules = exports.irregularRules = void 0;
|
|
3
|
+
exports.singularRules = exports.uncountables = exports.pluralRules = exports.irregularRules = void 0;
|
|
4
|
+
/** Irregular rules and replacements */
|
|
4
5
|
exports.irregularRules =
|
|
5
6
|
/* @__PURE__ */ Object.freeze([
|
|
6
7
|
// Pronouns
|
|
@@ -59,6 +60,7 @@ exports.irregularRules =
|
|
|
59
60
|
['groove', 'grooves'],
|
|
60
61
|
['pickaxe', 'pickaxes'],
|
|
61
62
|
['passerby', 'passersby'],
|
|
63
|
+
['honey', 'honeys'],
|
|
62
64
|
// Words ending in with a consonant and `o`.
|
|
63
65
|
['echo', 'echoes'],
|
|
64
66
|
['dingo', 'dingoes'],
|
|
@@ -77,33 +79,46 @@ exports.irregularRules =
|
|
|
77
79
|
['schema', 'schemata'],
|
|
78
80
|
['anathema', 'anathemata'],
|
|
79
81
|
]);
|
|
82
|
+
/** Plural rules and replacements */
|
|
80
83
|
exports.pluralRules =
|
|
81
84
|
/* @__PURE__ */ Object.freeze([
|
|
82
|
-
[/
|
|
83
|
-
|
|
84
|
-
[/(
|
|
85
|
-
[/(
|
|
86
|
-
[/(alias|[^aou]us|t[lm]as|gas|ris)$/i, '$1es'],
|
|
87
|
-
[/(e[mn]u)s?$/i, '$1s'],
|
|
88
|
-
[/([^l]ias|[aeiou]las|[ejzr]as|[iu]am)$/i, '$1'],
|
|
85
|
+
[/(\P{ASCII})$/u, '$1'],
|
|
86
|
+
[/(pe)(rson|ople)$/i, '$1ople'],
|
|
87
|
+
[/(child)(?:ren)?$/i, '$1ren'],
|
|
88
|
+
[/(matr|cod|mur|sil|vert|ind|append)(?:ix|ex)$/i, '$1ices'],
|
|
89
89
|
[
|
|
90
90
|
/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,
|
|
91
91
|
'$1i',
|
|
92
92
|
],
|
|
93
93
|
[/(alumn|alg|vertebr)(?:a|ae)$/i, '$1ae'],
|
|
94
|
-
[
|
|
94
|
+
[
|
|
95
|
+
/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i,
|
|
96
|
+
'$1a',
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)um$/i,
|
|
100
|
+
'$1a',
|
|
101
|
+
],
|
|
102
|
+
[/(octop|vir)us$/i, '$1i'],
|
|
103
|
+
[/(cris|ax|test)is$/i, '$1es'],
|
|
104
|
+
[/(alias|status)$/i, '$1es'],
|
|
105
|
+
[/(shoe)$/i, '$1s'],
|
|
106
|
+
[/(bus)$/i, '$1es'],
|
|
107
|
+
[/(o)es$/i, '$1es'],
|
|
108
|
+
[/(x|ch|ss|sh|zz)$/i, '$1es'],
|
|
95
109
|
[/(her|at|gr)o$/i, '$1oes'],
|
|
96
110
|
[/sis$/i, 'ses'],
|
|
97
111
|
[/(?:(kni|wi|li)fe|(ar|l|ea|eo|oa|hoo)f)$/i, '$1$2ves'],
|
|
98
112
|
[/([^aeiouy]|qu)y$/i, '$1ies'],
|
|
99
|
-
[/(
|
|
100
|
-
[/(
|
|
101
|
-
[
|
|
102
|
-
[/(pe)(?:rson|ople)$/i, '$1ople'],
|
|
103
|
-
[/(child)(?:ren)?$/i, '$1ren'],
|
|
104
|
-
[/eaux$/i, '$0'],
|
|
113
|
+
[/(tive)$/i, '$1s'],
|
|
114
|
+
[/(hive)$/i, '$1s'],
|
|
115
|
+
[/(quiz)$/i, '$1zes'],
|
|
105
116
|
[/m[ae]n$/i, 'men'],
|
|
117
|
+
[/eaux$/i, '$0'],
|
|
118
|
+
// fallback
|
|
119
|
+
[/s$/i, 's'], // <--- final catch-all
|
|
106
120
|
]);
|
|
121
|
+
/** Uncountable constants */
|
|
107
122
|
exports.uncountables = Object.freeze(new Set([
|
|
108
123
|
'aircraft',
|
|
109
124
|
'alcohol',
|
|
@@ -119,15 +134,12 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
119
134
|
'chess',
|
|
120
135
|
'clothing',
|
|
121
136
|
'commerce',
|
|
122
|
-
'cooperation',
|
|
123
137
|
'corps',
|
|
124
138
|
'debris',
|
|
125
139
|
'diabetes',
|
|
126
140
|
'energy',
|
|
127
141
|
'equipment',
|
|
128
142
|
'furniture',
|
|
129
|
-
'gas',
|
|
130
|
-
'gold',
|
|
131
143
|
'hardware',
|
|
132
144
|
'headquarters',
|
|
133
145
|
'health',
|
|
@@ -136,6 +148,7 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
136
148
|
'information',
|
|
137
149
|
'insurance',
|
|
138
150
|
'jewelry',
|
|
151
|
+
'jewellery',
|
|
139
152
|
'knowledge',
|
|
140
153
|
'livestock',
|
|
141
154
|
'luck',
|
|
@@ -157,44 +170,24 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
157
170
|
'weather',
|
|
158
171
|
'wildlife',
|
|
159
172
|
'you',
|
|
160
|
-
'access',
|
|
161
|
-
'accommodation',
|
|
162
173
|
'adulthood',
|
|
163
174
|
'advertising',
|
|
164
|
-
'advice',
|
|
165
|
-
'aggression',
|
|
166
|
-
'aid',
|
|
167
|
-
'air',
|
|
168
175
|
'anger',
|
|
169
176
|
'applause',
|
|
170
177
|
'arithmetic',
|
|
171
|
-
'assistance',
|
|
172
|
-
'attention',
|
|
173
178
|
'bacon',
|
|
174
|
-
'beauty',
|
|
175
179
|
'beef',
|
|
176
180
|
'biology',
|
|
177
181
|
'botany',
|
|
178
|
-
'bread',
|
|
179
182
|
'carbon',
|
|
180
|
-
'cardboard',
|
|
181
|
-
'chalk',
|
|
182
183
|
'chaos',
|
|
184
|
+
'coffee',
|
|
183
185
|
'cheese',
|
|
184
186
|
'childhood',
|
|
185
|
-
'coal',
|
|
186
|
-
'coffee',
|
|
187
187
|
'compassion',
|
|
188
|
-
'comprehension',
|
|
189
|
-
'corruption',
|
|
190
188
|
'cotton',
|
|
191
|
-
'courage',
|
|
192
|
-
'currency',
|
|
193
|
-
'damage',
|
|
194
189
|
'dancing',
|
|
195
|
-
'danger',
|
|
196
190
|
'delight',
|
|
197
|
-
'dessert',
|
|
198
191
|
'dignity',
|
|
199
192
|
'dirt',
|
|
200
193
|
'distribution',
|
|
@@ -202,10 +195,7 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
202
195
|
'economics',
|
|
203
196
|
'education',
|
|
204
197
|
'electricity',
|
|
205
|
-
'employment',
|
|
206
198
|
'engineering',
|
|
207
|
-
'enjoyment',
|
|
208
|
-
'entertainment',
|
|
209
199
|
'envy',
|
|
210
200
|
'ethics',
|
|
211
201
|
'evidence',
|
|
@@ -216,52 +206,37 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
216
206
|
'fiction',
|
|
217
207
|
'flour',
|
|
218
208
|
'flu',
|
|
219
|
-
'food',
|
|
220
209
|
'freedom',
|
|
221
|
-
'fruit',
|
|
222
210
|
'fuel',
|
|
223
211
|
'fun',
|
|
224
212
|
'garbage',
|
|
225
213
|
'garlic',
|
|
226
214
|
'genetics',
|
|
227
|
-
'glass',
|
|
228
215
|
'golf',
|
|
229
216
|
'gossip',
|
|
230
217
|
'grammar',
|
|
231
|
-
'grass',
|
|
232
218
|
'gratitude',
|
|
233
219
|
'grief',
|
|
234
|
-
'ground',
|
|
235
220
|
'guilt',
|
|
236
221
|
'gymnastics',
|
|
237
|
-
'hair',
|
|
238
222
|
'happiness',
|
|
239
223
|
'harm',
|
|
240
224
|
'hate',
|
|
241
225
|
'hatred',
|
|
242
|
-
'heat',
|
|
243
226
|
'height',
|
|
244
227
|
'help',
|
|
245
228
|
'honesty',
|
|
246
|
-
'honey',
|
|
247
229
|
'hospitality',
|
|
248
230
|
'humor',
|
|
249
231
|
'humour',
|
|
250
232
|
'hunger',
|
|
251
|
-
'hydrogen',
|
|
252
|
-
'ice',
|
|
253
|
-
'ice-cream',
|
|
254
|
-
'ice cream',
|
|
255
233
|
'importance',
|
|
256
234
|
'inflation',
|
|
257
235
|
'injustice',
|
|
258
236
|
'innocence',
|
|
259
237
|
'intelligence',
|
|
260
|
-
'iron',
|
|
261
238
|
'irony',
|
|
262
|
-
'jam',
|
|
263
239
|
'jealousy',
|
|
264
|
-
'jelly',
|
|
265
240
|
'joy',
|
|
266
241
|
'judo',
|
|
267
242
|
'justice',
|
|
@@ -269,7 +244,6 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
269
244
|
'kindness',
|
|
270
245
|
'labour',
|
|
271
246
|
'lack',
|
|
272
|
-
'land',
|
|
273
247
|
'laughter',
|
|
274
248
|
'lava',
|
|
275
249
|
'leather',
|
|
@@ -283,7 +257,6 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
283
257
|
'love',
|
|
284
258
|
'luggage',
|
|
285
259
|
'magic',
|
|
286
|
-
'mail',
|
|
287
260
|
'management',
|
|
288
261
|
'mankind',
|
|
289
262
|
'marble',
|
|
@@ -291,9 +264,7 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
291
264
|
'mayonnaise',
|
|
292
265
|
'measles',
|
|
293
266
|
'meat',
|
|
294
|
-
'metal',
|
|
295
267
|
'methane',
|
|
296
|
-
'milk',
|
|
297
268
|
'mud',
|
|
298
269
|
'nature',
|
|
299
270
|
'nitrogen',
|
|
@@ -302,15 +273,11 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
302
273
|
'nutrition',
|
|
303
274
|
'obedience',
|
|
304
275
|
'obesity',
|
|
305
|
-
'oil',
|
|
306
|
-
'oxygen',
|
|
307
276
|
'passion',
|
|
308
|
-
'pasta',
|
|
309
277
|
'patience',
|
|
310
278
|
'permission',
|
|
311
279
|
'physics',
|
|
312
280
|
'poverty',
|
|
313
|
-
'power',
|
|
314
281
|
'pride',
|
|
315
282
|
'production',
|
|
316
283
|
'progress',
|
|
@@ -332,13 +299,10 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
332
299
|
'rubbish',
|
|
333
300
|
'rum',
|
|
334
301
|
'safety',
|
|
335
|
-
'salad',
|
|
336
302
|
'salt',
|
|
337
303
|
'sand',
|
|
338
304
|
'satire',
|
|
339
305
|
'scenery',
|
|
340
|
-
'seafood',
|
|
341
|
-
'seaside',
|
|
342
306
|
'shame',
|
|
343
307
|
'shopping',
|
|
344
308
|
'silence',
|
|
@@ -349,10 +313,8 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
349
313
|
'soap',
|
|
350
314
|
'soil',
|
|
351
315
|
'sorrow',
|
|
352
|
-
'soup',
|
|
353
316
|
'speed',
|
|
354
317
|
'spelling',
|
|
355
|
-
'sport',
|
|
356
318
|
'steam',
|
|
357
319
|
'strength',
|
|
358
320
|
'stuff',
|
|
@@ -369,7 +331,6 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
369
331
|
'toast',
|
|
370
332
|
'tolerance',
|
|
371
333
|
'trade',
|
|
372
|
-
'travel',
|
|
373
334
|
'trust',
|
|
374
335
|
'understanding',
|
|
375
336
|
'underwear',
|
|
@@ -385,14 +346,11 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
385
346
|
'vision',
|
|
386
347
|
'vitality',
|
|
387
348
|
'warmth',
|
|
388
|
-
'water',
|
|
389
349
|
'wealth',
|
|
390
350
|
'weight',
|
|
391
351
|
'welfare',
|
|
392
|
-
'wheat',
|
|
393
352
|
'whiskey',
|
|
394
353
|
'width',
|
|
395
|
-
'wine',
|
|
396
354
|
'wisdom',
|
|
397
355
|
'wool',
|
|
398
356
|
'yeast',
|
|
@@ -410,9 +368,76 @@ exports.uncountables = Object.freeze(new Set([
|
|
|
410
368
|
/pox$/i,
|
|
411
369
|
/sheep$/i,
|
|
412
370
|
/scissors$/i,
|
|
413
|
-
/glasses$/i,
|
|
414
371
|
/pants$/i,
|
|
415
372
|
/shorts$/i,
|
|
416
373
|
/trousers$/i,
|
|
417
374
|
/jeans$/i,
|
|
418
375
|
]));
|
|
376
|
+
/** Singular rules and replacements */
|
|
377
|
+
exports.singularRules =
|
|
378
|
+
/* @__PURE__ */ Object.freeze([
|
|
379
|
+
[/(\P{ASCII})$/u, '$1'],
|
|
380
|
+
[/(pe)(rson|ople)$/i, '$1rson'],
|
|
381
|
+
[/(child)ren$/i, '$1'],
|
|
382
|
+
[/(eau)x?$/i, '$1'],
|
|
383
|
+
[/men$/i, 'man'],
|
|
384
|
+
[/(matr|append)ices$/i, '$1ix'],
|
|
385
|
+
[/(cod|mur|sil|vert|ind)ices$/i, '$1ex'],
|
|
386
|
+
[/(alumn|alg|vertebr)ae$/i, '$1a'],
|
|
387
|
+
[
|
|
388
|
+
/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i,
|
|
389
|
+
'$1on',
|
|
390
|
+
],
|
|
391
|
+
[
|
|
392
|
+
/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i,
|
|
393
|
+
'$1um',
|
|
394
|
+
],
|
|
395
|
+
[
|
|
396
|
+
/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,
|
|
397
|
+
'$1us',
|
|
398
|
+
],
|
|
399
|
+
[/(test)(?:is|es)$/i, '$1is'],
|
|
400
|
+
[/(movie|twelve|abuse|e[mn]u)s$/i, '$1'],
|
|
401
|
+
[
|
|
402
|
+
/(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i,
|
|
403
|
+
'$1sis',
|
|
404
|
+
],
|
|
405
|
+
[
|
|
406
|
+
/(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i,
|
|
407
|
+
'$1',
|
|
408
|
+
],
|
|
409
|
+
[/(seraph|cherub)im$/i, '$1'],
|
|
410
|
+
[/\b((?:tit)?m|l)ice$/i, '$1ouse'],
|
|
411
|
+
[/\b(mon|smil)ies$/i, '$1ey'],
|
|
412
|
+
[
|
|
413
|
+
/\b([pl]|zomb|(?:neck|cross)?t|coll|faer|food|gen|goon|group|lass|talk|goal|cut)ies$/i,
|
|
414
|
+
'$1ie',
|
|
415
|
+
],
|
|
416
|
+
[/ies$/i, 'y'],
|
|
417
|
+
[/(ar|(?:wo|[ae])l|[eo][ao])ves$/i, '$1f'],
|
|
418
|
+
[
|
|
419
|
+
/(wi|kni|(?:after|half|high|low|mid|non|night|[^\w]|^)li)ves$/i,
|
|
420
|
+
'$1fe',
|
|
421
|
+
],
|
|
422
|
+
[/(ss)$/i, '$1'],
|
|
423
|
+
[/(quiz)zes$/i, '$1'],
|
|
424
|
+
[/(matr)ices$/i, '$1ix'],
|
|
425
|
+
[/(vert|ind)ices$/i, '$1ex'],
|
|
426
|
+
[/^(ox)en$/i, '$1'],
|
|
427
|
+
[/(alias|status)es$/i, '$1'],
|
|
428
|
+
[/(octop|vir)i$/i, '$1us'],
|
|
429
|
+
[/(cris|ax|test)es$/i, '$1is'],
|
|
430
|
+
[/(shoe)s$/i, '$1'],
|
|
431
|
+
[/(o)es$/i, '$1'],
|
|
432
|
+
[/(bus)es$/i, '$1'],
|
|
433
|
+
[/ices$/i, 'ex'],
|
|
434
|
+
[/(hive)s$/i, '$1'],
|
|
435
|
+
[/(tive)s$/i, '$1'],
|
|
436
|
+
[/([^f])ves$/i, '$1fe'],
|
|
437
|
+
[/([lr])ves$/i, '$1f'],
|
|
438
|
+
[/(^analy)ses$/i, '$1sis'],
|
|
439
|
+
[/([ti])a$/i, '$1um'],
|
|
440
|
+
[/(n)ews$/i, '$1ews'],
|
|
441
|
+
// <-- put generic catch-all last
|
|
442
|
+
[/s$/i, ''],
|
|
443
|
+
]);
|
|
@@ -177,6 +177,8 @@ function extractURLs(str) {
|
|
|
177
177
|
/**
|
|
178
178
|
* * Returns a grammatically correct unit string, optionally prefixed with the number.
|
|
179
179
|
*
|
|
180
|
+
* @remarks For complex and versatile pluralization, please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/pluralizer pluralizer} or {@link https://nhb-toolbox.vercel.app/docs/classes/Pluralizer Pluralizer Class} instead.
|
|
181
|
+
*
|
|
180
182
|
* @param count The numeric value to determine singular or plural.
|
|
181
183
|
* @param unit The unit name (e.g., "day", "hour").
|
|
182
184
|
* @param withNumber Whether to prefix the count before the unit. Defaults to `true`.
|
|
@@ -8,6 +8,11 @@ import type { PluralizeOptions } from './types';
|
|
|
8
8
|
* - Automatically loads common irregular forms and uncountable nouns.
|
|
9
9
|
* - Supports options for count-based pluralization, allowing for inclusive formatting.
|
|
10
10
|
* - This class is useful for applications that need to handle natural language processing, such as chatbots, content management systems, or any text processing tasks that require accurate pluralization.
|
|
11
|
+
*
|
|
12
|
+
* @remarks For simpler pluralization (plural with only 's'), please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/formatUnitWithPlural formatUnitWithPlural} instead.
|
|
13
|
+
*
|
|
14
|
+
* @remarks For ready to use instance, please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/pluralizer pluralizer} instead.
|
|
15
|
+
*
|
|
11
16
|
* @example
|
|
12
17
|
* const pluralizer = new Pluralizer();
|
|
13
18
|
* pluralizer.pluralize('child'); // "children"
|
|
@@ -18,7 +23,7 @@ export declare class Pluralizer {
|
|
|
18
23
|
#private;
|
|
19
24
|
/**
|
|
20
25
|
* Initializes the Pluralizer with default rules and exceptions.
|
|
21
|
-
* Automatically loads irregular
|
|
26
|
+
* Automatically loads irregular, pluralization and singular rules along with pre-defined uncountable nouns.
|
|
22
27
|
*/
|
|
23
28
|
constructor();
|
|
24
29
|
/**
|
|
@@ -30,7 +35,7 @@ export declare class Pluralizer {
|
|
|
30
35
|
*/
|
|
31
36
|
addPluralRule(rule: RegExp, replacement: string): void;
|
|
32
37
|
/**
|
|
33
|
-
* Add a new singularization rule.
|
|
38
|
+
* * Add a new singularization rule.
|
|
34
39
|
* @param rule Pattern to match plural words.
|
|
35
40
|
* @param replacement Replacement pattern for singular form.
|
|
36
41
|
* @example
|
|
@@ -39,7 +44,7 @@ export declare class Pluralizer {
|
|
|
39
44
|
addSingularRule(rule: RegExp, replacement: string): void;
|
|
40
45
|
addUncountable(word: string | RegExp): void;
|
|
41
46
|
/**
|
|
42
|
-
* Add a word or pattern that should never change between singular and plural.
|
|
47
|
+
* * Add a word or pattern that should never change between singular and plural.
|
|
43
48
|
* @param word A word or regex pattern.
|
|
44
49
|
* @example
|
|
45
50
|
* pluralizer.addUncountable('fish');
|
|
@@ -47,7 +52,7 @@ export declare class Pluralizer {
|
|
|
47
52
|
*/
|
|
48
53
|
addIrregular(single: string, plural: string): void;
|
|
49
54
|
/**
|
|
50
|
-
* Get the proper singular or plural form based on count.
|
|
55
|
+
* * Get the proper singular or plural form based on optional count.
|
|
51
56
|
* @param word Target word to pluralize or singularize.
|
|
52
57
|
* @param options Optional count and inclusive formatting.
|
|
53
58
|
* @returns The transformed word.
|
|
@@ -57,7 +62,7 @@ export declare class Pluralizer {
|
|
|
57
62
|
*/
|
|
58
63
|
pluralize(word: string, options?: PluralizeOptions): string;
|
|
59
64
|
/**
|
|
60
|
-
* Convert a word to its plural form.
|
|
65
|
+
* * Convert a word to its plural form.
|
|
61
66
|
* @param word Singular form of the word.
|
|
62
67
|
* @returns Plural form of the word.
|
|
63
68
|
* @example
|
|
@@ -65,7 +70,7 @@ export declare class Pluralizer {
|
|
|
65
70
|
*/
|
|
66
71
|
toPlural(word: string): string;
|
|
67
72
|
/**
|
|
68
|
-
* Convert a word to its singular form.
|
|
73
|
+
* * Convert a word to its singular form.
|
|
69
74
|
* @param word Plural form of the word.
|
|
70
75
|
* @returns Singular form of the word.
|
|
71
76
|
* @example
|
|
@@ -73,7 +78,7 @@ export declare class Pluralizer {
|
|
|
73
78
|
*/
|
|
74
79
|
toSingular(word: string): string;
|
|
75
80
|
/**
|
|
76
|
-
* Check if a given word is plural.
|
|
81
|
+
* * Check if a given word is plural.
|
|
77
82
|
* @param word Word to check.
|
|
78
83
|
* @returns True if the word is plural, otherwise false.
|
|
79
84
|
* @example
|
|
@@ -81,7 +86,7 @@ export declare class Pluralizer {
|
|
|
81
86
|
*/
|
|
82
87
|
isPlural(word: string): boolean;
|
|
83
88
|
/**
|
|
84
|
-
* Check if a given word is singular.
|
|
89
|
+
* * Check if a given word is singular.
|
|
85
90
|
* @param word Word to check.
|
|
86
91
|
* @returns True if the word is singular, otherwise false.
|
|
87
92
|
* @example
|
|
@@ -90,11 +95,13 @@ export declare class Pluralizer {
|
|
|
90
95
|
isSingular(word: string): boolean;
|
|
91
96
|
}
|
|
92
97
|
/**
|
|
93
|
-
* Default shared instance of {@link Pluralizer}.
|
|
98
|
+
* Default shared instance of {@link https://nhb-toolbox.vercel.app/docs/classes/Pluralizer Pluralizer}.
|
|
94
99
|
*
|
|
95
100
|
* - _Use this when you don’t need multiple configurations._
|
|
96
101
|
* - _It comes preloaded with standard pluralization rules, irregular forms, and uncountable nouns._
|
|
97
102
|
*
|
|
103
|
+
* @remarks For simpler pluralization (plural with only 's'), please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/formatUnitWithPlural formatUnitWithPlural} instead.
|
|
104
|
+
*
|
|
98
105
|
* * Handles English word pluralization and singularization with support for irregular forms and uncountable nouns.
|
|
99
106
|
*
|
|
100
107
|
* - Provides methods to convert words between singular and plural forms, check if a word is plural or singular, and manage custom pluralization rules.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pluralizer.d.ts","sourceRoot":"","sources":["../../../src/pluralize/Pluralizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Pluralizer.d.ts","sourceRoot":"","sources":["../../../src/pluralize/Pluralizer.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAgB,gBAAgB,EAAiB,MAAM,SAAS,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,UAAU;;IAOtB;;;OAGG;;IAoFH;;;;;;OAMG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAItD;;;;;;OAMG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAIxD,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAM3C;;;;;;OAMG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAOlD;;;;;;;;OAQG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM;IAY/D;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAiB9B;;;;;;OAMG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAiBhC;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAO/B;;;;;;OAMG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAMjC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,UAAU,YAAmB,CAAC"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { PluralizeRule } from './types';
|
|
2
|
+
/** Irregular rules and replacements */
|
|
2
3
|
export declare const irregularRules: readonly [string, string][];
|
|
4
|
+
/** Plural rules and replacements */
|
|
3
5
|
export declare const pluralRules: readonly PluralizeRule[];
|
|
6
|
+
/** Uncountable constants */
|
|
4
7
|
export declare const uncountables: Readonly<Set<string | RegExp>>;
|
|
8
|
+
/** Singular rules and replacements */
|
|
9
|
+
export declare const singularRules: readonly PluralizeRule[];
|
|
5
10
|
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/pluralize/rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,eAAO,MAAM,cAAc,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/pluralize/rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,uCAAuC;AACvC,eAAO,MAAM,cAAc,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EA+EnD,CAAC;AAEJ,oCAAoC;AACpC,eAAO,MAAM,WAAW,EAAE,SAAS,aAAa,EAqC7C,CAAC;AAEJ,4BAA4B;AAC5B,eAAO,MAAM,YAAY,gCAiQxB,CAAC;AAEF,sCAAsC;AACtC,eAAO,MAAM,aAAa,EAAE,SAAS,aAAa,EAkE/C,CAAC"}
|
|
@@ -77,6 +77,8 @@ export declare function extractURLs(str: string): string[];
|
|
|
77
77
|
/**
|
|
78
78
|
* * Returns a grammatically correct unit string, optionally prefixed with the number.
|
|
79
79
|
*
|
|
80
|
+
* @remarks For complex and versatile pluralization, please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/pluralizer pluralizer} or {@link https://nhb-toolbox.vercel.app/docs/classes/Pluralizer Pluralizer Class} instead.
|
|
81
|
+
*
|
|
80
82
|
* @param count The numeric value to determine singular or plural.
|
|
81
83
|
* @param unit The unit name (e.g., "day", "hour").
|
|
82
84
|
* @param withNumber Whether to prefix the count before the unit. Defaults to `true`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../../src/string/convert.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAwF5E;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAC9B,OAAO,MAAM,EACb,MAAM,MAAM,GAAG,MAAM,EACrB,SAAS,MAAM,KACb,MAYF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,SAAS,CAAC,MAAM,CAK7D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,EAAE,UAAU,WAAW,KAAG,MAcjE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,MAI7C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjD;AAED
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../../src/string/convert.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAwF5E;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAC9B,OAAO,MAAM,EACb,MAAM,MAAM,GAAG,MAAM,EACrB,SAAS,MAAM,KACb,MAYF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,SAAS,CAAC,MAAM,CAK7D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,EAAE,UAAU,WAAW,KAAG,MAcjE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,MAI7C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,UAAU,UAAO,GACf,MAAM,CAKR"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { isNonEmptyString } from '../guards/primitives.js';
|
|
1
2
|
import { normalizeNumber } from '../number/utilities.js';
|
|
2
|
-
import { irregularRules, pluralRules, uncountables } from './rules.js';
|
|
3
|
+
import { irregularRules, pluralRules, singularRules, uncountables, } from './rules.js';
|
|
3
4
|
/**
|
|
4
5
|
* * Handles English word pluralization and singularization with support for irregular forms and uncountable nouns.
|
|
5
6
|
*
|
|
@@ -9,6 +10,11 @@ import { irregularRules, pluralRules, uncountables } from './rules.js';
|
|
|
9
10
|
* - Automatically loads common irregular forms and uncountable nouns.
|
|
10
11
|
* - Supports options for count-based pluralization, allowing for inclusive formatting.
|
|
11
12
|
* - This class is useful for applications that need to handle natural language processing, such as chatbots, content management systems, or any text processing tasks that require accurate pluralization.
|
|
13
|
+
*
|
|
14
|
+
* @remarks For simpler pluralization (plural with only 's'), please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/formatUnitWithPlural formatUnitWithPlural} instead.
|
|
15
|
+
*
|
|
16
|
+
* @remarks For ready to use instance, please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/pluralizer pluralizer} instead.
|
|
17
|
+
*
|
|
12
18
|
* @example
|
|
13
19
|
* const pluralizer = new Pluralizer();
|
|
14
20
|
* pluralizer.pluralize('child'); // "children"
|
|
@@ -23,7 +29,7 @@ export class Pluralizer {
|
|
|
23
29
|
#irregularPlurals = {};
|
|
24
30
|
/**
|
|
25
31
|
* Initializes the Pluralizer with default rules and exceptions.
|
|
26
|
-
* Automatically loads irregular
|
|
32
|
+
* Automatically loads irregular, pluralization and singular rules along with pre-defined uncountable nouns.
|
|
27
33
|
*/
|
|
28
34
|
constructor() {
|
|
29
35
|
this.#loadRules();
|
|
@@ -37,21 +43,38 @@ export class Pluralizer {
|
|
|
37
43
|
pluralRules.forEach(([rule, replacement]) => {
|
|
38
44
|
this.addPluralRule(rule, replacement);
|
|
39
45
|
});
|
|
46
|
+
// ! Load singular rules
|
|
47
|
+
singularRules.forEach(([rule, replacement]) => {
|
|
48
|
+
this.addSingularRule(rule, replacement);
|
|
49
|
+
});
|
|
40
50
|
// ! Load uncountables
|
|
41
51
|
uncountables.forEach((word) => {
|
|
42
52
|
this.addUncountable(word);
|
|
43
53
|
});
|
|
44
54
|
}
|
|
45
|
-
#restoreCase(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
#restoreCase(original, transformed) {
|
|
56
|
+
let result = '';
|
|
57
|
+
for (let i = 0; i < transformed.length; i++) {
|
|
58
|
+
const origChar = original[i];
|
|
59
|
+
if (origChar) {
|
|
60
|
+
if (origChar.toUpperCase() === origChar &&
|
|
61
|
+
origChar.toLowerCase() !== origChar) {
|
|
62
|
+
result += transformed[i].toUpperCase();
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
result += transformed[i].toLowerCase();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
result += transformed[i].toLowerCase();
|
|
70
|
+
}
|
|
50
71
|
}
|
|
51
|
-
return
|
|
72
|
+
return result;
|
|
52
73
|
}
|
|
53
74
|
#sanitizeWord(word, rules) {
|
|
54
|
-
if (!
|
|
75
|
+
if (!isNonEmptyString(word))
|
|
76
|
+
return '';
|
|
77
|
+
if (this.#isUncountable(word)) {
|
|
55
78
|
return word;
|
|
56
79
|
}
|
|
57
80
|
for (let i = rules.length - 1; i >= 0; i--) {
|
|
@@ -67,7 +90,7 @@ export class Pluralizer {
|
|
|
67
90
|
* Supports both string and RegExp entries.
|
|
68
91
|
*/
|
|
69
92
|
#isUncountable(word) {
|
|
70
|
-
const lower = word
|
|
93
|
+
const lower = word?.toLowerCase();
|
|
71
94
|
for (const entry of this.#uncountables) {
|
|
72
95
|
if (typeof entry === 'string') {
|
|
73
96
|
if (entry === lower)
|
|
@@ -91,7 +114,7 @@ export class Pluralizer {
|
|
|
91
114
|
this.#pluralRules.push([rule, replacement]);
|
|
92
115
|
}
|
|
93
116
|
/**
|
|
94
|
-
* Add a new singularization rule.
|
|
117
|
+
* * Add a new singularization rule.
|
|
95
118
|
* @param rule Pattern to match plural words.
|
|
96
119
|
* @param replacement Replacement pattern for singular form.
|
|
97
120
|
* @example
|
|
@@ -101,23 +124,23 @@ export class Pluralizer {
|
|
|
101
124
|
this.#singularRules.push([rule, replacement]);
|
|
102
125
|
}
|
|
103
126
|
addUncountable(word) {
|
|
104
|
-
this.#uncountables.add(typeof word === 'string' ? word
|
|
127
|
+
this.#uncountables.add(typeof word === 'string' ? word?.toLowerCase() : word);
|
|
105
128
|
}
|
|
106
129
|
/**
|
|
107
|
-
* Add a word or pattern that should never change between singular and plural.
|
|
130
|
+
* * Add a word or pattern that should never change between singular and plural.
|
|
108
131
|
* @param word A word or regex pattern.
|
|
109
132
|
* @example
|
|
110
133
|
* pluralizer.addUncountable('fish');
|
|
111
134
|
* pluralizer.addUncountable(/pok[eé]mon$/i);
|
|
112
135
|
*/
|
|
113
136
|
addIrregular(single, plural) {
|
|
114
|
-
const singleLower = single
|
|
115
|
-
const pluralLower = plural
|
|
137
|
+
const singleLower = single?.toLowerCase();
|
|
138
|
+
const pluralLower = plural?.toLowerCase();
|
|
116
139
|
this.#irregularSingles[singleLower] = pluralLower;
|
|
117
140
|
this.#irregularPlurals[pluralLower] = singleLower;
|
|
118
141
|
}
|
|
119
142
|
/**
|
|
120
|
-
* Get the proper singular or plural form based on count.
|
|
143
|
+
* * Get the proper singular or plural form based on optional count.
|
|
121
144
|
* @param word Target word to pluralize or singularize.
|
|
122
145
|
* @param options Optional count and inclusive formatting.
|
|
123
146
|
* @returns The transformed word.
|
|
@@ -134,14 +157,14 @@ export class Pluralizer {
|
|
|
134
157
|
return this.toPlural(word);
|
|
135
158
|
}
|
|
136
159
|
/**
|
|
137
|
-
* Convert a word to its plural form.
|
|
160
|
+
* * Convert a word to its plural form.
|
|
138
161
|
* @param word Singular form of the word.
|
|
139
162
|
* @returns Plural form of the word.
|
|
140
163
|
* @example
|
|
141
164
|
* pluralizer.toPlural('analysis'); // "analyses"
|
|
142
165
|
*/
|
|
143
166
|
toPlural(word) {
|
|
144
|
-
if (!word)
|
|
167
|
+
if (!isNonEmptyString(word))
|
|
145
168
|
return '';
|
|
146
169
|
const lower = word.toLowerCase();
|
|
147
170
|
if (this.#isUncountable(word))
|
|
@@ -152,14 +175,14 @@ export class Pluralizer {
|
|
|
152
175
|
return this.#restoreCase(word, this.#sanitizeWord(lower, this.#pluralRules));
|
|
153
176
|
}
|
|
154
177
|
/**
|
|
155
|
-
* Convert a word to its singular form.
|
|
178
|
+
* * Convert a word to its singular form.
|
|
156
179
|
* @param word Plural form of the word.
|
|
157
180
|
* @returns Singular form of the word.
|
|
158
181
|
* @example
|
|
159
182
|
* pluralizer.toSingular('geese'); // "goose"
|
|
160
183
|
*/
|
|
161
184
|
toSingular(word) {
|
|
162
|
-
if (!word)
|
|
185
|
+
if (!isNonEmptyString(word))
|
|
163
186
|
return '';
|
|
164
187
|
const lower = word.toLowerCase();
|
|
165
188
|
if (this.#isUncountable(word))
|
|
@@ -170,7 +193,7 @@ export class Pluralizer {
|
|
|
170
193
|
return this.#restoreCase(word, this.#sanitizeWord(lower, this.#singularRules));
|
|
171
194
|
}
|
|
172
195
|
/**
|
|
173
|
-
* Check if a given word is plural.
|
|
196
|
+
* * Check if a given word is plural.
|
|
174
197
|
* @param word Word to check.
|
|
175
198
|
* @returns True if the word is plural, otherwise false.
|
|
176
199
|
* @example
|
|
@@ -185,7 +208,7 @@ export class Pluralizer {
|
|
|
185
208
|
return this.toSingular(lower) !== lower;
|
|
186
209
|
}
|
|
187
210
|
/**
|
|
188
|
-
* Check if a given word is singular.
|
|
211
|
+
* * Check if a given word is singular.
|
|
189
212
|
* @param word Word to check.
|
|
190
213
|
* @returns True if the word is singular, otherwise false.
|
|
191
214
|
* @example
|
|
@@ -201,11 +224,13 @@ export class Pluralizer {
|
|
|
201
224
|
}
|
|
202
225
|
}
|
|
203
226
|
/**
|
|
204
|
-
* Default shared instance of {@link Pluralizer}.
|
|
227
|
+
* Default shared instance of {@link https://nhb-toolbox.vercel.app/docs/classes/Pluralizer Pluralizer}.
|
|
205
228
|
*
|
|
206
229
|
* - _Use this when you don’t need multiple configurations._
|
|
207
230
|
* - _It comes preloaded with standard pluralization rules, irregular forms, and uncountable nouns._
|
|
208
231
|
*
|
|
232
|
+
* @remarks For simpler pluralization (plural with only 's'), please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/formatUnitWithPlural formatUnitWithPlural} instead.
|
|
233
|
+
*
|
|
209
234
|
* * Handles English word pluralization and singularization with support for irregular forms and uncountable nouns.
|
|
210
235
|
*
|
|
211
236
|
* - Provides methods to convert words between singular and plural forms, check if a word is plural or singular, and manage custom pluralization rules.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Irregular rules and replacements */
|
|
1
2
|
export const irregularRules =
|
|
2
3
|
/* @__PURE__ */ Object.freeze([
|
|
3
4
|
// Pronouns
|
|
@@ -56,6 +57,7 @@ export const irregularRules =
|
|
|
56
57
|
['groove', 'grooves'],
|
|
57
58
|
['pickaxe', 'pickaxes'],
|
|
58
59
|
['passerby', 'passersby'],
|
|
60
|
+
['honey', 'honeys'],
|
|
59
61
|
// Words ending in with a consonant and `o`.
|
|
60
62
|
['echo', 'echoes'],
|
|
61
63
|
['dingo', 'dingoes'],
|
|
@@ -74,33 +76,46 @@ export const irregularRules =
|
|
|
74
76
|
['schema', 'schemata'],
|
|
75
77
|
['anathema', 'anathemata'],
|
|
76
78
|
]);
|
|
79
|
+
/** Plural rules and replacements */
|
|
77
80
|
export const pluralRules =
|
|
78
81
|
/* @__PURE__ */ Object.freeze([
|
|
79
|
-
[/
|
|
80
|
-
|
|
81
|
-
[/(
|
|
82
|
-
[/(
|
|
83
|
-
[/(alias|[^aou]us|t[lm]as|gas|ris)$/i, '$1es'],
|
|
84
|
-
[/(e[mn]u)s?$/i, '$1s'],
|
|
85
|
-
[/([^l]ias|[aeiou]las|[ejzr]as|[iu]am)$/i, '$1'],
|
|
82
|
+
[/(\P{ASCII})$/u, '$1'],
|
|
83
|
+
[/(pe)(rson|ople)$/i, '$1ople'],
|
|
84
|
+
[/(child)(?:ren)?$/i, '$1ren'],
|
|
85
|
+
[/(matr|cod|mur|sil|vert|ind|append)(?:ix|ex)$/i, '$1ices'],
|
|
86
86
|
[
|
|
87
87
|
/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,
|
|
88
88
|
'$1i',
|
|
89
89
|
],
|
|
90
90
|
[/(alumn|alg|vertebr)(?:a|ae)$/i, '$1ae'],
|
|
91
|
-
[
|
|
91
|
+
[
|
|
92
|
+
/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i,
|
|
93
|
+
'$1a',
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)um$/i,
|
|
97
|
+
'$1a',
|
|
98
|
+
],
|
|
99
|
+
[/(octop|vir)us$/i, '$1i'],
|
|
100
|
+
[/(cris|ax|test)is$/i, '$1es'],
|
|
101
|
+
[/(alias|status)$/i, '$1es'],
|
|
102
|
+
[/(shoe)$/i, '$1s'],
|
|
103
|
+
[/(bus)$/i, '$1es'],
|
|
104
|
+
[/(o)es$/i, '$1es'],
|
|
105
|
+
[/(x|ch|ss|sh|zz)$/i, '$1es'],
|
|
92
106
|
[/(her|at|gr)o$/i, '$1oes'],
|
|
93
107
|
[/sis$/i, 'ses'],
|
|
94
108
|
[/(?:(kni|wi|li)fe|(ar|l|ea|eo|oa|hoo)f)$/i, '$1$2ves'],
|
|
95
109
|
[/([^aeiouy]|qu)y$/i, '$1ies'],
|
|
96
|
-
[/(
|
|
97
|
-
[/(
|
|
98
|
-
[
|
|
99
|
-
[/(pe)(?:rson|ople)$/i, '$1ople'],
|
|
100
|
-
[/(child)(?:ren)?$/i, '$1ren'],
|
|
101
|
-
[/eaux$/i, '$0'],
|
|
110
|
+
[/(tive)$/i, '$1s'],
|
|
111
|
+
[/(hive)$/i, '$1s'],
|
|
112
|
+
[/(quiz)$/i, '$1zes'],
|
|
102
113
|
[/m[ae]n$/i, 'men'],
|
|
114
|
+
[/eaux$/i, '$0'],
|
|
115
|
+
// fallback
|
|
116
|
+
[/s$/i, 's'], // <--- final catch-all
|
|
103
117
|
]);
|
|
118
|
+
/** Uncountable constants */
|
|
104
119
|
export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
105
120
|
'aircraft',
|
|
106
121
|
'alcohol',
|
|
@@ -116,15 +131,12 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
116
131
|
'chess',
|
|
117
132
|
'clothing',
|
|
118
133
|
'commerce',
|
|
119
|
-
'cooperation',
|
|
120
134
|
'corps',
|
|
121
135
|
'debris',
|
|
122
136
|
'diabetes',
|
|
123
137
|
'energy',
|
|
124
138
|
'equipment',
|
|
125
139
|
'furniture',
|
|
126
|
-
'gas',
|
|
127
|
-
'gold',
|
|
128
140
|
'hardware',
|
|
129
141
|
'headquarters',
|
|
130
142
|
'health',
|
|
@@ -133,6 +145,7 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
133
145
|
'information',
|
|
134
146
|
'insurance',
|
|
135
147
|
'jewelry',
|
|
148
|
+
'jewellery',
|
|
136
149
|
'knowledge',
|
|
137
150
|
'livestock',
|
|
138
151
|
'luck',
|
|
@@ -154,44 +167,24 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
154
167
|
'weather',
|
|
155
168
|
'wildlife',
|
|
156
169
|
'you',
|
|
157
|
-
'access',
|
|
158
|
-
'accommodation',
|
|
159
170
|
'adulthood',
|
|
160
171
|
'advertising',
|
|
161
|
-
'advice',
|
|
162
|
-
'aggression',
|
|
163
|
-
'aid',
|
|
164
|
-
'air',
|
|
165
172
|
'anger',
|
|
166
173
|
'applause',
|
|
167
174
|
'arithmetic',
|
|
168
|
-
'assistance',
|
|
169
|
-
'attention',
|
|
170
175
|
'bacon',
|
|
171
|
-
'beauty',
|
|
172
176
|
'beef',
|
|
173
177
|
'biology',
|
|
174
178
|
'botany',
|
|
175
|
-
'bread',
|
|
176
179
|
'carbon',
|
|
177
|
-
'cardboard',
|
|
178
|
-
'chalk',
|
|
179
180
|
'chaos',
|
|
181
|
+
'coffee',
|
|
180
182
|
'cheese',
|
|
181
183
|
'childhood',
|
|
182
|
-
'coal',
|
|
183
|
-
'coffee',
|
|
184
184
|
'compassion',
|
|
185
|
-
'comprehension',
|
|
186
|
-
'corruption',
|
|
187
185
|
'cotton',
|
|
188
|
-
'courage',
|
|
189
|
-
'currency',
|
|
190
|
-
'damage',
|
|
191
186
|
'dancing',
|
|
192
|
-
'danger',
|
|
193
187
|
'delight',
|
|
194
|
-
'dessert',
|
|
195
188
|
'dignity',
|
|
196
189
|
'dirt',
|
|
197
190
|
'distribution',
|
|
@@ -199,10 +192,7 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
199
192
|
'economics',
|
|
200
193
|
'education',
|
|
201
194
|
'electricity',
|
|
202
|
-
'employment',
|
|
203
195
|
'engineering',
|
|
204
|
-
'enjoyment',
|
|
205
|
-
'entertainment',
|
|
206
196
|
'envy',
|
|
207
197
|
'ethics',
|
|
208
198
|
'evidence',
|
|
@@ -213,52 +203,37 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
213
203
|
'fiction',
|
|
214
204
|
'flour',
|
|
215
205
|
'flu',
|
|
216
|
-
'food',
|
|
217
206
|
'freedom',
|
|
218
|
-
'fruit',
|
|
219
207
|
'fuel',
|
|
220
208
|
'fun',
|
|
221
209
|
'garbage',
|
|
222
210
|
'garlic',
|
|
223
211
|
'genetics',
|
|
224
|
-
'glass',
|
|
225
212
|
'golf',
|
|
226
213
|
'gossip',
|
|
227
214
|
'grammar',
|
|
228
|
-
'grass',
|
|
229
215
|
'gratitude',
|
|
230
216
|
'grief',
|
|
231
|
-
'ground',
|
|
232
217
|
'guilt',
|
|
233
218
|
'gymnastics',
|
|
234
|
-
'hair',
|
|
235
219
|
'happiness',
|
|
236
220
|
'harm',
|
|
237
221
|
'hate',
|
|
238
222
|
'hatred',
|
|
239
|
-
'heat',
|
|
240
223
|
'height',
|
|
241
224
|
'help',
|
|
242
225
|
'honesty',
|
|
243
|
-
'honey',
|
|
244
226
|
'hospitality',
|
|
245
227
|
'humor',
|
|
246
228
|
'humour',
|
|
247
229
|
'hunger',
|
|
248
|
-
'hydrogen',
|
|
249
|
-
'ice',
|
|
250
|
-
'ice-cream',
|
|
251
|
-
'ice cream',
|
|
252
230
|
'importance',
|
|
253
231
|
'inflation',
|
|
254
232
|
'injustice',
|
|
255
233
|
'innocence',
|
|
256
234
|
'intelligence',
|
|
257
|
-
'iron',
|
|
258
235
|
'irony',
|
|
259
|
-
'jam',
|
|
260
236
|
'jealousy',
|
|
261
|
-
'jelly',
|
|
262
237
|
'joy',
|
|
263
238
|
'judo',
|
|
264
239
|
'justice',
|
|
@@ -266,7 +241,6 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
266
241
|
'kindness',
|
|
267
242
|
'labour',
|
|
268
243
|
'lack',
|
|
269
|
-
'land',
|
|
270
244
|
'laughter',
|
|
271
245
|
'lava',
|
|
272
246
|
'leather',
|
|
@@ -280,7 +254,6 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
280
254
|
'love',
|
|
281
255
|
'luggage',
|
|
282
256
|
'magic',
|
|
283
|
-
'mail',
|
|
284
257
|
'management',
|
|
285
258
|
'mankind',
|
|
286
259
|
'marble',
|
|
@@ -288,9 +261,7 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
288
261
|
'mayonnaise',
|
|
289
262
|
'measles',
|
|
290
263
|
'meat',
|
|
291
|
-
'metal',
|
|
292
264
|
'methane',
|
|
293
|
-
'milk',
|
|
294
265
|
'mud',
|
|
295
266
|
'nature',
|
|
296
267
|
'nitrogen',
|
|
@@ -299,15 +270,11 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
299
270
|
'nutrition',
|
|
300
271
|
'obedience',
|
|
301
272
|
'obesity',
|
|
302
|
-
'oil',
|
|
303
|
-
'oxygen',
|
|
304
273
|
'passion',
|
|
305
|
-
'pasta',
|
|
306
274
|
'patience',
|
|
307
275
|
'permission',
|
|
308
276
|
'physics',
|
|
309
277
|
'poverty',
|
|
310
|
-
'power',
|
|
311
278
|
'pride',
|
|
312
279
|
'production',
|
|
313
280
|
'progress',
|
|
@@ -329,13 +296,10 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
329
296
|
'rubbish',
|
|
330
297
|
'rum',
|
|
331
298
|
'safety',
|
|
332
|
-
'salad',
|
|
333
299
|
'salt',
|
|
334
300
|
'sand',
|
|
335
301
|
'satire',
|
|
336
302
|
'scenery',
|
|
337
|
-
'seafood',
|
|
338
|
-
'seaside',
|
|
339
303
|
'shame',
|
|
340
304
|
'shopping',
|
|
341
305
|
'silence',
|
|
@@ -346,10 +310,8 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
346
310
|
'soap',
|
|
347
311
|
'soil',
|
|
348
312
|
'sorrow',
|
|
349
|
-
'soup',
|
|
350
313
|
'speed',
|
|
351
314
|
'spelling',
|
|
352
|
-
'sport',
|
|
353
315
|
'steam',
|
|
354
316
|
'strength',
|
|
355
317
|
'stuff',
|
|
@@ -366,7 +328,6 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
366
328
|
'toast',
|
|
367
329
|
'tolerance',
|
|
368
330
|
'trade',
|
|
369
|
-
'travel',
|
|
370
331
|
'trust',
|
|
371
332
|
'understanding',
|
|
372
333
|
'underwear',
|
|
@@ -382,14 +343,11 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
382
343
|
'vision',
|
|
383
344
|
'vitality',
|
|
384
345
|
'warmth',
|
|
385
|
-
'water',
|
|
386
346
|
'wealth',
|
|
387
347
|
'weight',
|
|
388
348
|
'welfare',
|
|
389
|
-
'wheat',
|
|
390
349
|
'whiskey',
|
|
391
350
|
'width',
|
|
392
|
-
'wine',
|
|
393
351
|
'wisdom',
|
|
394
352
|
'wool',
|
|
395
353
|
'yeast',
|
|
@@ -407,9 +365,76 @@ export const uncountables = /* @__PURE__ */ Object.freeze(new Set([
|
|
|
407
365
|
/pox$/i,
|
|
408
366
|
/sheep$/i,
|
|
409
367
|
/scissors$/i,
|
|
410
|
-
/glasses$/i,
|
|
411
368
|
/pants$/i,
|
|
412
369
|
/shorts$/i,
|
|
413
370
|
/trousers$/i,
|
|
414
371
|
/jeans$/i,
|
|
415
372
|
]));
|
|
373
|
+
/** Singular rules and replacements */
|
|
374
|
+
export const singularRules =
|
|
375
|
+
/* @__PURE__ */ Object.freeze([
|
|
376
|
+
[/(\P{ASCII})$/u, '$1'],
|
|
377
|
+
[/(pe)(rson|ople)$/i, '$1rson'],
|
|
378
|
+
[/(child)ren$/i, '$1'],
|
|
379
|
+
[/(eau)x?$/i, '$1'],
|
|
380
|
+
[/men$/i, 'man'],
|
|
381
|
+
[/(matr|append)ices$/i, '$1ix'],
|
|
382
|
+
[/(cod|mur|sil|vert|ind)ices$/i, '$1ex'],
|
|
383
|
+
[/(alumn|alg|vertebr)ae$/i, '$1a'],
|
|
384
|
+
[
|
|
385
|
+
/(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i,
|
|
386
|
+
'$1on',
|
|
387
|
+
],
|
|
388
|
+
[
|
|
389
|
+
/(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i,
|
|
390
|
+
'$1um',
|
|
391
|
+
],
|
|
392
|
+
[
|
|
393
|
+
/(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,
|
|
394
|
+
'$1us',
|
|
395
|
+
],
|
|
396
|
+
[/(test)(?:is|es)$/i, '$1is'],
|
|
397
|
+
[/(movie|twelve|abuse|e[mn]u)s$/i, '$1'],
|
|
398
|
+
[
|
|
399
|
+
/(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i,
|
|
400
|
+
'$1sis',
|
|
401
|
+
],
|
|
402
|
+
[
|
|
403
|
+
/(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i,
|
|
404
|
+
'$1',
|
|
405
|
+
],
|
|
406
|
+
[/(seraph|cherub)im$/i, '$1'],
|
|
407
|
+
[/\b((?:tit)?m|l)ice$/i, '$1ouse'],
|
|
408
|
+
[/\b(mon|smil)ies$/i, '$1ey'],
|
|
409
|
+
[
|
|
410
|
+
/\b([pl]|zomb|(?:neck|cross)?t|coll|faer|food|gen|goon|group|lass|talk|goal|cut)ies$/i,
|
|
411
|
+
'$1ie',
|
|
412
|
+
],
|
|
413
|
+
[/ies$/i, 'y'],
|
|
414
|
+
[/(ar|(?:wo|[ae])l|[eo][ao])ves$/i, '$1f'],
|
|
415
|
+
[
|
|
416
|
+
/(wi|kni|(?:after|half|high|low|mid|non|night|[^\w]|^)li)ves$/i,
|
|
417
|
+
'$1fe',
|
|
418
|
+
],
|
|
419
|
+
[/(ss)$/i, '$1'],
|
|
420
|
+
[/(quiz)zes$/i, '$1'],
|
|
421
|
+
[/(matr)ices$/i, '$1ix'],
|
|
422
|
+
[/(vert|ind)ices$/i, '$1ex'],
|
|
423
|
+
[/^(ox)en$/i, '$1'],
|
|
424
|
+
[/(alias|status)es$/i, '$1'],
|
|
425
|
+
[/(octop|vir)i$/i, '$1us'],
|
|
426
|
+
[/(cris|ax|test)es$/i, '$1is'],
|
|
427
|
+
[/(shoe)s$/i, '$1'],
|
|
428
|
+
[/(o)es$/i, '$1'],
|
|
429
|
+
[/(bus)es$/i, '$1'],
|
|
430
|
+
[/ices$/i, 'ex'],
|
|
431
|
+
[/(hive)s$/i, '$1'],
|
|
432
|
+
[/(tive)s$/i, '$1'],
|
|
433
|
+
[/([^f])ves$/i, '$1fe'],
|
|
434
|
+
[/([lr])ves$/i, '$1f'],
|
|
435
|
+
[/(^analy)ses$/i, '$1sis'],
|
|
436
|
+
[/([ti])a$/i, '$1um'],
|
|
437
|
+
[/(n)ews$/i, '$1ews'],
|
|
438
|
+
// <-- put generic catch-all last
|
|
439
|
+
[/s$/i, ''],
|
|
440
|
+
]);
|
|
@@ -165,6 +165,8 @@ export function extractURLs(str) {
|
|
|
165
165
|
/**
|
|
166
166
|
* * Returns a grammatically correct unit string, optionally prefixed with the number.
|
|
167
167
|
*
|
|
168
|
+
* @remarks For complex and versatile pluralization, please refer to {@link https://nhb-toolbox.vercel.app/docs/utilities/string/pluralizer pluralizer} or {@link https://nhb-toolbox.vercel.app/docs/classes/Pluralizer Pluralizer Class} instead.
|
|
169
|
+
*
|
|
168
170
|
* @param count The numeric value to determine singular or plural.
|
|
169
171
|
* @param unit The unit name (e.g., "day", "hour").
|
|
170
172
|
* @param withNumber Whether to prefix the count before the unit. Defaults to `true`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nhb-toolbox",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.3",
|
|
4
4
|
"description": "A versatile collection of smart, efficient, and reusable utility functions and classes for everyday development needs.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|