Skip to content

Commit 2d7f4eb

Browse files
committed
feature #58228 [String] Add Spanish inflector with some rules (dennistobar)
This PR was squashed before being merged into the 7.2 branch. Discussion ---------- [String] Add Spanish inflector with some rules | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | License | MIT This PR will create the Spanish Inflector using some easy rules (vowels and some ending letters). There are some problems about the accented words (ie: _abdomen/abdómenes_), so there is no way to create rules to change it :( [some rules are taken from RAE](https://www.rae.es/gram%C3%A1tica/morfolog%C3%ADa/la-formaci%C3%B3n-del-plural-plurales-en-s-y-plurales-en-es-reglas-generales) I used the `FrenchInflector` as base to create this file and the test suite. Commits ------- 76a1b5f451 [String] Add Spanish inflector with some rules
2 parents db12d24 + f1f4d05 commit 2d7f4eb

File tree

2 files changed

+284
-0
lines changed

2 files changed

+284
-0
lines changed

Inflector/SpanishInflector.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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 SpanishInflector implements InflectorInterface
15+
{
16+
/**
17+
* A list of all rules for pluralise.
18+
*
19+
* @see https://www.spanishdict.com/guide/spanish-plural-noun-forms
20+
* @see https://www.rae.es/gram%C3%A1tica/morfolog%C3%ADa/la-formaci%C3%B3n-del-plural-plurales-en-s-y-plurales-en-es-reglas-generales
21+
*/
22+
// First entry: regex
23+
// Second entry: replacement
24+
private const PLURALIZE_REGEXP = [
25+
// Specials sí, no
26+
['/(sí|no)$/i', '\1es'],
27+
28+
// Words ending with vowel must use -s (RAE 3.2a, 3.2c)
29+
['/(a|e|i|o|u|á|é|í|ó|ú)$/i', '\1s'],
30+
31+
// Word ending in s or x and the previous letter is accented (RAE 3.2n)
32+
['/ás$/i', 'ases'],
33+
['/és$/i', 'eses'],
34+
['/ís$/i', 'ises'],
35+
['/ós$/i', 'oses'],
36+
['/ús$/i', 'uses'],
37+
38+
// Words ending in -ión must changed to -iones
39+
['/ión$/i', '\1iones'],
40+
41+
// Words ending in some consonants must use -es (RAE 3.2k)
42+
['/(l|r|n|d|j|s|x|ch|y)$/i', '\1es'],
43+
44+
// Word ending in z, must changed to ces
45+
['/(z)$/i', 'ces'],
46+
];
47+
48+
/**
49+
* A list of all rules for singularize.
50+
*/
51+
private const SINGULARIZE_REGEXP = [
52+
// Specials sí, no
53+
['/(sí|no)es$/i', '\1'],
54+
55+
// Words ending in -ión must changed to -iones
56+
['/iones$/i', '\1ión'],
57+
58+
// Word ending in z, must changed to ces
59+
['/ces$/i', 'z'],
60+
61+
// Word ending in s or x and the previous letter is accented (RAE 3.2n)
62+
['/(\w)ases$/i', '\1ás'],
63+
['/eses$/i', 'és'],
64+
['/ises$/i', 'ís'],
65+
['/(\w{2,})oses$/i', '\1ós'],
66+
['/(\w)uses$/i', '\1ús'],
67+
68+
// Words ending in some consonants and -es, must be the consonants
69+
['/(l|r|n|d|j|s|x|ch|y)e?s$/i', '\1'],
70+
71+
// Words ended with vowel and s, must be vowel
72+
['/(a|e|i|o|u|á|é|ó|í|ú)s$/i', '\1'],
73+
];
74+
75+
private const UNINFLECTED_RULES = [
76+
// Words ending with pies (RAE 3.2n)
77+
'/.*(piés)$/i',
78+
];
79+
80+
private const UNINFLECTED = '/^(lunes|martes|miércoles|jueves|viernes|análisis|torax|yo|pies)$/i';
81+
82+
public function singularize(string $plural): array
83+
{
84+
if ($this->isInflectedWord($plural)) {
85+
return [$plural];
86+
}
87+
88+
foreach (self::SINGULARIZE_REGEXP as $rule) {
89+
[$regexp, $replace] = $rule;
90+
91+
if (1 === preg_match($regexp, $plural)) {
92+
return [preg_replace($regexp, $replace, $plural)];
93+
}
94+
}
95+
96+
return [$plural];
97+
}
98+
99+
public function pluralize(string $singular): array
100+
{
101+
if ($this->isInflectedWord($singular)) {
102+
return [$singular];
103+
}
104+
105+
foreach (self::PLURALIZE_REGEXP as $rule) {
106+
[$regexp, $replace] = $rule;
107+
108+
if (1 === preg_match($regexp, $singular)) {
109+
return [preg_replace($regexp, $replace, $singular)];
110+
}
111+
}
112+
113+
return [$singular.'s'];
114+
}
115+
116+
private function isInflectedWord(string $word): bool
117+
{
118+
foreach (self::UNINFLECTED_RULES as $rule) {
119+
if (1 === preg_match($rule, $word)) {
120+
return true;
121+
}
122+
}
123+
124+
return 1 === preg_match(self::UNINFLECTED, $word);
125+
}
126+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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\Tests\Inflector;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\String\Inflector\SpanishInflector;
16+
17+
class SpanishInflectorTest extends TestCase
18+
{
19+
public static function singularizeProvider(): array
20+
{
21+
return [
22+
// vowels (RAE 3.2a, 3.2c)
23+
['peras', 'pera'],
24+
['especies', 'especie'],
25+
['álcalis', 'álcali'],
26+
['códigos', 'código'],
27+
['espíritus', 'espíritu'],
28+
29+
// accented (RAE 3.2a, 3.2c)
30+
['papás', 'papá'],
31+
['cafés', 'café'],
32+
['isrealís', 'isrealí'],
33+
['burós', 'buró'],
34+
['tisús', 'tisú'],
35+
36+
// ending in -ión
37+
['aviones', 'avión'],
38+
['camiones', 'camión'],
39+
40+
// ending in some letters (RAE 3.2k)
41+
['amores', 'amor'],
42+
['antifaces', 'antifaz'],
43+
['atriles', 'atril'],
44+
['fácsimiles', 'fácsimil'],
45+
['vides', 'vid'],
46+
['reyes', 'rey'],
47+
['relojes', 'reloj'],
48+
['faxes', 'fax'],
49+
['sándwiches', 'sándwich'],
50+
['cánones', 'cánon'],
51+
52+
// (RAE 3.2n)
53+
['adioses', 'adiós'],
54+
['aguarrases', 'aguarrás'],
55+
['arneses', 'arnés'],
56+
['autobuses', 'autobús'],
57+
['kermeses', 'kermés'],
58+
['palmareses', 'palmarés'],
59+
['toses', 'tos'],
60+
61+
// Special
62+
['síes', ''],
63+
['noes', 'no'],
64+
];
65+
}
66+
67+
public static function pluralizeProvider(): array
68+
{
69+
return [
70+
// vowels (RAE 3.2a, 3.2c)
71+
['pera', 'peras'],
72+
['especie', 'especies'],
73+
['álcali', 'álcalis'],
74+
['código', 'códigos'],
75+
['espíritu', 'espíritus'],
76+
77+
// accented (RAE 3.2a, 3.2c)
78+
['papá', 'papás'],
79+
['café', 'cafés'],
80+
['isrealí', 'isrealís'],
81+
['buró', 'burós'],
82+
['tisú', 'tisús'],
83+
84+
// ending in -ión
85+
['avión', 'aviones'],
86+
['camión', 'camiones'],
87+
88+
// ending in some letters (RAE 3.2k)
89+
['amor', 'amores'],
90+
['antifaz', 'antifaces'],
91+
['atril', 'atriles'],
92+
['fácsimil', 'fácsimiles'],
93+
['vid', 'vides'],
94+
['rey', 'reyes'],
95+
['reloj', 'relojes'],
96+
['fax', 'faxes'],
97+
['sándwich', 'sándwiches'],
98+
['cánon', 'cánones'],
99+
100+
// (RAE 3.2n)
101+
['adiós', 'adioses'],
102+
['aguarrás', 'aguarrases'],
103+
['arnés', 'arneses'],
104+
['autobús', 'autobuses'],
105+
['kermés', 'kermeses'],
106+
['palmarés', 'palmareses'],
107+
['tos', 'toses'],
108+
109+
// Specials
110+
['', 'síes'],
111+
['no', 'noes'],
112+
];
113+
}
114+
115+
public static function uninflectedProvider(): array
116+
{
117+
return [
118+
['lunes'],
119+
['rodapiés'],
120+
['reposapiés'],
121+
['miércoles'],
122+
['pies'],
123+
];
124+
}
125+
126+
/**
127+
* @dataProvider singularizeProvider
128+
*/
129+
public function testSingularize(string $plural, $singular)
130+
{
131+
$this->assertSame(
132+
\is_array($singular) ? $singular : [$singular],
133+
(new SpanishInflector())->singularize($plural)
134+
);
135+
}
136+
137+
/**
138+
* @dataProvider pluralizeProvider
139+
*/
140+
public function testPluralize(string $singular, $plural)
141+
{
142+
$this->assertSame(
143+
\is_array($plural) ? $plural : [$plural],
144+
(new SpanishInflector())->pluralize($singular)
145+
);
146+
}
147+
148+
/**
149+
* @dataProvider uninflectedProvider
150+
*/
151+
public function testUninflected(string $word)
152+
{
153+
$this->assertSame(
154+
\is_array($word) ? $word : [$word],
155+
(new SpanishInflector())->pluralize($word)
156+
);
157+
}
158+
}

0 commit comments

Comments
 (0)