chengjie 3 kuukautta sitten
vanhempi
commit
e45db53d9e
2 muutettua tiedostoa jossa 247 lisäystä ja 186 poistoa
  1. 36 15
      src/test/build.test32.js
  2. 211 171
      src/util/stringClass.js

+ 36 - 15
src/test/build.test32.js

@@ -2,21 +2,42 @@ import commonModel from '../model/commonModel.js';
2
 import fs from 'fs';
2
 import fs from 'fs';
3
 import { stringUtils } from '../util/stringClass.js';
3
 import { stringUtils } from '../util/stringClass.js';
4
 
4
 
5
-function runScript(a,b,fuhao){
6
-    if (fuhao=="+")
7
-        return a+b;
8
-    if (fuhao=="-")
9
-        return a-b;
10
-    if (fuhao=="×")
11
-        return a*b;
12
-    if (fuhao=="÷")
13
-        return a/b;
5
+function runScript(){
6
+    check("went");
7
+    check("better");
8
+    check("children");
9
+    check("cannot");
10
+    check("walked");
11
+    check("planned");
12
+    check("used");
13
+    check("studied");
14
+    check("walking");
15
+    check("running");
16
+    check("faster");
17
+    check("fastest");
18
+    check("happier");
19
+    check("happiest");
20
+    check("quickly");
21
+    check("happily");
22
+    check("books");
23
+    check("boxes");
24
+    check("heroes");
25
+    check("cities");
26
+    check("knives");
27
+    check("mice");
28
+    check("this");
29
+    check("WALKED");
30
+    check("can't");
31
+    check("quickly");
32
+    check("run");
33
+    check("big");
34
+    check("computer");
35
+    check("beautiful");
36
+}
14
 
37
 
15
-    if (fuhao=="²")
16
-        return a*a;
17
-    
38
+function check(str){
39
+    let arr1=stringUtils.getWordAllForms(str);
40
+    console.log(str+" 变形有:"+arr1.join(",")+";\n");
18
 }
41
 }
19
 
42
 
20
-let a=123,b=456;
21
-let result=runScript(a,b,"²");
22
-console.log(a+"÷"+b+"="+result);
43
+runScript();

+ 211 - 171
src/util/stringClass.js

@@ -1019,9 +1019,6 @@ export const stringUtils = {
1019
         // 保留原始单词,包括大小写和标点符号
1019
         // 保留原始单词,包括大小写和标点符号
1020
         const originalWord = word;
1020
         const originalWord = word;
1021
         
1021
         
1022
-        // 处理缩写词中的撇号
1023
-        const hasApostrophe = word.includes("'");
1024
-        
1025
         // 转换为小写进行处理
1022
         // 转换为小写进行处理
1026
         const lowerWord = word.toLowerCase();
1023
         const lowerWord = word.toLowerCase();
1027
         
1024
         
@@ -1033,47 +1030,41 @@ export const stringUtils = {
1033
             allForms.add(originalWord);
1030
             allForms.add(originalWord);
1034
         }
1031
         }
1035
         
1032
         
1036
-        // 可以考虑添加一个标志来避免重复检查
1037
-        let foundIrregular = false;
1038
         // 检查是否是不规则动词
1033
         // 检查是否是不规则动词
1039
         for (const [base, forms] of Object.entries(stringUtils.irregularVerbs)) {
1034
         for (const [base, forms] of Object.entries(stringUtils.irregularVerbs)) {
1040
             if (base === lowerWord || forms.includes(lowerWord)) {
1035
             if (base === lowerWord || forms.includes(lowerWord)) {
1041
                 // 添加原形和所有变形
1036
                 // 添加原形和所有变形
1042
                 allForms.add(base);
1037
                 allForms.add(base);
1043
                 forms.forEach(form => allForms.add(form));
1038
                 forms.forEach(form => allForms.add(form));
1044
-                foundIrregular = true;
1045
-                break;
1039
+                // 不规则动词处理完成后继续处理其他类型
1046
             }
1040
             }
1047
         }
1041
         }
1048
         
1042
         
1049
-        // 不规则形容词/副词检查后应该也考虑是否返回
1043
+        // 不规则形容词/副词检查
1050
         for (const [base, forms] of Object.entries(stringUtils.irregularAdjectives)) {
1044
         for (const [base, forms] of Object.entries(stringUtils.irregularAdjectives)) {
1051
             if (base === lowerWord || forms.includes(lowerWord)) {
1045
             if (base === lowerWord || forms.includes(lowerWord)) {
1052
                 // 添加原形和所有变形
1046
                 // 添加原形和所有变形
1053
                 allForms.add(base);
1047
                 allForms.add(base);
1054
                 forms.forEach(form => allForms.add(form));
1048
                 forms.forEach(form => allForms.add(form));
1055
-                foundIrregular = true;
1056
-                break;
1049
+                // 不规则形容词处理完成后继续处理其他类型
1057
             }
1050
             }
1058
         }
1051
         }
1059
 
1052
 
1060
-        // 不规则名词检查后也应该设置标志
1053
+        // 不规则名词检查(单数形式)
1061
         if (stringUtils.irregularNouns[lowerWord]) {
1054
         if (stringUtils.irregularNouns[lowerWord]) {
1055
+            allForms.add(lowerWord); // 添加原词
1062
             stringUtils.irregularNouns[lowerWord].forEach(form => allForms.add(form));
1056
             stringUtils.irregularNouns[lowerWord].forEach(form => allForms.add(form));
1063
-            foundIrregular = true;
1064
         }
1057
         }
1065
 
1058
 
