summaryrefslogtreecommitdiff
path: root/vendor/symfony/string/Inflector
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/symfony/string/Inflector')
-rw-r--r--vendor/symfony/string/Inflector/EnglishInflector.php577
-rw-r--r--vendor/symfony/string/Inflector/FrenchInflector.php151
-rw-r--r--vendor/symfony/string/Inflector/InflectorInterface.php33
3 files changed, 761 insertions, 0 deletions
diff --git a/vendor/symfony/string/Inflector/EnglishInflector.php b/vendor/symfony/string/Inflector/EnglishInflector.php
new file mode 100644
index 0000000..77ebc13
--- /dev/null
+++ b/vendor/symfony/string/Inflector/EnglishInflector.php
@@ -0,0 +1,577 @@
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
12namespace Symfony\Component\String\Inflector;
13
14final 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 vowel
25 // Fourth entry: Whether the suffix may succeed a consonant
26 // Fifth entry: singular suffix, normal
27
28 // bacteria (bacterium)
29 ['airetcab', 8, true, true, 'bacterium'],
30
31 // corpora (corpus)
32 ['aroproc', 7, true, true, 'corpus'],
33
34 // criteria (criterion)
35 ['airetirc', 8, true, true, 'criterion'],
36
37 // curricula (curriculum)
38 ['alucirruc', 9, true, true, 'curriculum'],
39
40 // genera (genus)
41 ['areneg', 6, true, true, 'genus'],
42
43 // media (medium)
44 ['aidem', 5, true, true, 'medium'],
45
46 // memoranda (memorandum)
47 ['adnaromem', 9, true, true, 'memorandum'],
48
49 // phenomena (phenomenon)
50 ['anemonehp', 9, true, true, 'phenomenon'],
51
52 // strata (stratum)
53 ['atarts', 6, true, true, 'stratum'],
54
55 // nebulae (nebula)
56 ['ea', 2, true, true, 'a'],
57
58 // services (service)
59 ['secivres', 8, true, true, 'service'],
60
61 // mice (mouse), lice (louse)
62 ['eci', 3, false, true, 'ouse'],
63
64 // geese (goose)
65 ['esee', 4, false, true, 'oose'],
66
67 // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
68 ['i', 1, true, true, 'us'],
69
70 // men (man), women (woman)
71 ['nem', 3, true, true, 'man'],
72
73 // children (child)
74 ['nerdlihc', 8, true, true, 'child'],
75
76 // oxen (ox)
77 ['nexo', 4, false, false, 'ox'],
78
79 // indices (index), appendices (appendix), prices (price)
80 ['seci', 4, false, true, ['ex', 'ix', 'ice']],
81
82 // codes (code)
83 ['sedoc', 5, false, true, 'code'],
84
85 // selfies (selfie)
86 ['seifles', 7, true, true, 'selfie'],
87
88 // zombies (zombie)
89 ['seibmoz', 7, true, true, 'zombie'],
90
91 // movies (movie)
92 ['seivom', 6, true, true, 'movie'],
93
94 // names (name)
95 ['seman', 5, true, false, 'name'],
96
97 // conspectuses (conspectus), prospectuses (prospectus)
98 ['sesutcep', 8, true, true, 'pectus'],
99
100 // feet (foot)
101 ['teef', 4, true, true, 'foot'],
102
103 // geese (goose)
104 ['eseeg', 5, true, true, 'goose'],
105
106 // teeth (tooth)
107 ['hteet', 5, true, true, 'tooth'],
108
109 // news (news)
110 ['swen', 4, true, true, 'news'],
111
112 // series (series)
113 ['seires', 6, true, true, 'series'],
114
115 // babies (baby)
116 ['sei', 3, false, true, 'y'],
117
118 // accesses (access), addresses (address), kisses (kiss)
119 ['sess', 4, true, false, 'ss'],
120
121 // statuses (status)
122 ['sesutats', 8, true, true, 'status'],
123
124 // analyses (analysis), ellipses (ellipsis), fungi (fungus),
125 // neuroses (neurosis), theses (thesis), emphases (emphasis),
126 // oases (oasis), crises (crisis), houses (house), bases (base),
127 // atlases (atlas)
128 ['ses', 3, true, true, ['s', 'se', 'sis']],
129
130 // objectives (objective), alternative (alternatives)
131 ['sevit', 5, true, true, 'tive'],
132
133 // drives (drive)
134 ['sevird', 6, false, true, 'drive'],
135
136 // lives (life), wives (wife)
137 ['sevi', 4, false, true, 'ife'],
138
139 // moves (move)
140 ['sevom', 5, true, true, 'move'],
141
142 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff)
143 ['sev', 3, true, true, ['f', 've', 'ff']],
144
145 // axes (axis), axes (ax), axes (axe)
146 ['sexa', 4, false, false, ['ax', 'axe', 'axis']],
147
148 // indexes (index), matrixes (matrix)
149 ['sex', 3, true, false, 'x'],
150
151 // quizzes (quiz)
152 ['sezz', 4, true, false, 'z'],
153
154 // bureaus (bureau)
155 ['suae', 4, false, true, 'eau'],
156
157 // fees (fee), trees (tree), employees (employee)
158 ['see', 3, true, true, 'ee'],
159
160 // edges (edge)
161 ['segd', 4, true, true, 'dge'],
162
163 // roses (rose), garages (garage), cassettes (cassette),
164 // waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
165 // shoes (shoe)
166 ['se', 2, true, true, ['', 'e']],
167
168 // status (status)
169 ['sutats', 6, true, true, 'status'],
170
171 // tags (tag)
172 ['s', 1, true, true, ''],
173
174 // chateaux (chateau)
175 ['xuae', 4, false, true, 'eau'],
176
177 // people (person)
178 ['elpoep', 6, true, true, 'person'],
179 ];
180
181 /**
182 * Map English singular to plural suffixes.
183 *
184 * @see http://english-zone.com/spelling/plurals.html
185 */
186 private const SINGULAR_MAP = [
187 // First entry: singular suffix, reversed
188 // Second entry: length of singular suffix
189 // Third entry: Whether the suffix may succeed a vowel
190 // Fourth entry: Whether the suffix may succeed a consonant
191 // Fifth entry: plural suffix, normal
192
193 // axes (axis)
194 ['sixa', 4, false, false, 'axes'],
195
196 // criterion (criteria)
197 ['airetirc', 8, false, false, 'criterion'],
198
199 // nebulae (nebula)
200 ['aluben', 6, false, false, 'nebulae'],
201
202 // children (child)
203 ['dlihc', 5, true, true, 'children'],
204
205 // prices (price)
206 ['eci', 3, false, true, 'ices'],
207
208 // services (service)
209 ['ecivres', 7, true, true, 'services'],
210
211 // lives (life), wives (wife)
212 ['efi', 3, false, true, 'ives'],
213
214 // selfies (selfie)
215 ['eifles', 6, true, true, 'selfies'],
216
217 // movies (movie)
218 ['eivom', 5, true, true, 'movies'],
219
220 // lice (louse)
221 ['esuol', 5, false, true, 'lice'],
222
223 // mice (mouse)
224 ['esuom', 5, false, true, 'mice'],
225
226 // geese (goose)
227 ['esoo', 4, false, true, 'eese'],
228
229 // houses (house), bases (base)
230 ['es', 2, true, true, 'ses'],
231
232 // geese (goose)
233 ['esoog', 5, true, true, 'geese'],
234
235 // caves (cave)
236 ['ev', 2, true, true, 'ves'],
237
238 // drives (drive)
239 ['evird', 5, false, true, 'drives'],
240
241 // objectives (objective), alternative (alternatives)
242 ['evit', 4, true, true, 'tives'],
243
244 // moves (move)
245 ['evom', 4, true, true, 'moves'],
246
247 // staves (staff)
248 ['ffats', 5, true, true, 'staves'],
249
250 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
251 ['ff', 2, true, true, 'ffs'],
252
253 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
254 ['f', 1, true, true, ['fs', 'ves']],
255
256 // arches (arch)
257 ['hc', 2, true, true, 'ches'],
258
259 // bushes (bush)
260 ['hs', 2, true, true, 'shes'],
261
262 // teeth (tooth)
263 ['htoot', 5, true, true, 'teeth'],
264
265 // albums (album)
266 ['mubla', 5, true, true, 'albums'],
267
268 // bacteria (bacterium), curricula (curriculum), media (medium), memoranda (memorandum), phenomena (phenomenon), strata (stratum)
269 ['mu', 2, true, true, 'a'],
270
271 // men (man), women (woman)
272 ['nam', 3, true, true, 'men'],
273
274 // people (person)
275 ['nosrep', 6, true, true, ['persons', 'people']],
276
277 // criteria (criterion)
278 ['noiretirc', 9, true, true, 'criteria'],
279
280 // phenomena (phenomenon)
281 ['nonemonehp', 10, true, true, 'phenomena'],
282
283 // echoes (echo)
284 ['ohce', 4, true, true, 'echoes'],
285
286 // heroes (hero)
287 ['oreh', 4, true, true, 'heroes'],
288
289 // atlases (atlas)
290 ['salta', 5, true, true, 'atlases'],
291
292 // aliases (alias)
293 ['saila', 5, true, true, 'aliases'],
294
295 // irises (iris)
296 ['siri', 4, true, true, 'irises'],
297
298 // analyses (analysis), ellipses (ellipsis), neuroses (neurosis)
299 // theses (thesis), emphases (emphasis), oases (oasis),
300 // crises (crisis)
301 ['sis', 3, true, true, 'ses'],
302
303 // accesses (access), addresses (address), kisses (kiss)
304 ['ss', 2, true, false, 'sses'],
305
306 // syllabi (syllabus)
307 ['suballys', 8, true, true, 'syllabi'],
308
309 // buses (bus)
310 ['sub', 3, true, true, 'buses'],
311
312 // circuses (circus)
313 ['suc', 3, true, true, 'cuses'],
314
315 // hippocampi (hippocampus)
316 ['supmacoppih', 11, false, false, 'hippocampi'],
317
318 // campuses (campus)
319 ['sup', 3, true, true, 'puses'],
320
321 // status (status)
322 ['sutats', 6, true, true, ['status', 'statuses']],
323
324 // conspectuses (conspectus), prospectuses (prospectus)
325 ['sutcep', 6, true, true, 'pectuses'],
326
327 // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
328 ['su', 2, true, true, 'i'],
329
330 // news (news)
331 ['swen', 4, true, true, 'news'],
332
333 // feet (foot)
334 ['toof', 4, true, true, 'feet'],
335
336 // chateaux (chateau), bureaus (bureau)
337 ['uae', 3, false, true, ['eaus', 'eaux']],
338
339 // oxen (ox)
340 ['xo', 2, false, false, 'oxen'],
341
342 // hoaxes (hoax)
343 ['xaoh', 4, true, false, 'hoaxes'],
344
345 // indices (index)
346 ['xedni', 5, false, true, ['indicies', 'indexes']],
347
348 // boxes (box)
349 ['xo', 2, false, true, 'oxes'],
350
351 // indexes (index), matrixes (matrix)
352 ['x', 1, true, false, ['cies', 'xes']],
353
354 // appendices (appendix)
355 ['xi', 2, false, true, 'ices'],
356
357 // babies (baby)
358 ['y', 1, false, true, 'ies'],
359
360 // quizzes (quiz)
361 ['ziuq', 4, true, false, 'quizzes'],
362
363 // waltzes (waltz)
364 ['z', 1, true, true, 'zes'],
365 ];
366
367 /**
368 * A list of words which should not be inflected, reversed.
369 */
370 private const UNINFLECTED = [
371 '',
372
373 // data
374 'atad',
375
376 // deer
377 'reed',
378
379 // equipment
380 'tnempiuqe',
381
382 // feedback
383 'kcabdeef',
384
385 // fish
386 'hsif',
387
388 // health
389 'htlaeh',
390
391 // history
392 'yrotsih',
393
394 // info
395 'ofni',
396
397 // information
398 'noitamrofni',
399
400 // money
401 'yenom',
402
403 // moose
404 'esoom',
405
406 // series
407 'seires',
408
409 // sheep
410 'peehs',
411
412 // species
413 'seiceps',
414
415 // traffic
416 'ciffart',
417
418 // aircraft
419 'tfarcria',
420
421 // hardware
422 'erawdrah',
423 ];
424
425 public function singularize(string $plural): array
426 {
427 $pluralRev = strrev($plural);
428 $lowerPluralRev = strtolower($pluralRev);
429 $pluralLength = \strlen($lowerPluralRev);
430
431 // Check if the word is one which is not inflected, return early if so
432 if (\in_array($lowerPluralRev, self::UNINFLECTED, true)) {
433 return [$plural];
434 }
435
436 // The outer loop iterates over the entries of the plural table
437 // The inner loop $j iterates over the characters of the plural suffix
438 // in the plural table to compare them with the characters of the actual
439 // given plural suffix
440 foreach (self::PLURAL_MAP as $map) {
441 $suffix = $map[0];
442 $suffixLength = $map[1];
443 $j = 0;
444
445 // Compare characters in the plural table and of the suffix of the
446 // given plural one by one
447 while ($suffix[$j] === $lowerPluralRev[$j]) {
448 // Let $j point to the next character
449 ++$j;
450
451 // Successfully compared the last character
452 // Add an entry with the singular suffix to the singular array
453 if ($j === $suffixLength) {
454 // Is there any character preceding the suffix in the plural string?
455 if ($j < $pluralLength) {
456 $nextIsVowel = str_contains('aeiou', $lowerPluralRev[$j]);
457
458 if (!$map[2] && $nextIsVowel) {
459 // suffix may not succeed a vowel but next char is one
460 break;
461 }
462
463 if (!$map[3] && !$nextIsVowel) {
464 // suffix may not succeed a consonant but next char is one
465 break;
466 }
467 }
468
469 $newBase = substr($plural, 0, $pluralLength - $suffixLength);
470 $newSuffix = $map[4];
471
472 // Check whether the first character in the plural suffix
473 // is uppercased. If yes, uppercase the first character in
474 // the singular suffix too
475 $firstUpper = ctype_upper($pluralRev[$j - 1]);
476
477 if (\is_array($newSuffix)) {
478 $singulars = [];
479
480 foreach ($newSuffix as $newSuffixEntry) {
481 $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
482 }
483
484 return $singulars;
485 }
486
487 return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
488 }
489
490 // Suffix is longer than word
491 if ($j === $pluralLength) {
492 break;
493 }
494 }
495 }
496
497 // Assume that plural and singular is identical
498 return [$plural];
499 }
500
501 public function pluralize(string $singular): array
502 {
503 $singularRev = strrev($singular);
504 $lowerSingularRev = strtolower($singularRev);
505 $singularLength = \strlen($lowerSingularRev);
506
507 // Check if the word is one which is not inflected, return early if so
508 if (\in_array($lowerSingularRev, self::UNINFLECTED, true)) {
509 return [$singular];
510 }
511
512 // The outer loop iterates over the entries of the singular table
513 // The inner loop $j iterates over the characters of the singular suffix
514 // in the singular table to compare them with the characters of the actual
515 // given singular suffix
516 foreach (self::SINGULAR_MAP as $map) {
517 $suffix = $map[0];
518 $suffixLength = $map[1];
519 $j = 0;
520
521 // Compare characters in the singular table and of the suffix of the
522 // given plural one by one
523
524 while ($suffix[$j] === $lowerSingularRev[$j]) {
525 // Let $j point to the next character
526 ++$j;
527
528 // Successfully compared the last character
529 // Add an entry with the plural suffix to the plural array
530 if ($j === $suffixLength) {
531 // Is there any character preceding the suffix in the plural string?
532 if ($j < $singularLength) {
533 $nextIsVowel = str_contains('aeiou', $lowerSingularRev[$j]);
534
535 if (!$map[2] && $nextIsVowel) {
536 // suffix may not succeed a vowel but next char is one
537 break;
538 }
539
540 if (!$map[3] && !$nextIsVowel) {
541 // suffix may not succeed a consonant but next char is one
542 break;
543 }
544 }
545
546 $newBase = substr($singular, 0, $singularLength - $suffixLength);
547 $newSuffix = $map[4];
548
549 // Check whether the first character in the singular suffix
550 // is uppercased. If yes, uppercase the first character in
551 // the singular suffix too
552 $firstUpper = ctype_upper($singularRev[$j - 1]);
553
554 if (\is_array($newSuffix)) {
555 $plurals = [];
556
557 foreach ($newSuffix as $newSuffixEntry) {
558 $plurals[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
559 }
560
561 return $plurals;
562 }
563
564 return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
565 }
566
567 // Suffix is longer than word
568 if ($j === $singularLength) {
569 break;
570 }
571 }
572 }
573
574 // Assume that plural is singular with a trailing `s`
575 return [$singular.'s'];
576 }
577}
diff --git a/vendor/symfony/string/Inflector/FrenchInflector.php b/vendor/symfony/string/Inflector/FrenchInflector.php
new file mode 100644
index 0000000..955abbf
--- /dev/null
+++ b/vendor/symfony/string/Inflector/FrenchInflector.php
@@ -0,0 +1,151 @@
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
12namespace Symfony\Component\String\Inflector;
13
14/**
15 * French inflector.
16 *
17 * This class does only inflect nouns; not adjectives nor composed words like "soixante-dix".
18 */
19final class FrenchInflector implements InflectorInterface
20{
21 /**
22 * A list of all rules for pluralise.
23 *
24 * @see https://la-conjugaison.nouvelobs.com/regles/grammaire/le-pluriel-des-noms-121.php
25 */
26 private const PLURALIZE_REGEXP = [
27 // First entry: regexp
28 // Second entry: replacement
29
30 // Words finishing with "s", "x" or "z" are invariables
31 // Les mots finissant par "s", "x" ou "z" sont invariables
32 ['/(s|x|z)$/i', '\1'],
33
34 // Words finishing with "eau" are pluralized with a "x"
35 // Les mots finissant par "eau" prennent tous un "x" au pluriel
36 ['/(eau)$/i', '\1x'],
37
38 // Words finishing with "au" are pluralized with a "x" excepted "landau"
39 // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau"
40 ['/^(landau)$/i', '\1s'],
41 ['/(au)$/i', '\1x'],
42
43 // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu"
44 // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu"
45 ['/^(pneu|bleu|émeu)$/i', '\1s'],
46 ['/(eu)$/i', '\1x'],
47
48 // Words finishing with "al" are pluralized with a "aux" excepted
49 // Les mots finissant en "al" se terminent en "aux" sauf
50 ['/^(bal|carnaval|caracal|chacal|choral|corral|étal|festival|récital|val)$/i', '\1s'],
51 ['/al$/i', '\1aux'],
52
53 // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux
54 ['/^(aspir|b|cor|ém|ferm|soupir|trav|vant|vitr)ail$/i', '\1aux'],
55
56 // Bijou, caillou, chou, genou, hibou, joujou et pou qui prennent un x au pluriel
57 ['/^(bij|caill|ch|gen|hib|jouj|p)ou$/i', '\1oux'],
58
59 // Invariable words
60 ['/^(cinquante|soixante|mille)$/i', '\1'],
61
62 // French titles
63 ['/^(mon|ma)(sieur|dame|demoiselle|seigneur)$/', 'mes\2s'],
64 ['/^(Mon|Ma)(sieur|dame|demoiselle|seigneur)$/', 'Mes\2s'],
65 ];
66
67 /**
68 * A list of all rules for singularize.
69 */
70 private const SINGULARIZE_REGEXP = [
71 // First entry: regexp
72 // Second entry: replacement
73
74 // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux
75 ['/((aspir|b|cor|ém|ferm|soupir|trav|vant|vitr))aux$/i', '\1ail'],
76
77 // Words finishing with "eau" are pluralized with a "x"
78 // Les mots finissant par "eau" prennent tous un "x" au pluriel
79 ['/(eau)x$/i', '\1'],
80
81 // Words finishing with "al" are pluralized with a "aux" expected
82 // Les mots finissant en "al" se terminent en "aux" sauf
83 ['/(amir|anim|arsen|boc|can|capit|capor|chev|crist|génér|hopit|hôpit|idé|journ|littor|loc|m|mét|minér|princip|radic|termin)aux$/i', '\1al'],
84
85 // Words finishing with "au" are pluralized with a "x" excepted "landau"
86 // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau"
87 ['/(au)x$/i', '\1'],
88
89 // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu"
90 // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu"
91 ['/(eu)x$/i', '\1'],
92
93 // Words finishing with "ou" are pluralized with a "s" excepted bijou, caillou, chou, genou, hibou, joujou, pou
94 // Les mots finissant par "ou" prennent un "s" sauf bijou, caillou, chou, genou, hibou, joujou, pou
95 ['/(bij|caill|ch|gen|hib|jouj|p)oux$/i', '\1ou'],
96
97 // French titles
98 ['/^mes(dame|demoiselle)s$/', 'ma\1'],
99 ['/^Mes(dame|demoiselle)s$/', 'Ma\1'],
100 ['/^mes(sieur|seigneur)s$/', 'mon\1'],
101 ['/^Mes(sieur|seigneur)s$/', 'Mon\1'],
102
103 // Default rule
104 ['/s$/i', ''],
105 ];
106
107 /**
108 * A list of words which should not be inflected.
109 * This list is only used by singularize.
110 */
111 private const UNINFLECTED = '/^(abcès|accès|abus|albatros|anchois|anglais|autobus|bois|brebis|carquois|cas|chas|colis|concours|corps|cours|cyprès|décès|devis|discours|dos|embarras|engrais|entrelacs|excès|fils|fois|gâchis|gars|glas|héros|intrus|jars|jus|kermès|lacis|legs|lilas|marais|mars|matelas|mépris|mets|mois|mors|obus|os|palais|paradis|parcours|pardessus|pays|plusieurs|poids|pois|pouls|printemps|processus|progrès|puits|pus|rabais|radis|recors|recours|refus|relais|remords|remous|rictus|rhinocéros|repas|rubis|sans|sas|secours|sens|souris|succès|talus|tapis|tas|taudis|temps|tiers|univers|velours|verglas|vernis|virus)$/i';
112
113 public function singularize(string $plural): array
114 {
115 if ($this->isInflectedWord($plural)) {
116 return [$plural];
117 }
118
119 foreach (self::SINGULARIZE_REGEXP as $rule) {
120 [$regexp, $replace] = $rule;
121
122 if (1 === preg_match($regexp, $plural)) {
123 return [preg_replace($regexp, $replace, $plural)];
124 }
125 }
126
127 return [$plural];
128 }
129
130 public function pluralize(string $singular): array
131 {
132 if ($this->isInflectedWord($singular)) {
133 return [$singular];
134 }
135
136 foreach (self::PLURALIZE_REGEXP as $rule) {
137 [$regexp, $replace] = $rule;
138
139 if (1 === preg_match($regexp, $singular)) {
140 return [preg_replace($regexp, $replace, $singular)];
141 }
142 }
143
144 return [$singular.'s'];
145 }
146
147 private function isInflectedWord(string $word): bool
148 {
149 return 1 === preg_match(self::UNINFLECTED, $word);
150 }
151}
diff --git a/vendor/symfony/string/Inflector/InflectorInterface.php b/vendor/symfony/string/Inflector/InflectorInterface.php
new file mode 100644
index 0000000..67f2834
--- /dev/null
+++ b/vendor/symfony/string/Inflector/InflectorInterface.php
@@ -0,0 +1,33 @@
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
12namespace Symfony\Component\String\Inflector;
13
14interface InflectorInterface
15{
16 /**
17 * Returns the singular forms of a string.
18 *
19 * If the method can't determine the form with certainty, several possible singulars are returned.
20 *
21 * @return string[]
22 */
23 public function singularize(string $plural): array;
24
25 /**
26 * Returns the plural forms of a string.
27 *
28 * If the method can't determine the form with certainty, several possible plurals are returned.
29 *
30 * @return string[]
31 */
32 public function pluralize(string $singular): array;
33}