PluginProbe
Depicter — Popup & Slider Builder / 1.9.2
Depicter — Popup & Slider Builder v1.9.2
4.8.1 trunk 1.0.0 1.1.0 1.1.2 1.1.4 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.5 1.3.8 1.5.0 1.5.1 1.5.2 1.5.5 1.6.0 1.6.1 1.6.2 1.7.0 All 76 releases
depicter / vendor / symfony / string / Inflector / EnglishInflector.php

EnglishInflector.php in Depicter — Popup & Slider Builder 1.9.2, at vendor/symfony/string/Inflector/EnglishInflector.php

518 lines 15.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <[email protected]>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\String\Inflector;
13
14 final class EnglishInflector implements InflectorInterface
15 {
16 /**
17 * Map English plural to singular suffixes.
18 *
19 * @see http://english-zone.com/spelling/plurals.html
20 */
21 private const PLURAL_MAP = [
22 // First entry: plural suffix, reversed
23 // Second entry: length of plural suffix
24 // Third entry: Whether the suffix may succeed a vocal
25 // Fourth entry: Whether the suffix may succeed a consonant
26 // Fifth entry: singular suffix, normal
27
28 // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
29 ['a', 1, true, true, ['on', 'um']],
30
31 // nebulae (nebula)
32 ['ea', 2, true, true, 'a'],
33
34 // services (service)
35 ['secivres', 8, true, true, 'service'],
36
37 // mice (mouse), lice (louse)
38 ['eci', 3, false, true, 'ouse'],
39
40 // geese (goose)
41 ['esee', 4, false, true, 'oose'],
42
43 // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
44 ['i', 1, true, true, 'us'],
45
46 // men (man), women (woman)
47 ['nem', 3, true, true, 'man'],
48
49 // children (child)
50 ['nerdlihc', 8, true, true, 'child'],
51
52 // oxen (ox)
53 ['nexo', 4, false, false, 'ox'],
54
55 // indices (index), appendices (appendix), prices (price)
56 ['seci', 4, false, true, ['ex', 'ix', 'ice']],
57
58 // codes (code)
59 ['sedoc', 5, false, true, 'code'],
60
61 // selfies (selfie)
62 ['seifles', 7, true, true, 'selfie'],
63
64 // zombies (zombie)
65 ['seibmoz', 7, true, true, 'zombie'],
66
67 // movies (movie)
68 ['seivom', 6, true, true, 'movie'],
69
70 // names (name)
71 ['seman', 5, true, false, 'name'],
72
73 // conspectuses (conspectus), prospectuses (prospectus)
74 ['sesutcep', 8, true, true, 'pectus'],
75
76 // feet (foot)
77 ['teef', 4, true, true, 'foot'],
78
79 // geese (goose)
80 ['eseeg', 5, true, true, 'goose'],
81
82 // teeth (tooth)
83 ['hteet', 5, true, true, 'tooth'],
84
85 // news (news)
86 ['swen', 4, true, true, 'news'],
87
88 // series (series)
89 ['seires', 6, true, true, 'series'],
90
91 // babies (baby)
92 ['sei', 3, false, true, 'y'],
93
94 // accesses (access), addresses (address), kisses (kiss)
95 ['sess', 4, true, false, 'ss'],
96
97 // analyses (analysis), ellipses (ellipsis), fungi (fungus),
98 // neuroses (neurosis), theses (thesis), emphases (emphasis),
99 // oases (oasis), crises (crisis), houses (house), bases (base),
100 // atlases (atlas)
101 ['ses', 3, true, true, ['s', 'se', 'sis']],
102
103 // objectives (objective), alternative (alternatives)
104 ['sevit', 5, true, true, 'tive'],
105
106 // drives (drive)
107 ['sevird', 6, false, true, 'drive'],
108
109 // lives (life), wives (wife)
110 ['sevi', 4, false, true, 'ife'],
111
112 // moves (move)
113 ['sevom', 5, true, true, 'move'],
114
115 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff)
116 ['sev', 3, true, true, ['f', 've', 'ff']],
117
118 // axes (axis), axes (ax), axes (axe)
119 ['sexa', 4, false, false, ['ax', 'axe', 'axis']],
120
121 // indexes (index), matrixes (matrix)
122 ['sex', 3, true, false, 'x'],
123
124 // quizzes (quiz)
125 ['sezz', 4, true, false, 'z'],
126
127 // bureaus (bureau)
128 ['suae', 4, false, true, 'eau'],
129
130 // fees (fee), trees (tree), employees (employee)
131 ['see', 3, true, true, 'ee'],
132
133 // edges (edge)
134 ['segd', 4, true, true, 'dge'],
135
136 // roses (rose), garages (garage), cassettes (cassette),
137 // waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
138 // shoes (shoe)
139 ['se', 2, true, true, ['', 'e']],
140
141 // tags (tag)
142 ['s', 1, true, true, ''],
143
144 // chateaux (chateau)
145 ['xuae', 4, false, true, 'eau'],
146
147 // people (person)
148 ['elpoep', 6, true, true, 'person'],
149 ];
150
151 /**
152 * Map English singular to plural suffixes.
153 *
154 * @see http://english-zone.com/spelling/plurals.html
155 */
156 private const SINGULAR_MAP = [
157 // First entry: singular suffix, reversed
158 // Second entry: length of singular suffix
159 // Third entry: Whether the suffix may succeed a vocal
160 // Fourth entry: Whether the suffix may succeed a consonant
161 // Fifth entry: plural suffix, normal
162
163 // criterion (criteria)
164 ['airetirc', 8, false, false, 'criterion'],
165
166 // nebulae (nebula)
167 ['aluben', 6, false, false, 'nebulae'],
168
169 // children (child)
170 ['dlihc', 5, true, true, 'children'],
171
172 // prices (price)
173 ['eci', 3, false, true, 'ices'],
174
175 // services (service)
176 ['ecivres', 7, true, true, 'services'],
177
178 // lives (life), wives (wife)
179 ['efi', 3, false, true, 'ives'],
180
181 // selfies (selfie)
182 ['eifles', 6, true, true, 'selfies'],
183
184 // movies (movie)
185 ['eivom', 5, true, true, 'movies'],
186
187 // lice (louse)
188 ['esuol', 5, false, true, 'lice'],
189
190 // mice (mouse)
191 ['esuom', 5, false, true, 'mice'],
192
193 // geese (goose)
194 ['esoo', 4, false, true, 'eese'],
195
196 // houses (house), bases (base)
197 ['es', 2, true, true, 'ses'],
198
199 // geese (goose)
200 ['esoog', 5, true, true, 'geese'],
201
202 // caves (cave)
203 ['ev', 2, true, true, 'ves'],
204
205 // drives (drive)
206 ['evird', 5, false, true, 'drives'],
207
208 // objectives (objective), alternative (alternatives)
209 ['evit', 4, true, true, 'tives'],
210
211 // moves (move)
212 ['evom', 4, true, true, 'moves'],
213
214 // staves (staff)
215 ['ffats', 5, true, true, 'staves'],
216
217 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
218 ['ff', 2, true, true, 'ffs'],
219
220 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
221 ['f', 1, true, true, ['fs', 'ves']],
222
223 // arches (arch)
224 ['hc', 2, true, true, 'ches'],
225
226 // bushes (bush)
227 ['hs', 2, true, true, 'shes'],
228
229 // teeth (tooth)
230 ['htoot', 5, true, true, 'teeth'],
231
232 // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
233 ['mu', 2, true, true, 'a'],
234
235 // men (man), women (woman)
236 ['nam', 3, true, true, 'men'],
237
238 // people (person)
239 ['nosrep', 6, true, true, ['persons', 'people']],
240
241 // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
242 ['noi', 3, true, true, 'ions'],
243
244 // coupon (coupons)
245 ['nop', 3, true, true, 'pons'],
246
247 // seasons (season), treasons (treason), poisons (poison), lessons (lesson)
248 ['nos', 3, true, true, 'sons'],
249
250 // bacteria (bacterium), criteria (criterion), phenomena (phenomenon)
251 ['no', 2, true, true, 'a'],
252
253 // echoes (echo)
254 ['ohce', 4, true, true, 'echoes'],
255
256 // heroes (hero)
257 ['oreh', 4, true, true, 'heroes'],
258
259 // atlases (atlas)
260 ['salta', 5, true, true, 'atlases'],
261
262 // irises (iris)
263 ['siri', 4, true, true, 'irises'],
264
265 // analyses (analysis), ellipses (ellipsis), neuroses (neurosis)
266 // theses (thesis), emphases (emphasis), oases (oasis),
267 // crises (crisis)
268 ['sis', 3, true, true, 'ses'],
269
270 // accesses (access), addresses (address), kisses (kiss)
271 ['ss', 2, true, false, 'sses'],
272
273 // syllabi (syllabus)
274 ['suballys', 8, true, true, 'syllabi'],
275
276 // buses (bus)
277 ['sub', 3, true, true, 'buses'],
278
279 // circuses (circus)
280 ['suc', 3, true, true, 'cuses'],
281
282 // conspectuses (conspectus), prospectuses (prospectus)
283 ['sutcep', 6, true, true, 'pectuses'],
284
285 // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
286 ['su', 2, true, true, 'i'],
287
288 // news (news)
289 ['swen', 4, true, true, 'news'],
290
291 // feet (foot)
292 ['toof', 4, true, true, 'feet'],
293
294 // chateaux (chateau), bureaus (bureau)
295 ['uae', 3, false, true, ['eaus', 'eaux']],
296
297 // oxen (ox)
298 ['xo', 2, false, false, 'oxen'],
299
300 // hoaxes (hoax)
301 ['xaoh', 4, true, false, 'hoaxes'],
302
303 // indices (index)
304 ['xedni', 5, false, true, ['indicies', 'indexes']],
305
306 // boxes (box)
307 ['xo', 2, false, true, 'oxes'],
308
309 // indexes (index), matrixes (matrix)
310 ['x', 1, true, false, ['cies', 'xes']],
311
312 // appendices (appendix)
313 ['xi', 2, false, true, 'ices'],
314
315 // babies (baby)
316 ['y', 1, false, true, 'ies'],
317
318 // quizzes (quiz)
319 ['ziuq', 4, true, false, 'quizzes'],
320
321 // waltzes (waltz)
322 ['z', 1, true, true, 'zes'],
323 ];
324
325 /**
326 * A list of words which should not be inflected, reversed.
327 */
328 private const UNINFLECTED = [
329 '',
330
331 // data
332 'atad',
333
334 // deer
335 'reed',
336
337 // feedback
338 'kcabdeef',
339
340 // fish
341 'hsif',
342
343 // info
344 'ofni',
345
346 // moose
347 'esoom',
348
349 // series
350 'seires',
351
352 // sheep
353 'peehs',
354
355 // species
356 'seiceps',
357 ];
358
359 /**
360 * {@inheritdoc}
361 */
362 public function singularize(string $plural): array
363 {
364 $pluralRev = strrev($plural);
365 $lowerPluralRev = strtolower($pluralRev);
366 $pluralLength = \strlen($lowerPluralRev);
367
368 // Check if the word is one which is not inflected, return early if so
369 if (\in_array($lowerPluralRev, self::UNINFLECTED, true)) {
370 return [$plural];
371 }
372
373 // The outer loop iterates over the entries of the plural table
374 // The inner loop $j iterates over the characters of the plural suffix
375 // in the plural table to compare them with the characters of the actual
376 // given plural suffix
377 foreach (self::PLURAL_MAP as $map) {
378 $suffix = $map[0];
379 $suffixLength = $map[1];
380 $j = 0;
381
382 // Compare characters in the plural table and of the suffix of the
383 // given plural one by one
384 while ($suffix[$j] === $lowerPluralRev[$j]) {
385 // Let $j point to the next character
386 ++$j;
387
388 // Successfully compared the last character
389 // Add an entry with the singular suffix to the singular array
390 if ($j === $suffixLength) {
391 // Is there any character preceding the suffix in the plural string?
392 if ($j < $pluralLength) {
393 $nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]);
394
395 if (!$map[2] && $nextIsVocal) {
396 // suffix may not succeed a vocal but next char is one
397 break;
398 }
399
400 if (!$map[3] && !$nextIsVocal) {
401 // suffix may not succeed a consonant but next char is one
402 break;
403 }
404 }
405
406 $newBase = substr($plural, 0, $pluralLength - $suffixLength);
407 $newSuffix = $map[4];
408
409 // Check whether the first character in the plural suffix
410 // is uppercased. If yes, uppercase the first character in
411 // the singular suffix too
412 $firstUpper = ctype_upper($pluralRev[$j - 1]);
413
414 if (\is_array($newSuffix)) {
415 $singulars = [];
416
417 foreach ($newSuffix as $newSuffixEntry) {
418 $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
419 }
420
421 return $singulars;
422 }
423
424 return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
425 }
426
427 // Suffix is longer than word
428 if ($j === $pluralLength) {
429 break;
430 }
431 }
432 }
433
434 // Assume that plural and singular is identical
435 return [$plural];
436 }
437
438 /**
439 * {@inheritdoc}
440 */
441 public function pluralize(string $singular): array
442 {
443 $singularRev = strrev($singular);
444 $lowerSingularRev = strtolower($singularRev);
445 $singularLength = \strlen($lowerSingularRev);
446
447 // Check if the word is one which is not inflected, return early if so
448 if (\in_array($lowerSingularRev, self::UNINFLECTED, true)) {
449 return [$singular];
450 }
451
452 // The outer loop iterates over the entries of the singular table
453 // The inner loop $j iterates over the characters of the singular suffix
454 // in the singular table to compare them with the characters of the actual
455 // given singular suffix
456 foreach (self::SINGULAR_MAP as $map) {
457 $suffix = $map[0];
458 $suffixLength = $map[1];
459 $j = 0;
460
461 // Compare characters in the singular table and of the suffix of the
462 // given plural one by one
463
464 while ($suffix[$j] === $lowerSingularRev[$j]) {
465 // Let $j point to the next character
466 ++$j;
467
468 // Successfully compared the last character
469 // Add an entry with the plural suffix to the plural array
470 if ($j === $suffixLength) {
471 // Is there any character preceding the suffix in the plural string?
472 if ($j < $singularLength) {
473 $nextIsVocal = false !== strpos('aeiou', $lowerSingularRev[$j]);
474
475 if (!$map[2] && $nextIsVocal) {
476 // suffix may not succeed a vocal but next char is one
477 break;
478 }
479
480 if (!$map[3] && !$nextIsVocal) {
481 // suffix may not succeed a consonant but next char is one
482 break;
483 }
484 }
485
486 $newBase = substr($singular, 0, $singularLength - $suffixLength);
487 $newSuffix = $map[4];
488
489 // Check whether the first character in the singular suffix
490 // is uppercased. If yes, uppercase the first character in
491 // the singular suffix too
492 $firstUpper = ctype_upper($singularRev[$j - 1]);
493
494 if (\is_array($newSuffix)) {
495 $plurals = [];
496
497 foreach ($newSuffix as $newSuffixEntry) {
498 $plurals[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
499 }
500
501 return $plurals;
502 }
503
504 return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
505 }
506
507 // Suffix is longer than word
508 if ($j === $singularLength) {
509 break;
510 }
511 }
512 }
513
514 // Assume that plural is singular with a trailing `s`
515 return [$singular.'s'];
516 }
517 }
518