1066
-        // 不规则名词复数形式检查后也应该设置标志
1059
+        // 不规则名词检查(复数形式)
1067
         for (const [singular, plurals] of Object.entries(stringUtils.irregularNouns)) {
1060
         for (const [singular, plurals] of Object.entries(stringUtils.irregularNouns)) {
1068
             if (plurals.includes(lowerWord)) {
1061
             if (plurals.includes(lowerWord)) {
1069
                 allForms.add(singular);
1062
                 allForms.add(singular);
1063
+                allForms.add(lowerWord); // 添加原词
1070
                 plurals.forEach(form => allForms.add(form));
1064
                 plurals.forEach(form => allForms.add(form));
1071
-                foundIrregular = true;
1072
-                break;
1073
             }
1065
             }
1074
         }
1066
         }
1075
         
1067
         
1076
-        // 处理规则变形
1077
         // 特殊处理一些常见的副词和特殊单词,避免错误的词干提取和变形
1068
         // 特殊处理一些常见的副词和特殊单词,避免错误的词干提取和变形
1078
         const specialWords = {
1069
         const specialWords = {
1079
             // 情态动词
1070
             // 情态动词
@@ -1083,7 +1074,7 @@ export const stringUtils = {
1083
             'will': ['will', 'would', "won't", "wouldn't"],
1074
             'will': ['will', 'would', "won't", "wouldn't"],
1084
             'must': ['must', 'have to', 'has to', 'had to', "mustn't"],
1075
             'must': ['must', 'have to', 'has to', 'had to', "mustn't"],
1085
             
1076
             
1086
-            // 副词
1077
+            // 特殊副词
1087
             'early': ['early', 'earlier', 'earliest'],
1078
             'early': ['early', 'earlier', 'earliest'],
1088
             'only': ['only'],
1079
             'only': ['only'],
1089
             'likely': ['likely', 'more likely', 'most likely'],
1080
             'likely': ['likely', 'more likely', 'most likely'],
@@ -1115,11 +1106,14 @@ export const stringUtils = {
1115
             'hero': ['hero', 'heroes'],
1106
             'hero': ['hero', 'heroes'],
1116
             'echo': ['echo', 'echoes'],
1107
             'echo': ['echo', 'echoes'],
1117
 
1108
 
1109
+            // 缩写词
1118
             'its': ['its'], // 物主代词
1110
             'its': ['its'], // 物主代词
1119
-            'it\'s': ['it\'s'], // it is 的缩写
1120
-            'I\'m': ['I\'m'], // I am 的缩写
1121
-            'don\'t': ['don\'t'], // do not 的缩写
1122
-            'doesn\'t': ['doesn\'t'], // does not 的缩写
1111
+            'it\'s': ['it\'s', 'it is', 'it has'], // it is 的缩写
1112
+            'I\'m': ['I\'m', 'I am'], // I am 的缩写
1113
+            'don\'t': ['don\'t', 'do not'], // do not 的缩写
1114
+            'doesn\'t': ['doesn\'t', 'does not'], // does not 的缩写
1115
+            'can\'t': ['can\'t', 'cannot'], // can not 的缩写
1116
+            'won\'t': ['won\'t', 'will not'], // will not 的缩写
1123
             'we\'re': ['we are'],
1117
             'we\'re': ['we are'],
1124
             'they\'re': ['they are'],
1118
             'they\'re': ['they are'],
1125
             'you\'re': ['you are'],
1119
             'you\'re': ['you are'],
@@ -1129,14 +1123,25 @@ export const stringUtils = {
1129
             'we\'d': ['we would', 'we had'],
1123
             'we\'d': ['we would', 'we had'],
1130
             'they\'d': ['they would', 'they had'],
1124
             'they\'d': ['they would', 'they had'],
1131
             'couldn\'t': ['could not'],
1125
             'couldn\'t': ['could not'],
1126
+            'shouldn\'t': ['should not'],
1127
+            'wouldn\'t': ['would not'],
1128
+            'mustn\'t': ['must not'],
1129
+            'haven\'t': ['haven\'t', 'have not'],
1130
+            'hasn\'t': ['hasn\'t', 'has not'],
1131
+            'hadn\'t': ['hadn\'t', 'had not'],
1132
+            'isn\'t': ['isn\'t', 'is not'],
1133
+            'aren\'t': ['aren\'t', 'are not'],
1134
+            'wasn\'t': ['wasn\'t', 'was not'],
1135
+            'weren\'t': ['weren\'t', 'were not'],
1132
             'should\'ve': ['should have'],
1136
             'should\'ve': ['should have'],
1133
             'would\'ve': ['would have'],
1137
             'would\'ve': ['would have'],
1134
             'could\'ve': ['could have']
1138
             'could\'ve': ['could have']
1135
         };
1139
         };
1136
         
1140
         
1137
-        // 如果是特殊单词,直接返回预定义的变形
1141
+        // 如果是特殊单词,添加预定义的变形
1138
         if (specialWords[lowerWord]) {
1142
         if (specialWords[lowerWord]) {
1139
             specialWords[lowerWord].forEach(form => allForms.add(form));
1143
             specialWords[lowerWord].forEach(form => allForms.add(form));
1144
+            // 处理完特殊词后返回,避免进一步处理
1140
             return [...allForms];
1145
             return [...allForms];
1141
         }
1146
         }
1142
         
1147
         
@@ -1144,6 +1149,7 @@ export const stringUtils = {
1144
         for (const [base, forms] of Object.entries(specialWords)) {
1149
         for (const [base, forms] of Object.entries(specialWords)) {
1145
             if (forms.includes(lowerWord)) {
1150
             if (forms.includes(lowerWord)) {
1146
                 forms.forEach(form => allForms.add(form));
1151
                 forms.forEach(form => allForms.add(form));
1152
+                // 处理完特殊词后返回,避免进一步处理
1147
                 return [...allForms];
1153
                 return [...allForms];
1148
             }
1154
             }
1149
         }
1155
         }
@@ -1155,156 +1161,176 @@ export const stringUtils = {
1155
         const specialBaseWords = ['this', 'is', 'was', 'has', 'his', 'its', 'us', 'yes', 'thus', 'plus'];
1161
         const specialBaseWords = ['this', 'is', 'was', 'has', 'his', 'its', 'us', 'yes', 'thus', 'plus'];
1156
         if (specialBaseWords.includes(lowerWord)) {
1162
         if (specialBaseWords.includes(lowerWord)) {
1157
             possibleBaseWords.push(lowerWord);
1163
             possibleBaseWords.push(lowerWord);
1164
+            // 对于特殊基础词,只返回原词
1158
             return [...allForms];
1165
             return [...allForms];
1159
         }
1166
         }
1160
         
1167
         
1161
         // 处理规则变形
1168
         // 处理规则变形
1162
         // 处理过去式/过去分词 (-ed)
1169
         // 处理过去式/过去分词 (-ed)
1163
-        if (lowerWord.endsWith('ed')) {
1170
+        if (lowerWord.endsWith('ed') && lowerWord.length > 2) {
1171
+            // 基本形式
1164
             possibleBaseWords.push(lowerWord.slice(0, -2)); // 常规情况 (walked -> walk)
1172
             possibleBaseWords.push(lowerWord.slice(0, -2)); // 常规情况 (walked -> walk)
1165
             
1173
             
1166
-            // 处理双辅音+ed的情况 (stepped -> step, hummed -> hum)
1174
+            // 处理双辅音+ed的情况 (stepped -> step, planned -> plan)
1167
             const doubleConsonantPattern = /([bcdfghjklmnpqrstvwxyz])\1ed$/;
1175
             const doubleConsonantPattern = /([bcdfghjklmnpqrstvwxyz])\1ed$/;
1168
             if (doubleConsonantPattern.test(lowerWord)) {
1176
             if (doubleConsonantPattern.test(lowerWord)) {
1169
-                possibleBaseWords.push(lowerWord.slice(0, -3)); // 如 stepped -> step
1177
+                possibleBaseWords.push(lowerWord.slice(0, -3)); // 如 planned -> plan
1170
             }
1178
             }
1171
             
1179
             
1172
-            // 处理以辅音+e结尾的动词变为ed的情况
1180
+            // 处理以辅音+y结尾变为ied的情况 (studied -> study)
1181
+            if (lowerWord.endsWith('ied') && lowerWord.length > 3) {
1182
+                const beforeY = lowerWord.slice(0, -3);
1183
+                if (beforeY.length > 0 && /[a-z]$/.test(beforeY)) {
1184
+                    possibleBaseWords.push(beforeY + 'y');
1185
+                }
1186
+            }
1187
+            
1188
+            // 处理以e结尾的动词变为ed的情况 (liked -> like)
1173
             if (/[bcdfghjklmnpqrstvwxyz]ed$/.test(lowerWord)) {
1189
             if (/[bcdfghjklmnpqrstvwxyz]ed$/.test(lowerWord)) {
1174
-                possibleBaseWords.push(lowerWord.slice(0, -2) + 'e'); // 如 liked -> like
1190
+                possibleBaseWords.push(lowerWord.slice(0, -1)); // 如 liked -> like
1175
             }
1191
             }
1176
         }
1192
         }
1177
         
1193
         
1178
-        // 处理以e结尾的动词加d的情况
1179
-        if (lowerWord.endsWith('d') && !lowerWord.endsWith('ed')) {
1194
+        // 处理以e结尾的动词加d的情况 (used -> use)
1195
+        if (lowerWord.endsWith('d') && !lowerWord.endsWith('ed') && lowerWord.length > 1) {
1180
             possibleBaseWords.push(lowerWord.slice(0, -1)); // 如 used -> use
1196
             possibleBaseWords.push(lowerWord.slice(0, -1)); // 如 used -> use
1181
         }
1197
         }
1182
         
1198
         
1183
-        // 处理以辅音+y结尾变为ied的情况
1184
-        if (lowerWord.endsWith('ied')) {
1185
-            possibleBaseWords.push(lowerWord.slice(0, -3) + 'y'); // 如 tried -> try
1186
-            // 处理一些不是由y变形而来的ied结尾单词
1187
-            possibleBaseWords.push(lowerWord.slice(0, -1)); // 如 applied -> apply (虽然这个例子不准确,但为了覆盖更多情况)
1188
-        }
1189
-        
1190
         // 处理现在分词 (-ing)
1199
         // 处理现在分词 (-ing)
1191
-        if (lowerWord.endsWith('ing')) {
1200
+        if (lowerWord.endsWith('ing') && lowerWord.length > 3) {
1192
             possibleBaseWords.push(lowerWord.slice(0, -3)); // 常规情况 (walking -> walk)
1201
             possibleBaseWords.push(lowerWord.slice(0, -3)); // 常规情况 (walking -> walk)
1193
-            possibleBaseWords.push(lowerWord.slice(0, -3) + 'e'); // 以e结尾的动词 (making -> make)
1194
             
1202
             
1195
-            // 处理双辅音+ing的情况 (running -> run, swimming -> swim)
1203
+            // 处理双辅音+ing的情况 (running -> run)
1196
             const doubleConsonantPattern = /([bcdfghjklmnpqrstvwxyz])\1ing$/;
1204
             const doubleConsonantPattern = /([bcdfghjklmnpqrstvwxyz])\1ing$/;
1197
             if (doubleConsonantPattern.test(lowerWord)) {
1205
             if (doubleConsonantPattern.test(lowerWord)) {
1198
                 possibleBaseWords.push(lowerWord.slice(0, -4)); // 如 running -> run
1206
                 possibleBaseWords.push(lowerWord.slice(0, -4)); // 如 running -> run
1199
             }
1207
             }
1200
             
1208
             
1201
-            // 处理特殊的ing形式
1202
-            if (lowerWord.endsWith('ying')) {
1209
+            // 处理特殊的ing形式 (lying -> lie)
1210
+            if (lowerWord.endsWith('ying') && lowerWord.length > 4) {
1203
                 possibleBaseWords.push(lowerWord.slice(0, -4) + 'ie'); // 如 lying -> lie
1211
                 possibleBaseWords.push(lowerWord.slice(0, -4) + 'ie'); // 如 lying -> lie
1204
             }
1212
             }
1213
+            
1214
+            // 处理以辅音+e结尾的动词变为ing的情况 (like -> liking)
1215
+            if (/[bcdfghjklmnpqrstvwxyz]ing$/.test(lowerWord)) {
1216
+                possibleBaseWords.push(lowerWord.slice(0, -3) + 'e');
1217
+            }
1205
         }
1218
         }
1206
         
1219
         
1207
-        // 处理比较级/最高级
1208
-        if (lowerWord.endsWith('er')) {
1220
+        // 处理比较级 (-er)
1221
+        if (lowerWord.endsWith('er') && lowerWord.length > 2) {
1209
             possibleBaseWords.push(lowerWord.slice(0, -2)); // 常规情况 (faster -> fast)
1222
             possibleBaseWords.push(lowerWord.slice(0, -2)); // 常规情况 (faster -> fast)
1210
             
1223
             
1211
-            // 处理双辅音+er的情况
1224
+            // 处理双辅音+er的情况 (bigger -> big)
1212
             const doubleConsonantPattern = /([bcdfghjklmnpqrstvwxyz])\1er$/;
1225
             const doubleConsonantPattern = /([bcdfghjklmnpqrstvwxyz])\1er$/;
1213
             if (doubleConsonantPattern.test(lowerWord)) {
1226
             if (doubleConsonantPattern.test(lowerWord)) {
1214
                 possibleBaseWords.push(lowerWord.slice(0, -3)); // 如 bigger -> big
1227
                 possibleBaseWords.push(lowerWord.slice(0, -3)); // 如 bigger -> big
1215
             }
1228
             }
1216
             
1229
             
1217
-            // 处理以e结尾+r的情况
1230
+            // 处理以辅音+y结尾变为ier的情况 (happier -> happy)
1231
+            if (lowerWord.endsWith('ier') && lowerWord.length > 3) {
1232
+                const beforeY = lowerWord.slice(0, -3);
1233
+                if (beforeY.length > 0 && /[a-z]$/.test(beforeY)) {
1234
+                    possibleBaseWords.push(beforeY + 'y');
1235
+                }
1236
+            }
1237
+            
1238
+            // 处理以e结尾+r的情况 (nicer -> nice)
1218
             if (/[^aeiou]er$/.test(lowerWord)) {
1239
             if (/[^aeiou]er$/.test(lowerWord)) {
1219
-                possibleBaseWords.push(lowerWord.slice(0, -2) + 'e'); // 如 larger -> large
1240
+                possibleBaseWords.push(lowerWord.slice(0, -2) + 'e'); // 如 nicer -> nice
1220
             }
1241
             }
1221
         }
1242
         }
1222
-        if (lowerWord.endsWith('est')) {
1243
+        
1244
+        // 处理最高级 (-est)
1245
+        if (lowerWord.endsWith('est') && lowerWord.length > 3) {
1223
             possibleBaseWords.push(lowerWord.slice(0, -3)); // 常规情况 (fastest -> fast)
1246
             possibleBaseWords.push(lowerWord.slice(0, -3)); // 常规情况 (fastest -> fast)
1224
             
1247
             
1225
-            // 处理双辅音+est的情况
1248
+            // 处理双辅音+est的情况 (biggest -> big)
1226
             const doubleConsonantPattern = /([bcdfghjklmnpqrstvwxyz])\1est$/;
1249
             const doubleConsonantPattern = /([bcdfghjklmnpqrstvwxyz])\1est$/;
1227
             if (doubleConsonantPattern.test(lowerWord)) {
1250
             if (doubleConsonantPattern.test(lowerWord)) {
1228
                 possibleBaseWords.push(lowerWord.slice(0, -4)); // 如 biggest -> big
1251
                 possibleBaseWords.push(lowerWord.slice(0, -4)); // 如 biggest -> big
1229
             }
1252
             }
1230
             
1253
             
1231
-            // 处理以e结尾+st的情况
1254
+            // 处理以辅音+y结尾变为iest的情况 (happiest -> happy)
1255
+            if (lowerWord.endsWith('iest') && lowerWord.length > 4) {
1256
+                const beforeY = lowerWord.slice(0, -4);
1257
+                if (beforeY.length > 0 && /[a-z]$/.test(beforeY)) {
1258
+                    possibleBaseWords.push(beforeY + 'y');
1259
+                }
1260
+            }
1261
+            
1262
+            // 处理以e结尾+st的情况 (nicest -> nice)
1232
             if (/[^aeiou]est$/.test(lowerWord)) {
1263
             if (/[^aeiou]est$/.test(lowerWord)) {
1233
-                possibleBaseWords.push(lowerWord.slice(0, -3) + 'e'); // 如 largest -> large
1264
+                possibleBaseWords.push(lowerWord.slice(0, -3) + 'e'); // 如 nicest -> nice
1234
             }
1265
             }
1235
         }
1266
         }
1236
         
1267
         
1237
-        // 处理以辅音+y结尾变为ier/iest的情况
1238
-        if (lowerWord.endsWith('ier')) {
1239
-            possibleBaseWords.push(lowerWord.slice(0, -3) + 'y'); // 如 happier -> happy
1240
-        }
1241
-        if (lowerWord.endsWith('iest')) {
1242
-            possibleBaseWords.push(lowerWord.slice(0, -4) + 'y'); // 如 happiest -> happy
1243
-        }
1244
-        
1245
         // 处理副词 (-ly)
1268
         // 处理副词 (-ly)
1246
-        if (lowerWord.endsWith('ly')) {
1269
+        if (lowerWord.endsWith('ly') && lowerWord.length > 2) {
1247
             possibleBaseWords.push(lowerWord.slice(0, -2)); // 常规情况 (quickly -> quick)
1270
             possibleBaseWords.push(lowerWord.slice(0, -2)); // 常规情况 (quickly -> quick)
1248
-        }
1249
-        
1250
-        // 处理以y结尾变为ily的情况
1251
-        if (lowerWord.endsWith('ily')) {
1252
-            possibleBaseWords.push(lowerWord.slice(0, -3) + 'y'); // 如 happily -> happy
1271
+            
1272
+            // 处理以辅音+y结尾变为ily的情况 (happily -> happy)
1273
+            if (lowerWord.endsWith('ily') && lowerWord.length > 3) {
1274
+                const beforeY = lowerWord.slice(0, -3);
1275
+                if (beforeY.length > 0 && /[a-z]$/.test(beforeY)) {
1276
+                    possibleBaseWords.push(beforeY + 'y');
1277
+                }
1278
+            }
1253
         }
1279
         }
1254
 
1280
 
1255
-        // 处理复数形式
1256
-        if (lowerWord.endsWith('s') && !lowerWord.endsWith('ss')) {
1257
-            possibleBaseWords.push(lowerWord.slice(0, -1)); // 常规情况 (books -> book)
1258
-        }
1259
-        if (lowerWord.endsWith('es')) {
1260
-            possibleBaseWords.push(lowerWord.slice(0, -2)); // 常规情况 (boxes -> box)
1261
-            
1262
-            // 处理以ch, sh, ss, x, z结尾的名词复数形式
1263
-            if (/(?:ch|sh|ss|x|z)es$/.test(lowerWord)) {
1264
-                possibleBaseWords.push(lowerWord.slice(0, -2)); // 如 boxes -> box, dishes -> dish
1281
+        // 处理复数形式 (-s, -es)
1282
+        if (lowerWord.endsWith('s') && lowerWord.length > 1) {
1283
+            // 基本复数形式 (books -> book)
1284
+            if (!lowerWord.endsWith('ss')) {
1285
+                possibleBaseWords.push(lowerWord.slice(0, -1));
1265
             }
1286
             }
1266
             
1287
             
1267
-            // 处理以o结尾的名词复数形式
1268
-            if (lowerWord.endsWith('oes')) {
1269
-                possibleBaseWords.push(lowerWord.slice(0, -2)); // 如 heroes -> hero
1270
-                possibleBaseWords.push(lowerWord.slice(0, -1)); // 如 potatoes -> potato
1288
+            // 处理 -es 结尾 (boxes -> box)
1289
+            if (lowerWord.endsWith('es') && lowerWord.length > 2) {
1290
+                possibleBaseWords.push(lowerWord.slice(0, -2)); // 常规情况
1291
+                
1292
+                // 处理以ch, sh, ss, x, z结尾的名词复数形式 (boxes -> box)
1293
+                if (/(?:ch|sh|ss|x|z)es$/.test(lowerWord)) {
1294
+                    possibleBaseWords.push(lowerWord.slice(0, -2)); // 如 boxes -> box
1295
+                }
1296
+                
1297
+                // 处理以辅音+o结尾的名词复数形式 (heroes -> hero)
1298
+                if (lowerWord.endsWith('oes') && lowerWord.length > 3) {
1299
+                    possibleBaseWords.push(lowerWord.slice(0, -2)); // 如 heroes -> hero
1300
+                }
1301
+                
1302
+                // 处理以y结尾变为ies的情况 (cities -> city)
1303
+                if (lowerWord.endsWith('ies') && lowerWord.length > 3) {
1304
+                    const beforeY = lowerWord.slice(0, -3);
1305
+                    if (beforeY.length > 0 && /[a-z]$/.test(beforeY)) {
1306
+                        possibleBaseWords.push(beforeY + 'y');
1307
+                    }
1308
+                }
1271
             }
1309
             }
1272
         }
1310
         }
1273
         
1311
         
1274
-        // 处理以y结尾变为ies的情况
1275
-        if (lowerWord.endsWith('ies')) {
1276
-            possibleBaseWords.push(lowerWord.slice(0, -3) + 'y'); // 如 cities -> city
1277
-        }
1278
-        
1279
-        // 处理以fe结尾变为ves的情况
1280
-        if (lowerWord.endsWith('ves')) {
1312
+        // 处理以fe结尾变为ves的情况 (knives -> knife)
1313
+        if (lowerWord.endsWith('ves') && lowerWord.length > 4) {
1281
             possibleBaseWords.push(lowerWord.slice(0, -3) + 'fe'); // 如 knives -> knife
1314
             possibleBaseWords.push(lowerWord.slice(0, -3) + 'fe'); // 如 knives -> knife
1282
-            possibleBaseWords.push(lowerWord.slice(0, -3) + 'f'); // 如 leaves -> leaf
1315
+            possibleBaseWords.push(lowerWord.slice(0, -3) + 'f');  // 如 wives -> wife
1283
         }
1316
         }
1284
         
1317
         
1285
         // 处理特殊的复数形式
1318
         // 处理特殊的复数形式
1286
-        if (lowerWord.endsWith('men')) {
1319
+        if (lowerWord.endsWith('men') && lowerWord.length > 3) {
1287
             possibleBaseWords.push(lowerWord.slice(0, -3) + 'man'); // 如 women -> woman, men -> man
1320
             possibleBaseWords.push(lowerWord.slice(0, -3) + 'man'); // 如 women -> woman, men -> man
1288
         }
1321
         }
1289
-        if (lowerWord.endsWith('ice')) {
1322
+        if (lowerWord.endsWith('ice') && lowerWord.length > 3) {
1290
             possibleBaseWords.push(lowerWord.slice(0, -3) + 'ouse'); // 如 mice -> mouse
1323
             possibleBaseWords.push(lowerWord.slice(0, -3) + 'ouse'); // 如 mice -> mouse
1291
         }
1324
         }
1292
-        if (lowerWord.endsWith('eet')) {
1325
+        if (lowerWord.endsWith('eet') && lowerWord.length > 3) {
1293
             possibleBaseWords.push(lowerWord.slice(0, -3) + 'oot'); // 如 feet -> foot
1326
             possibleBaseWords.push(lowerWord.slice(0, -3) + 'oot'); // 如 feet -> foot
1294
         }
1327
         }
1295
-        if (lowerWord.endsWith('ildren')) {
1328
+        if (lowerWord.endsWith('ildren') && lowerWord.length > 6) {
1296
             possibleBaseWords.push(lowerWord.slice(0, -6) + 'ild'); // 如 children -> child
1329
             possibleBaseWords.push(lowerWord.slice(0, -6) + 'ild'); // 如 children -> child
1297
         }
1330
         }
1298
         
1331
         
1299
         // 去重并过滤掉过短的单词
1332
         // 去重并过滤掉过短的单词
1300
-        const uniqueBaseWords = [...new Set(possibleBaseWords)].filter(w => w.length >= 2);
1301
-        
1302
-        // 如果没有找到任何可能的原形,或者所有可能的原形都不是有效单词,则返回单词本身
1303
-        if (uniqueBaseWords.length === 0 || !uniqueBaseWords.some(w => w === lowerWord || w.length >= 3)) {
1304
-            // 清空可能不正确的原形
1305
-            uniqueBaseWords.length = 0;
1306
-            uniqueBaseWords.push(lowerWord);
1307
-        }
1333
+        const uniqueBaseWords = [...new Set(possibleBaseWords)].filter(w => w.length >= 1);
1308
         
1334
         
1309
         // 添加所有可能的原形到变形集合中
1335
         // 添加所有可能的原形到变形集合中
1310
         uniqueBaseWords.forEach(base => allForms.add(base));
1336
         uniqueBaseWords.forEach(base => allForms.add(base));
@@ -1313,14 +1339,14 @@ export const stringUtils = {
1313
         const adjectiveSuffixes = ['ful', 'ous', 'ive', 'ic', 'al', 'ent', 'ant', 'able', 'ible', 'ary', 'ory', 'ish'];
1339
         const adjectiveSuffixes = ['ful', 'ous', 'ive', 'ic', 'al', 'ent', 'ant', 'able', 'ible', 'ary', 'ory', 'ish'];
1314
         const verbSuffixes = ['ize', 'ise', 'ate', 'ify', 'en'];
1340
         const verbSuffixes = ['ize', 'ise', 'ate', 'ify', 'en'];
1315
         const nounSuffixes = ['tion', 'sion', 'ment', 'ness', 'ity', 'hood', 'ship', 'dom', 'ism', 'ist'];
1341
         const nounSuffixes = ['tion', 'sion', 'ment', 'ness', 'ity', 'hood', 'ship', 'dom', 'ism', 'ist'];
1316
-        const adverbSuffixes = ['ly', 'ward', 'wise'];// 副词后缀
1342
+        const adverbSuffixes = ['ly', 'ward', 'wise']; // 副词后缀
1317
 
1343
 
1318
         
1344
         
1319
         // 一些常见的形容词
1345
         // 一些常见的形容词
1320
         const commonAdjectives = ['good', 'bad', 'big', 'small', 'high', 'low', 'long', 'short', 'old', 'new', 
1346
         const commonAdjectives = ['good', 'bad', 'big', 'small', 'high', 'low', 'long', 'short', 'old', 'new', 
1321
                                  'fast', 'slow', 'hard', 'soft', 'hot', 'cold', 'warm', 'cool', 'rich', 'poor', 
1347
                                  'fast', 'slow', 'hard', 'soft', 'hot', 'cold', 'warm', 'cool', 'rich', 'poor', 
1322
                                  'thick', 'thin', 'wide', 'narrow', 'deep', 'shallow', 'strong', 'weak', 'young', 
1348
                                  'thick', 'thin', 'wide', 'narrow', 'deep', 'shallow', 'strong', 'weak', 'young', 
1323
-                                 'old', 'bright', 'dark', 'light', 'heavy', 'easy', 'hard', 'clean', 'dirty', 
1349
+                                 'bright', 'dark', 'light', 'heavy', 'easy', 'clean', 'dirty', 
1324
                                  'full', 'empty', 'dry', 'wet', 'sick', 'healthy', 'loud', 'quiet', 'sweet', 
1350
                                  'full', 'empty', 'dry', 'wet', 'sick', 'healthy', 'loud', 'quiet', 'sweet', 
1325
                                  'sour', 'bitter', 'nice', 'mean', 'kind', 'cruel', 'brave', 'afraid', 'happy', 
1351
                                  'sour', 'bitter', 'nice', 'mean', 'kind', 'cruel', 'brave', 'afraid', 'happy', 
1326
                                  'sad', 'angry', 'calm', 'busy', 'free', 'cheap', 'expensive', 'safe', 'dangerous'];
1352
                                  'sad', 'angry', 'calm', 'busy', 'free', 'cheap', 'expensive', 'safe', 'dangerous'];
@@ -1349,50 +1375,58 @@ export const stringUtils = {
1349
                 
1375
                 
1350
         // 动词变形 (如果可能是动词)
1376
         // 动词变形 (如果可能是动词)
1351
         if (isLikelyVerb && lowerWord.length >= 2) {
1377
         if (isLikelyVerb && lowerWord.length >= 2) {
1352
-            // 第三人称单数
1353
-            if (lowerWord.endsWith('s') || lowerWord.endsWith('x') || lowerWord.endsWith('ch') || lowerWord.endsWith('sh') || lowerWord.endsWith('z')) {
1354
-                allForms.add(lowerWord + 'es');
1355
-            } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1356
-                allForms.add(lowerWord.slice(0, -1) + 'ies');
1357
-            } else {
1358
-                allForms.add(lowerWord + 's');
1378
+            // 第三人称单数 (walk -> walks)
1379
+            if (!lowerWord.endsWith('s')) {
1380
+                if (lowerWord.endsWith('s') || lowerWord.endsWith('x') || lowerWord.endsWith('ch') || lowerWord.endsWith('sh') || lowerWord.endsWith('z')) {
1381
+                    allForms.add(lowerWord + 'es');
1382
+                } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1383
+                    allForms.add(lowerWord.slice(0, -1) + 'ies');
1384
+                } else {
1385
+                    allForms.add(lowerWord + 's');
1386
+                }
1359
             }
1387
             }
1360
             
1388
             
1361
             // 过去式和过去分词 (-ed)
1389
             // 过去式和过去分词 (-ed)
1362
-            if (lowerWord.endsWith('e')) {
1363
-                allForms.add(lowerWord + 'd');
1364
-            } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1365
-                allForms.add(lowerWord.slice(0, -1) + 'ied');
1366
-            } else if (lowerWord.length > 2 && 
1367
-                      !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 1)) && 
1368
-                      ['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2)) && 
1369
-                      !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 3))) {
1370
-                // 双写末尾辅音字母的情况,如 stop -> stopped
1371
-                allForms.add(lowerWord + lowerWord.charAt(lowerWord.length - 1) + 'ed');
1372
-            } else {
1373
-                allForms.add(lowerWord + 'ed');
1390
+            // 不对已经以-ed结尾的词添加-ed
1391
+            if (!lowerWord.endsWith('ed')) {
1392
+                if (lowerWord.endsWith('e')) {
1393
+                    allForms.add(lowerWord + 'd');
1394
+                } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1395
+                    allForms.add(lowerWord.slice(0, -1) + 'ied');
1396
+                } else if (lowerWord.length > 2 && 
1397
+                          !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 1)) && 
1398
+                          ['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2)) && 
1399
+                          !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 3))) {
1400
+                    // 双写末尾辅音字母的情况,如 stop -> stopped
1401
+                    allForms.add(lowerWord + lowerWord.charAt(lowerWord.length - 1) + 'ed');
1402
+                } else {
1403
+                    allForms.add(lowerWord + 'ed');
1404
+                }
1374
             }
