PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / trunk
Matomo Analytics – Powerful, Privacy-First Insights for WordPress vtrunk
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / vendor / prefixed / symfony / string / Inflector / EnglishInflector.php
matomo / app / vendor / prefixed / symfony / string / Inflector Last commit date
EnglishInflector.php 1 year ago FrenchInflector.php 2 years ago InflectorInterface.php 2 years ago
EnglishInflector.php
423 lines
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 namespace Matomo\Dependencies\Symfony\Component\String\Inflector;
12
13 final class EnglishInflector implements InflectorInterface
14 {
15 /**
16 * Map English plural to singular suffixes.
17 *
18 * @see http://english-zone.com/spelling/plurals.html
19 */
20 private const PLURAL_MAP = [
21 // First entry: plural suffix, reversed
22 // Second entry: length of plural suffix
23 // Third entry: Whether the suffix may succeed a vowel
24 // Fourth entry: Whether the suffix may succeed a consonant
25 // Fifth entry: singular suffix, normal
26 // bacteria (bacterium)
27 ['airetcab', 8, \true, \true, 'bacterium'],
28 // corpora (corpus)
29 ['aroproc', 7, \true, \true, 'corpus'],
30 // criteria (criterion)
31 ['airetirc', 8, \true, \true, 'criterion'],
32 // curricula (curriculum)
33 ['alucirruc', 9, \true, \true, 'curriculum'],
34 // genera (genus)
35 ['areneg', 6, \true, \true, 'genus'],
36 // media (medium)
37 ['aidem', 5, \true, \true, 'medium'],
38 // memoranda (memorandum)
39 ['adnaromem', 9, \true, \true, 'memorandum'],
40 // phenomena (phenomenon)
41 ['anemonehp', 9, \true, \true, 'phenomenon'],
42 // strata (stratum)
43 ['atarts', 6, \true, \true, 'stratum'],
44 // nebulae (nebula)
45 ['ea', 2, \true, \true, 'a'],
46 // services (service)
47 ['secivres', 8, \true, \true, 'service'],
48 // mice (mouse), lice (louse)
49 ['eci', 3, \false, \true, 'ouse'],
50 // geese (goose)
51 ['esee', 4, \false, \true, 'oose'],
52 // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
53 ['i', 1, \true, \true, 'us'],
54 // men (man), women (woman)
55 ['nem', 3, \true, \true, 'man'],
56 // children (child)
57 ['nerdlihc', 8, \true, \true, 'child'],
58 // oxen (ox)
59 ['nexo', 4, \false, \false, 'ox'],
60 // indices (index), appendices (appendix), prices (price)
61 ['seci', 4, \false, \true, ['ex', 'ix', 'ice']],
62 // codes (code)
63 ['sedoc', 5, \false, \true, 'code'],
64 // selfies (selfie)
65 ['seifles', 7, \true, \true, 'selfie'],
66 // zombies (zombie)
67 ['seibmoz', 7, \true, \true, 'zombie'],
68 // movies (movie)
69 ['seivom', 6, \true, \true, 'movie'],
70 // names (name)
71 ['seman', 5, \true, \false, 'name'],
72 // conspectuses (conspectus), prospectuses (prospectus)
73 ['sesutcep', 8, \true, \true, 'pectus'],
74 // feet (foot)
75 ['teef', 4, \true, \true, 'foot'],
76 // geese (goose)
77 ['eseeg', 5, \true, \true, 'goose'],
78 // teeth (tooth)
79 ['hteet', 5, \true, \true, 'tooth'],
80 // news (news)
81 ['swen', 4, \true, \true, 'news'],
82 // series (series)
83 ['seires', 6, \true, \true, 'series'],
84 // babies (baby)
85 ['sei', 3, \false, \true, 'y'],
86 // accesses (access), addresses (address), kisses (kiss)
87 ['sess', 4, \true, \false, 'ss'],
88 // statuses (status)
89 ['sesutats', 8, \true, \true, 'status'],
90 // article (articles), ancle (ancles)
91 ['sel', 3, \true, \true, 'le'],
92 // analyses (analysis), ellipses (ellipsis), fungi (fungus),
93 // neuroses (neurosis), theses (thesis), emphases (emphasis),
94 // oases (oasis), crises (crisis), houses (house), bases (base),
95 // atlases (atlas)
96 ['ses', 3, \true, \true, ['s', 'se', 'sis']],
97 // objectives (objective), alternative (alternatives)
98 ['sevit', 5, \true, \true, 'tive'],
99 // drives (drive)
100 ['sevird', 6, \false, \true, 'drive'],
101 // lives (life), wives (wife)
102 ['sevi', 4, \false, \true, 'ife'],
103 // moves (move)
104 ['sevom', 5, \true, \true, 'move'],
105 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff)
106 ['sev', 3, \true, \true, ['f', 've', 'ff']],
107 // axes (axis), axes (ax), axes (axe)
108 ['sexa', 4, \false, \false, ['ax', 'axe', 'axis']],
109 // indexes (index), matrixes (matrix)
110 ['sex', 3, \true, \false, 'x'],
111 // quizzes (quiz)
112 ['sezz', 4, \true, \false, 'z'],
113 // bureaus (bureau)
114 ['suae', 4, \false, \true, 'eau'],
115 // fees (fee), trees (tree), employees (employee)
116 ['see', 3, \true, \true, 'ee'],
117 // edges (edge)
118 ['segd', 4, \true, \true, 'dge'],
119 // roses (rose), garages (garage), cassettes (cassette),
120 // waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
121 // shoes (shoe)
122 ['se', 2, \true, \true, ['', 'e']],
123 // status (status)
124 ['sutats', 6, \true, \true, 'status'],
125 // tags (tag)
126 ['s', 1, \true, \true, ''],
127 // chateaux (chateau)
128 ['xuae', 4, \false, \true, 'eau'],
129 // people (person)
130 ['elpoep', 6, \true, \true, 'person'],
131 ];
132 /**
133 * Map English singular to plural suffixes.
134 *
135 * @see http://english-zone.com/spelling/plurals.html
136 */
137 private const SINGULAR_MAP = [
138 // First entry: singular suffix, reversed
139 // Second entry: length of singular suffix
140 // Third entry: Whether the suffix may succeed a vowel
141 // Fourth entry: Whether the suffix may succeed a consonant
142 // Fifth entry: plural suffix, normal
143 // axes (axis)
144 ['sixa', 4, \false, \false, 'axes'],
145 // criterion (criteria)
146 ['airetirc', 8, \false, \false, 'criterion'],
147 // nebulae (nebula)
148 ['aluben', 6, \false, \false, 'nebulae'],
149 // children (child)
150 ['dlihc', 5, \true, \true, 'children'],
151 // prices (price)
152 ['eci', 3, \false, \true, 'ices'],
153 // services (service)
154 ['ecivres', 7, \true, \true, 'services'],
155 // lives (life), wives (wife)
156 ['efi', 3, \false, \true, 'ives'],
157 // selfies (selfie)
158 ['eifles', 6, \true, \true, 'selfies'],
159 // movies (movie)
160 ['eivom', 5, \true, \true, 'movies'],
161 // lice (louse)
162 ['esuol', 5, \false, \true, 'lice'],
163 // mice (mouse)
164 ['esuom', 5, \false, \true, 'mice'],
165 // geese (goose)
166 ['esoo', 4, \false, \true, 'eese'],
167 // houses (house), bases (base)
168 ['es', 2, \true, \true, 'ses'],
169 // geese (goose)
170 ['esoog', 5, \true, \true, 'geese'],
171 // caves (cave)
172 ['ev', 2, \true, \true, 'ves'],
173 // drives (drive)
174 ['evird', 5, \false, \true, 'drives'],
175 // objectives (objective), alternative (alternatives)
176 ['evit', 4, \true, \true, 'tives'],
177 // moves (move)
178 ['evom', 4, \true, \true, 'moves'],
179 // staves (staff)
180 ['ffats', 5, \true, \true, 'staves'],
181 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
182 ['ff', 2, \true, \true, 'ffs'],
183 // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf)
184 ['f', 1, \true, \true, ['fs', 'ves']],
185 // arches (arch)
186 ['hc', 2, \true, \true, 'ches'],
187 // bushes (bush)
188 ['hs', 2, \true, \true, 'shes'],
189 // teeth (tooth)
190 ['htoot', 5, \true, \true, 'teeth'],
191 // albums (album)
192 ['mubla', 5, \true, \true, 'albums'],
193 // bacteria (bacterium), curricula (curriculum), media (medium), memoranda (memorandum), phenomena (phenomenon), strata (stratum)
194 ['mu', 2, \true, \true, 'a'],
195 // men (man), women (woman)
196 ['nam', 3, \true, \true, 'men'],
197 // people (person)
198 ['nosrep', 6, \true, \true, ['persons', 'people']],
199 // criteria (criterion)
200 ['noiretirc', 9, \true, \true, 'criteria'],
201 // phenomena (phenomenon)
202 ['nonemonehp', 10, \true, \true, 'phenomena'],
203 // echoes (echo)
204 ['ohce', 4, \true, \true, 'echoes'],
205 // heroes (hero)
206 ['oreh', 4, \true, \true, 'heroes'],
207 // atlases (atlas)
208 ['salta', 5, \true, \true, 'atlases'],
209 // aliases (alias)
210 ['saila', 5, \true, \true, 'aliases'],
211 // irises (iris)
212 ['siri', 4, \true, \true, 'irises'],
213 // analyses (analysis), ellipses (ellipsis), neuroses (neurosis)
214 // theses (thesis), emphases (emphasis), oases (oasis),
215 // crises (crisis)
216 ['sis', 3, \true, \true, 'ses'],
217 // accesses (access), addresses (address), kisses (kiss)
218 ['ss', 2, \true, \false, 'sses'],
219 // syllabi (syllabus)
220 ['suballys', 8, \true, \true, 'syllabi'],
221 // buses (bus)
222 ['sub', 3, \true, \true, 'buses'],
223 // circuses (circus)
224 ['suc', 3, \true, \true, 'cuses'],
225 // hippocampi (hippocampus)
226 ['supmacoppih', 11, \false, \false, 'hippocampi'],
227 // campuses (campus)
228 ['sup', 3, \true, \true, 'puses'],
229 // status (status)
230 ['sutats', 6, \true, \true, ['status', 'statuses']],
231 // conspectuses (conspectus), prospectuses (prospectus)
232 ['sutcep', 6, \true, \true, 'pectuses'],
233 // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)
234 ['su', 2, \true, \true, 'i'],
235 // news (news)
236 ['swen', 4, \true, \true, 'news'],
237 // feet (foot)
238 ['toof', 4, \true, \true, 'feet'],
239 // chateaux (chateau), bureaus (bureau)
240 ['uae', 3, \false, \true, ['eaus', 'eaux']],
241 // oxen (ox)
242 ['xo', 2, \false, \false, 'oxen'],
243 // hoaxes (hoax)
244 ['xaoh', 4, \true, \false, 'hoaxes'],
245 // indices (index)
246 ['xedni', 5, \false, \true, ['indicies', 'indexes']],
247 // fax (faxes, faxxes)
248 ['xaf', 3, \true, \true, ['faxes', 'faxxes']],
249 // boxes (box)
250 ['xo', 2, \false, \true, 'oxes'],
251 // indexes (index), matrixes (matrix), appendices (appendix)
252 ['x', 1, \true, \false, ['ces', 'xes']],
253 // babies (baby)
254 ['y', 1, \false, \true, 'ies'],
255 // quizzes (quiz)
256 ['ziuq', 4, \true, \false, 'quizzes'],
257 // waltzes (waltz)
258 ['z', 1, \true, \true, 'zes'],
259 ];
260 /**
261 * A list of words which should not be inflected, reversed.
262 */
263 private const UNINFLECTED = [
264 '',
265 // data
266 'atad',
267 // deer
268 'reed',
269 // equipment
270 'tnempiuqe',
271 // feedback
272 'kcabdeef',
273 // fish
274 'hsif',
275 // health
276 'htlaeh',
277 // history
278 'yrotsih',
279 // info
280 'ofni',
281 // information
282 'noitamrofni',
283 // money
284 'yenom',
285 // moose
286 'esoom',
287 // series
288 'seires',
289 // sheep
290 'peehs',
291 // species
292 'seiceps',
293 // traffic
294 'ciffart',
295 // aircraft
296 'tfarcria',
297 // hardware
298 'erawdrah',
299 ];
300 public function singularize(string $plural) : array
301 {
302 $pluralRev = strrev($plural);
303 $lowerPluralRev = strtolower($pluralRev);
304 $pluralLength = \strlen($lowerPluralRev);
305 // Check if the word is one which is not inflected, return early if so
306 if (\in_array($lowerPluralRev, self::UNINFLECTED, \true)) {
307 return [$plural];
308 }
309 // The outer loop iterates over the entries of the plural table
310 // The inner loop $j iterates over the characters of the plural suffix
311 // in the plural table to compare them with the characters of the actual
312 // given plural suffix
313 foreach (self::PLURAL_MAP as $map) {
314 $suffix = $map[0];
315 $suffixLength = $map[1];
316 $j = 0;
317 // Compare characters in the plural table and of the suffix of the
318 // given plural one by one
319 while ($suffix[$j] === $lowerPluralRev[$j]) {
320 // Let $j point to the next character
321 ++$j;
322 // Successfully compared the last character
323 // Add an entry with the singular suffix to the singular array
324 if ($j === $suffixLength) {
325 // Is there any character preceding the suffix in the plural string?
326 if ($j < $pluralLength) {
327 $nextIsVowel = str_contains('aeiou', $lowerPluralRev[$j]);
328 if (!$map[2] && $nextIsVowel) {
329 // suffix may not succeed a vowel but next char is one
330 break;
331 }
332 if (!$map[3] && !$nextIsVowel) {
333 // suffix may not succeed a consonant but next char is one
334 break;
335 }
336 }
337 $newBase = substr($plural, 0, $pluralLength - $suffixLength);
338 $newSuffix = $map[4];
339 // Check whether the first character in the plural suffix
340 // is uppercased. If yes, uppercase the first character in
341 // the singular suffix too
342 $firstUpper = ctype_upper($pluralRev[$j - 1]);
343 if (\is_array($newSuffix)) {
344 $singulars = [];
345 foreach ($newSuffix as $newSuffixEntry) {
346 $singulars[] = $newBase . ($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
347 }
348 return $singulars;
349 }
350 return [$newBase . ($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
351 }
352 // Suffix is longer than word
353 if ($j === $pluralLength) {
354 break;
355 }
356 }
357 }
358 // Assume that plural and singular is identical
359 return [$plural];
360 }
361 public function pluralize(string $singular) : array
362 {
363 $singularRev = strrev($singular);
364 $lowerSingularRev = strtolower($singularRev);
365 $singularLength = \strlen($lowerSingularRev);
366 // Check if the word is one which is not inflected, return early if so
367 if (\in_array($lowerSingularRev, self::UNINFLECTED, \true)) {
368 return [$singular];
369 }
370 // The outer loop iterates over the entries of the singular table
371 // The inner loop $j iterates over the characters of the singular suffix
372 // in the singular table to compare them with the characters of the actual
373 // given singular suffix
374 foreach (self::SINGULAR_MAP as $map) {
375 $suffix = $map[0];
376 $suffixLength = $map[1];
377 $j = 0;
378 // Compare characters in the singular table and of the suffix of the
379 // given plural one by one
380 while ($suffix[$j] === $lowerSingularRev[$j]) {
381 // Let $j point to the next character
382 ++$j;
383 // Successfully compared the last character
384 // Add an entry with the plural suffix to the plural array
385 if ($j === $suffixLength) {
386 // Is there any character preceding the suffix in the plural string?
387 if ($j < $singularLength) {
388 $nextIsVowel = str_contains('aeiou', $lowerSingularRev[$j]);
389 if (!$map[2] && $nextIsVowel) {
390 // suffix may not succeed a vowel but next char is one
391 break;
392 }
393 if (!$map[3] && !$nextIsVowel) {
394 // suffix may not succeed a consonant but next char is one
395 break;
396 }
397 }
398 $newBase = substr($singular, 0, $singularLength - $suffixLength);
399 $newSuffix = $map[4];
400 // Check whether the first character in the singular suffix
401 // is uppercased. If yes, uppercase the first character in
402 // the singular suffix too
403 $firstUpper = ctype_upper($singularRev[$j - 1]);
404 if (\is_array($newSuffix)) {
405 $plurals = [];
406 foreach ($newSuffix as $newSuffixEntry) {
407 $plurals[] = $newBase . ($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry);
408 }
409 return $plurals;
410 }
411 return [$newBase . ($firstUpper ? ucfirst($newSuffix) : $newSuffix)];
412 }
413 // Suffix is longer than word
414 if ($j === $singularLength) {
415 break;
416 }
417 }
418 }
419 // Assume that plural is singular with a trailing `s`
420 return [$singular . 's'];
421 }
422 }
423