PluginProbe
Depicter — Popup & Slider Builder / 1.3.3
Depicter — Popup & Slider Builder v1.3.3
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.3.3, at vendor/symfony/string/Inflector/EnglishInflector.php

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