1405
             }
1375
             
1406
             
1376
             // 现在分词 (-ing)
1407
             // 现在分词 (-ing)
1377
-            if (lowerWord.endsWith('ie')) {
1378
-                allForms.add(lowerWord.slice(0, -2) + 'ying');
1379
-            } else if (lowerWord.endsWith('e') && lowerWord.length > 2) {
1380
-                allForms.add(lowerWord.slice(0, -1) + 'ing');
1381
-            } else if (lowerWord.length > 2 && 
1382
-                      !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 1)) && 
1383
-                      ['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2)) && 
1384
-                      !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 3))) {
1385
-                // 双写末尾辅音字母的情况,如 run -> running
1386
-                allForms.add(lowerWord + lowerWord.charAt(lowerWord.length - 1) + 'ing');
1387
-            } else {
1388
-                allForms.add(lowerWord + 'ing');
1408
+            // 不对已经以-ing结尾的词添加-ing
1409
+            if (!lowerWord.endsWith('ing')) {
1410
+                if (lowerWord.endsWith('ie')) {
1411
+                    allForms.add(lowerWord.slice(0, -2) + 'ying');
1412
+                } else if (lowerWord.endsWith('e') && lowerWord.length > 2) {
1413
+                    allForms.add(lowerWord.slice(0, -1) + 'ing');
1414
+                } else if (lowerWord.length > 2 && 
1415
+                          !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 1)) && 
1416
+                          ['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2)) && 
1417
+                          !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 3))) {
1418
+                    // 双写末尾辅音字母的情况,如 run -> running
1419
+                    allForms.add(lowerWord + lowerWord.charAt(lowerWord.length - 1) + 'ing');
1420
+                } else {
1421
+                    allForms.add(lowerWord + 'ing');
1422
+                }
1389
             }
