@@ -52,7 +52,7 @@ public abstract class Utils {
52
52
protected final static Deque <WordEnding > plurals = new LinkedList <>();
53
53
54
54
static {
55
- plurals .add (new WordEnding ("axe" , "axes" ));
55
+ plurals .add (new WordEnding ("axe" , "axes" )); // not complete since we have battleaxe, etc.
56
56
plurals .add (new WordEnding ("x" , "xes" ));
57
57
58
58
plurals .add (new WordEnding ("ay" , "ays" ));
@@ -61,14 +61,20 @@ public abstract class Utils {
61
61
plurals .add (new WordEnding ("oy" , "oys" ));
62
62
plurals .add (new WordEnding ("uy" , "uys" ));
63
63
plurals .add (new WordEnding ("kie" , "kies" ));
64
- plurals .add (new WordEnding ("zombie" , "zombies" ));
64
+ plurals .add (new WordEnding ("zombie" , "zombies" , true ));
65
65
plurals .add (new WordEnding ("y" , "ies" ));
66
66
67
- plurals .add (new WordEnding ("wife" , "wives" )); // we have to do the -ife -> ives first
67
+ plurals .add (new WordEnding ("wife" , "wives" , true )); // we have to do the -ife -> ives first
68
68
plurals .add (new WordEnding ("life" , "lives" ));
69
- plurals .add (new WordEnding ("knife" , "knives" ));
69
+ plurals .add (new WordEnding ("knife" , "knives" , true ));
70
70
plurals .add (new WordEnding ("ive" , "ives" ));
71
- plurals .add (new WordEnding ("elf" , "elves" )); // self shelf elf
71
+
72
+ plurals .add (new WordEnding ("lf" , "lves" )); // self shelf elf wolf half etc.
73
+ plurals .add (new WordEnding ("thief" , "thieves" , true ));
74
+ plurals .add (new WordEnding ("ief" , "iefs" )); // chiefs, fiefs, briefs
75
+
76
+ plurals .add (new WordEnding ("hoof" , "hooves" ));
77
+
72
78
plurals .add (new WordEnding ("fe" , "ves" ));// most -f words' plurals can end in -fs as well as -ves
73
79
74
80
plurals .add (new WordEnding ("h" , "hes" ));
@@ -79,16 +85,18 @@ public abstract class Utils {
79
85
plurals .add (new WordEnding ("api" , "apis" )); // api fix
80
86
plurals .add (new WordEnding ("us" , "i" ));
81
87
82
- plurals .add (new WordEnding ("hoe" , "hoes" ));
83
- plurals .add (new WordEnding ("toe" , "toes" ));
88
+ plurals .add (new WordEnding ("hoe" , "hoes" , true ));
89
+ plurals .add (new WordEnding ("toe" , "toes" , true ));
90
+ plurals .add (new WordEnding ("foe" , "foes" , true ));
91
+ plurals .add (new WordEnding ("woe" , "woes" , true ));
84
92
plurals .add (new WordEnding ("o" , "oes" ));
85
93
86
- plurals .add (new WordEnding ("alias" , "aliases" ));
87
- plurals .add (new WordEnding ("gas" , "gases" ));
94
+ plurals .add (new WordEnding ("alias" , "aliases" , true ));
95
+ plurals .add (new WordEnding ("gas" , "gases" , true ));
88
96
89
- plurals .add (new WordEnding ("child" , "children" ));
97
+ plurals .add (new WordEnding ("child" , "children" )); // grandchild, etc.
90
98
91
- plurals .add (new WordEnding ("sheep" , "sheep" ));
99
+ plurals .add (new WordEnding ("sheep" , "sheep" , true ));
92
100
93
101
// general ending
94
102
plurals .add (new WordEnding ("" , "s" ));
@@ -104,7 +112,7 @@ public static String join(final Object[] objects) {
104
112
b .append (", " );
105
113
b .append (Classes .toString (objects [i ]));
106
114
}
107
- return "" + b .toString ();
115
+ return b .toString ();
108
116
}
109
117
110
118
public static String join (final Iterable <?> objects ) {
@@ -118,7 +126,7 @@ public static String join(final Iterable<?> objects) {
118
126
first = false ;
119
127
b .append (Classes .toString (o ));
120
128
}
121
- return "" + b .toString ();
129
+ return b .toString ();
122
130
}
123
131
124
132
@ SuppressWarnings ("unchecked" )
@@ -134,7 +142,7 @@ public static Pair<String, Integer> getAmount(String s) {
134
142
} else if (s .matches ("an? .+" )) {
135
143
return new Pair <>(s .split (" " , 2 )[1 ], 1 );
136
144
}
137
- return new Pair <>(s , Integer . valueOf (- 1 ) );
145
+ return new Pair <>(s , - 1 );
138
146
}
139
147
140
148
// public final static class AmountResponse {
@@ -267,14 +275,55 @@ public static File getFile(Plugin plugin) {
267
275
public static NonNullPair <String , Boolean > getEnglishPlural (String word ) {
268
276
assert word != null ;
269
277
if (word .isEmpty ())
270
- return new NonNullPair <>("" , Boolean .FALSE );
278
+ return new NonNullPair <>("" , false );
279
+ if (!couldBeSingular (word )) {
280
+ for (final WordEnding ending : plurals ) {
281
+ if (ending .isCompleteWord ()) {
282
+ // Complete words shouldn't be used as partial pieces
283
+ if (word .length () != ending .plural ().length ())
284
+ continue ;
285
+ }
286
+ if (word .endsWith (ending .plural ()))
287
+ return new NonNullPair <>(
288
+ word .substring (0 , word .length () - ending .plural ().length ()) + ending .singular (),
289
+ true
290
+ );
291
+ if (word .endsWith (ending .plural ().toUpperCase (Locale .ENGLISH )))
292
+ return new NonNullPair <>(
293
+ word .substring (0 , word .length () - ending .plural ().length ())
294
+ + ending .singular ().toUpperCase (Locale .ENGLISH ),
295
+ true
296
+ );
297
+ }
298
+ }
299
+ return new NonNullPair <>(word , false );
300
+ }
301
+
302
+ private static boolean couldBeSingular (String word ) {
271
303
for (final WordEnding ending : plurals ) {
272
- if (word .endsWith (ending .plural ()))
273
- return new NonNullPair <>(word .substring (0 , word .length () - ending .plural ().length ()) + ending .singular (), Boolean .TRUE );
274
- if (word .endsWith (ending .plural ().toUpperCase (Locale .ENGLISH )))
275
- return new NonNullPair <>(word .substring (0 , word .length () - ending .plural ().length ()) + ending .singular ().toUpperCase (Locale .ENGLISH ), Boolean .TRUE );
304
+ if (ending .singular ().isBlank ())
305
+ continue ;
306
+ if (ending .isCompleteWord () && ending .singular ().length () != word .length ())
307
+ continue ; // Skip complete words
308
+
309
+ if (word .endsWith (ending .singular ()) || word .toLowerCase ().endsWith (ending .singular ())) {
310
+ return true ;
311
+ }
276
312
}
277
- return new NonNullPair <>(word , Boolean .FALSE );
313
+ return false ;
314
+ }
315
+
316
+ /**
317
+ * Adds a singular/plural word override for the given words.
318
+ * This is inserted first in the list of words to be checked: it will always be matched
319
+ * and will override all other plurality rules.
320
+ * This will only match the word <s>exactly</s>, and will not apply to derivations of the word.
321
+ *
322
+ * @param singular The singular form of the word
323
+ * @param plural The plural form of the word
324
+ */
325
+ public static void addPluralOverride (String singular , String plural ) {
326
+ Utils .plurals .addFirst (new WordEnding (singular , plural , true ));
278
327
}
279
328
280
329
/**
@@ -286,6 +335,11 @@ public static NonNullPair<String, Boolean> getEnglishPlural(String word) {
286
335
public static String toEnglishPlural (String word ) {
287
336
assert word != null && word .length () != 0 ;
288
337
for (WordEnding ending : plurals ) {
338
+ if (ending .isCompleteWord ()) {
339
+ // Complete words shouldn't be used as partial pieces
340
+ if (word .length () != ending .singular ().length ())
341
+ continue ;
342
+ }
289
343
if (word .endsWith (ending .singular ()))
290
344
return word .substring (0 , word .length () - ending .singular ().length ()) + ending .plural ();
291
345
}
@@ -791,13 +845,10 @@ public static boolean isInteger(Number... numbers) {
791
845
return true ;
792
846
}
793
847
794
- protected static class WordEnding { // To be a record in 2.10
795
-
796
- private final String singular , plural ;
848
+ protected record WordEnding (String singular , String plural , boolean isCompleteWord ) {
797
849
798
- private WordEnding (String singular , String plural ) {
799
- this .singular = singular ;
800
- this .plural = plural ;
850
+ public WordEnding (String singular , String plural ) {
851
+ this (singular , plural , false );
801
852
}
802
853
803
854
public String singular () {
@@ -811,8 +862,7 @@ public String plural() {
811
862
@ Override
812
863
public boolean equals (Object object ) {
813
864
if (this == object ) return true ;
814
- if (!(object instanceof WordEnding )) return false ;
815
- WordEnding ending = (WordEnding ) object ;
865
+ if (!(object instanceof WordEnding ending )) return false ;
816
866
return Objects .equals (singular , ending .singular ) && Objects .equals (plural , ending .plural );
817
867
}
818
868
0 commit comments