1423
             }
1390
         }
1424
         }
1391
         
1425
         
1392
         // 形容词和副词变形 (如果可能是形容词或副词)
1426
         // 形容词和副词变形 (如果可能是形容词或副词)
1393
-        if ((isLikelyAdjective || isLikelyAdverb) && lowerWord.length >= 3 && 
1427
+        if ((isLikelyAdjective || isLikelyAdverb) && lowerWord.length >= 2 && 
1394
             !lowerWord.endsWith('ing') && !lowerWord.endsWith('ed') && 
1428
             !lowerWord.endsWith('ing') && !lowerWord.endsWith('ed') && 
1395
-            lowerWord.length <= 8) { // 限制长度,避免生成不必要的变形
1429
+            lowerWord.length <= 12) { // 限制长度,避免生成不必要的变形
1396
             
1430
             
1397
             // 检查是否是多音节形容词,这些通常使用 more/most 而不是 -er/-est
1431
             // 检查是否是多音节形容词,这些通常使用 more/most 而不是 -er/-est
1398
             const isMultisyllabic = lowerWord.length > 7 || 
1432
             const isMultisyllabic = lowerWord.length > 7 || 
@@ -1409,33 +1443,37 @@ export const stringUtils = {
1409
             // 只为短形容词生成比较级和最高级
1443
             // 只为短形容词生成比较级和最高级
1410
             if (!isMultisyllabic) {
1444
             if (!isMultisyllabic) {
1411
                 // 比较级 (-er)
1445
                 // 比较级 (-er)
1412
-                if (lowerWord.endsWith('e')) {
1413
-                    allForms.add(lowerWord + 'r');
1414
-                } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1415
-                    allForms.add(lowerWord.slice(0, -1) + 'ier');
1416
-                } else if (lowerWord.length > 2 && 
1417
-                          !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 1)) && 
1418
-                          ['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2)) && 
1419
-                          !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 3))) {
1420
-                    // 双写末尾辅音字母的情况,如 big -> bigger
1421
-                    allForms.add(lowerWord + lowerWord.charAt(lowerWord.length - 1) + 'er');
1422
-                } else {
1423
-                    allForms.add(lowerWord + 'er');
1446
+                if (!lowerWord.endsWith('er')) {
1447
+                    if (lowerWord.endsWith('e')) {
1448
+                        allForms.add(lowerWord + 'r');
1449
+                    } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1450
+                        allForms.add(lowerWord.slice(0, -1) + 'ier');
1451
+                    } else if (lowerWord.length > 2 && 
1452
+                              !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 1)) && 
1453
+                              ['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2)) && 
1454
+                              !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 3))) {
1455
+                        // 双写末尾辅音字母的情况,如 big -> bigger
1456
+                        allForms.add(lowerWord + lowerWord.charAt(lowerWord.length - 1) + 'er');
1457
+                    } else {
1458
+                        allForms.add(lowerWord + 'er');
1459
+                    }
1424
                 }
1460
                 }
1425
                 
1461
                 
1426
                 // 最高级 (-est)
1462
                 // 最高级 (-est)
1427
-                if (lowerWord.endsWith('e')) {
1428
-                    allForms.add(lowerWord + 'st');
1429
-                } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1430
-                    allForms.add(lowerWord.slice(0, -1) + 'iest');
1431
-                } else if (lowerWord.length > 2 && 
1432
-                          !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 1)) && 
1433
-                          ['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2)) && 
1434
-                          !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 3))) {
1435
-                    // 双写末尾辅音字母的情况,如 big -> biggest
1436
-                    allForms.add(lowerWord + lowerWord.charAt(lowerWord.length - 1) + 'est');
1437
-                } else {
1438
-                    allForms.add(lowerWord + 'est');
1463
+                if (!lowerWord.endsWith('est')) {
1464
+                    if (lowerWord.endsWith('e')) {
1465
+                        allForms.add(lowerWord + 'st');
1466
+                    } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1467
+                        allForms.add(lowerWord.slice(0, -1) + 'iest');
1468
+                    } else if (lowerWord.length > 2 && 
1469
+                              !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 1)) && 
1470
+                              ['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2)) && 
1471
+                              !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 3))) {
1472
+                        // 双写末尾辅音字母的情况,如 big -> biggest
1473
+                        allForms.add(lowerWord + lowerWord.charAt(lowerWord.length - 1) + 'est');
1474
+                    } else {
1475
+                        allForms.add(lowerWord + 'est');
1476
+                    }
1439
                 }
1477
                 }
1440
             }
1478
             }
1441
             
1479
             
@@ -1443,8 +1481,8 @@ export const stringUtils = {
1443
             if (!lowerWord.endsWith('ly')) {
1481
             if (!lowerWord.endsWith('ly')) {
1444
                 if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1482
                 if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1445
                     allForms.add(lowerWord.slice(0, -1) + 'ily');
1483
                     allForms.add(lowerWord.slice(0, -1) + 'ily');
1446
-                } else if (lowerWord.endsWith('le')) {
1447
-                    allForms.add(lowerWord.slice(0, -1) + 'y');
1484
+                } else if (lowerWord.endsWith('le') && lowerWord.length > 2) {
1485
+                    allForms.add(lowerWord.slice(0, -2) + 'ly');
1448
                 } else {
1486
                 } else {
1449
                     allForms.add(lowerWord + 'ly');
1487
                     allForms.add(lowerWord + 'ly');
1450
                 }
1488
                 }
@@ -1452,17 +1490,19 @@ export const stringUtils = {
1452
         }
1490
         }
1453
         
1491
         
1454
         // 名词复数形式 (对大多数单词都适用)
1492
         // 名词复数形式 (对大多数单词都适用)
1455
-        if (lowerWord.length >= 2 && !lowerWord.endsWith('ing') && !lowerWord.endsWith('ed')) {
1456
-            if (lowerWord.endsWith('s') || lowerWord.endsWith('x') || lowerWord.endsWith('ch') || lowerWord.endsWith('sh') || lowerWord.endsWith('z')) {
1457
-                allForms.add(lowerWord + 'es');
1458
-            } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1459
-                allForms.add(lowerWord.slice(0, -1) + 'ies');
1460
-            } else if (lowerWord.endsWith('f')) {
1461
-                allForms.add(lowerWord.slice(0, -1) + 'ves');
1462
-            } else if (lowerWord.endsWith('fe')) {
1463
-                allForms.add(lowerWord.slice(0, -2) + 'ves');
1464
-            } else {
1465
-                allForms.add(lowerWord + 's');
1493
+        if (lowerWord.length >= 2 && !lowerWord.endsWith('ing') && !lowerWord.endsWith('ed') && !lowerWord.endsWith('er') && !lowerWord.endsWith('est')) {
1494
+            if (!lowerWord.endsWith('s') && !lowerWord.endsWith('sh') && !lowerWord.endsWith('ch') && !lowerWord.endsWith('x') && !lowerWord.endsWith('z')) {
1495
+                if (lowerWord.endsWith('s') || lowerWord.endsWith('x') || lowerWord.endsWith('ch') || lowerWord.endsWith('sh') || lowerWord.endsWith('z')) {
1496
+                    allForms.add(lowerWord + 'es');
1497
+                } else if (lowerWord.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(lowerWord.charAt(lowerWord.length - 2))) {
1498
+                    allForms.add(lowerWord.slice(0, -1) + 'ies');
1499
+                } else if (lowerWord.endsWith('f')) {
1500
+                    allForms.add(lowerWord.slice(0, -1) + 'ves');
1501
+                } else if (lowerWord.endsWith('fe')) {
1502
+                    allForms.add(lowerWord.slice(0, -2) + 'ves');
1503
+                } else {
1504
+                    allForms.add(lowerWord + 's');
1505
+                }
1466
             }
1506
             }
1467
         }
1507
         }
1468
         
1508
         
@@ -1490,4 +1530,4 @@ export const stringUtils = {
1490
         
1530
         
1491
         return false;
1531
         return false;
1492
     }
1532
     }
1493
-}
1533
+}