Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dutch language #173

Open
wants to merge 18 commits into
base: 2.1.x
Choose a base branch
from
1 change: 1 addition & 0 deletions docs/en/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ you want to create an inflector for to the ``createForLanguage()`` method:

The supported languages are as follows:

- ``Language::DUTCH``
- ``Language::ENGLISH``
- ``Language::FRENCH``
- ``Language::NORWEGIAN_BOKMAL``
Expand Down
4 changes: 4 additions & 0 deletions lib/Doctrine/Inflector/InflectorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Doctrine\Inflector;

use Doctrine\Inflector\Rules\Dutch;
use Doctrine\Inflector\Rules\English;
use Doctrine\Inflector\Rules\French;
use Doctrine\Inflector\Rules\NorwegianBokmal;
Expand All @@ -24,6 +25,9 @@ public static function create(): LanguageInflectorFactory
public static function createForLanguage(string $language): LanguageInflectorFactory
{
switch ($language) {
case Language::DUTCH:
return new Dutch\InflectorFactory();

case Language::ENGLISH:
return new English\InflectorFactory();

Expand Down
1 change: 1 addition & 0 deletions lib/Doctrine/Inflector/Language.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

final class Language
{
public const DUTCH = 'dutch';
public const ENGLISH = 'english';
public const FRENCH = 'french';
public const NORWEGIAN_BOKMAL = 'norwegian-bokmal';
Expand Down
164 changes: 164 additions & 0 deletions lib/Doctrine/Inflector/Rules/Dutch/Inflectible.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php

declare(strict_types=1);

namespace Doctrine\Inflector\Rules\Dutch;

use Doctrine\Inflector\Rules\Pattern;
use Doctrine\Inflector\Rules\Substitution;
use Doctrine\Inflector\Rules\Transformation;
use Doctrine\Inflector\Rules\Word;

/**
* // http://nl.wikipedia.org/wiki/Klinker_(klank)
* private $klinker = '(a|e|i|o|u|ij)';
* private $korteKlinker = '(u|i|e|a|o)'; // @todo '(ie|oe)'. $plofKlank
*
* // http://nl.wikipedia.org/wiki/Medeklinker
* private $plofKlank = '(p|t|k|b|d)';
* private $wrijfKlank = '(f|s|ch|sj|v|z|g|j)'; // journaal
* private $neusKlank = '(m|n|ng)';
* private $vloeiKlank = '(l|r)';
* private $glijKlank = '(j|w)'; // jaar
* private $affricate = '(ts|zz|tsj|g)'; // /d3/ gin
* private $missingFromWiki = '(c|h|p|q|x|y|z)';
*
* public function __construct()
* {
* $this->medeklinker = '(' . $this->missingFromWiki . '|' . $this->plofKlank . '|' . $this->wrijfKlank . '|' . $this->neusKlank . '|' . $this->vloeiKlank . '|' . $this->glijKlank . '|' . $this->affricate . ')';
* $this->medeklinker = '((c|h|p|q|x|y|z)|(p|t|k|b|d)|(f|s|ch|sj|v|z|g|j)|(m|n|ng)|(l|r)|(j|w)|(ts|zz|tsj|g))';
* }
greg0ire marked this conversation as resolved.
Show resolved Hide resolved
*/
class Inflectible
{
/**
* @return iterable<Transformation>
*/
public static function getSingular(): iterable
{
// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Klinkerverandering
yield new Transformation(new Pattern('()heden$'), '\1heid');

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Beroepen_eindigend_op_-man
yield new Transformation(new Pattern('()mannen$'), '\1man');

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Latijnse_meervoudsvormen
yield new Transformation(new Pattern('()ices$'), '\1ex');

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Stapelmeervoud
yield new Transformation(new Pattern('^(ei|gemoed|goed|hoen|kind|lied|rad|rund)eren$'), '\1');

// http://nl.wikipedia.org/wiki/Nederlandse_grammatica
yield new Transformation(new Pattern('()ijen$'), '\1ij');

yield new Transformation(new Pattern('()ieen$'), '\1ie'); // ën

yield new Transformation(new Pattern('()((a|e|i|o|u|ij))s$'), '\1\2');

yield new Transformation(new Pattern('()((s)s)en$'), '\1s');

yield new Transformation(new Pattern('()((c|h|p|q|x|y|z)|(p|t|k|b|d)|(f|s|ch|sj|v|z|g|j)|(m|n|ng)|(l|r)|(j|w)|(ts|zz|tsj|g))en$'), '\1\2');
}

/**
* @return iterable<Transformation>
*/
public static function getPlural(): iterable
{
// @todo already in plural (?)
// @todo refine
yield new Transformation(new Pattern('()(e)(s)$'), '\1\2\3\3en');

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Klinkerverandering
yield new Transformation(new Pattern('()heid$'), '\1heden');

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Beroepen_eindigend_op_-man
yield new Transformation(new Pattern('()man$'), '\1mannen');

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Latijnse_meervoudsvormen
yield new Transformation(new Pattern('()ix$'), '\1ices');

yield new Transformation(new Pattern('()ex$'), '\1ices');

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Stapelmeervoud
yield new Transformation(new Pattern('^(ei|gemoed|goed|hoen|kind|lied|rad|rund)$'), '\1eren');

// http://nl.wikipedia.org/wiki/Nederlandse_grammatica
yield new Transformation(new Pattern('()ij$'), '\1ijen');

yield new Transformation(new Pattern('()orie$'), '\1orieen'); // ën klemtoon

yield new Transformation(new Pattern('()io$'), '\1io\'s');

yield new Transformation(new Pattern('()(a|e|i|o|u|ij)$'), '\1\2s');

yield new Transformation(new Pattern('()(((c|h|p|q|x|y|z)|(p|t|k|b|d)|(f|s|ch|sj|v|z|g|j)|(m|n|ng)|(l|r)|(j|w)|(ts|zz|tsj|g))e((c|h|p|q|x|y|z)|(p|t|k|b|d)|(f|s|ch|sj|v|z|g|j)|(m|n|ng)|(l|r)|(j|w)|(ts|zz|tsj|g)))$'), '\1\2s');

yield new Transformation(new Pattern('()(((c|h|p|q|x|y|z)|(p|t|k|b|d)|(f|s|ch|sj|v|z|g|j)|(m|n|ng)|(l|r)|(j|w)|(ts|zz|tsj|g))(u|i|e|a|o)s)$'), '\1\2sen');

yield new Transformation(new Pattern('()(((c|h|p|q|x|y|z)|(p|t|k|b|d)|(f|s|ch|sj|v|z|g|j)|(m|n|ng)|(l|r)|(j|w)|(ts|zz|tsj|g))s)$'), '\1\2en');

yield new Transformation(new Pattern('()s$'), '\1zen');

yield new Transformation(new Pattern('()((c|h|p|q|x|y|z)|(p|t|k|b|d)|(f|s|ch|sj|v|z|g|j)|(m|n|ng)|(l|r)|(j|w)|(ts|zz|tsj|g))$'), '\1\2en');
}

/**
* @return iterable<Substitution>
*/
public static function getIrregular(): iterable
{
// http://nl.wikipedia.org/wiki/Klemtoon
yield new Substitution(new Word('olie'), new Word('oliën'));

yield new Substitution(new Word('industrie'), new Word('industrieën'));

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Klinkerverandering
yield new Substitution(new Word('lid'), new Word('leden'));

yield new Substitution(new Word('smid'), new Word('smeden'));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the word above should be a rule. Also includes words like ooglid (and other samples of ~lid)


yield new Substitution(new Word('schip'), new Word('schepen'));

yield new Substitution(new Word('stad'), new Word('steden'));

yield new Substitution(new Word('gelid'), new Word('gelederen'));

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Stapelmeervoud
yield new Substitution(new Word('gelid'), new Word('gelederen'));
noud marked this conversation as resolved.
Show resolved Hide resolved

yield new Substitution(new Word('kalf'), new Word('kalveren'));

yield new Substitution(new Word('lam'), new Word('lammeren'));

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Onregelmatige_meervoudsvorming
yield new Substitution(new Word('koe'), new Word('koeien'));

yield new Substitution(new Word('vlo'), new Word('vlooien'));

yield new Substitution(new Word('leerrede'), new Word('leerredenen'));

yield new Substitution(new Word('lende'), new Word('lendenen'));

yield new Substitution(new Word('epos'), new Word('epen'));

yield new Substitution(new Word('genius'), new Word('geniën'));

yield new Substitution(new Word('aanbod'), new Word('aanbiedingen'));

yield new Substitution(new Word('beleg'), new Word('belegeringen'));

yield new Substitution(new Word('dank'), new Word('dankbetuigingen'));

yield new Substitution(new Word('gedrag'), new Word('gedragingen'));

yield new Substitution(new Word('genot'), new Word('genietingen'));

yield new Substitution(new Word('lof'), new Word('lofbetuigingen'));

// http://nl.wikipedia.org/wiki/Meervoud_(Nederlands)#Latijnse_meervoudsvormen
yield new Substitution(new Word('qaestrices'), new Word('quaestrix'));

yield new Substitution(new Word('matrices'), new Word('matrix'));
}
}
21 changes: 21 additions & 0 deletions lib/Doctrine/Inflector/Rules/Dutch/InflectorFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Doctrine\Inflector\Rules\Dutch;

use Doctrine\Inflector\GenericLanguageInflectorFactory;
use Doctrine\Inflector\Rules\Ruleset;

final class InflectorFactory extends GenericLanguageInflectorFactory
{
protected function getSingularRuleset(): Ruleset
{
return Rules::getSingularRuleset();
}

protected function getPluralRuleset(): Ruleset
{
return Rules::getPluralRuleset();
}
}
31 changes: 31 additions & 0 deletions lib/Doctrine/Inflector/Rules/Dutch/Rules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Doctrine\Inflector\Rules\Dutch;

use Doctrine\Inflector\Rules\Patterns;
use Doctrine\Inflector\Rules\Ruleset;
use Doctrine\Inflector\Rules\Substitutions;
use Doctrine\Inflector\Rules\Transformations;

final class Rules
{
public static function getSingularRuleset(): Ruleset
{
return new Ruleset(
new Transformations(...Inflectible::getSingular()),
new Patterns(...Uninflected::getSingular()),
(new Substitutions(...Inflectible::getIrregular()))->getFlippedSubstitutions()
);
}

public static function getPluralRuleset(): Ruleset
{
return new Ruleset(
new Transformations(...Inflectible::getPlural()),
new Patterns(...Uninflected::getPlural()),
new Substitutions(...Inflectible::getIrregular())
);
}
}
66 changes: 66 additions & 0 deletions lib/Doctrine/Inflector/Rules/Dutch/Uninflected.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace Doctrine\Inflector\Rules\Dutch;

use Doctrine\Inflector\Rules\Pattern;

final class Uninflected
{
/**
* @return iterable<Pattern>
*/
public static function getSingular(): iterable
{
yield from self::getDefault();

// http://nl.wikipedia.org/wiki/Plurale_tantum
yield new Pattern('hersenen');
yield new Pattern('ingewanden');
yield new Pattern('mazelen');
yield new Pattern('pokken');
yield new Pattern('waterpokken');
yield new Pattern('financiën');
yield new Pattern('activa');
yield new Pattern('passiva');
yield new Pattern('onkosten');
yield new Pattern('kosten');
yield new Pattern('bescheiden');
yield new Pattern('paperassen');
yield new Pattern('notulen');
yield new Pattern('Roma');
yield new Pattern('Sinti');
yield new Pattern('Inuit');
yield new Pattern('taliban');
yield new Pattern('illuminati');
yield new Pattern('aanstalten');
yield new Pattern('hurken');
yield new Pattern('lurven');
yield new Pattern('luren');
}

/**
* @return iterable<Pattern>
*/
public static function getPlural(): iterable
{
yield from self::getDefault();

// http://nl.wikipedia.org/wiki/Singulare_tantum
yield new Pattern('letterkunde');
yield new Pattern('muziek');
yield new Pattern('heelal');
yield new Pattern('vastgoed');
yield new Pattern('have');
yield new Pattern('nageslacht');
}

/**
* @return iterable<Pattern>
*/
private static function getDefault(): iterable
{
yield new Pattern('twitter');
}
}
2 changes: 2 additions & 0 deletions tests/Doctrine/Tests/Inflector/InflectorFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Doctrine\Inflector\InflectorFactory;
use Doctrine\Inflector\Language;
use Doctrine\Inflector\LanguageInflectorFactory;
use Doctrine\Inflector\Rules\Dutch\InflectorFactory as DutchInflectorFactory;
use Doctrine\Inflector\Rules\English\InflectorFactory as EnglishInflectorFactory;
use Doctrine\Inflector\Rules\French\InflectorFactory as FrenchInflectorFactory;
use Doctrine\Inflector\Rules\NorwegianBokmal\InflectorFactory as NorwegianBokmalInflectorFactory;
Expand Down Expand Up @@ -38,6 +39,7 @@ public function testCreateForLanguageWithCustomLanguage(string $expectedClass, s
*/
public static function provideLanguages(): Generator
{
yield 'Dutch' => [DutchInflectorFactory::class, Language::DUTCH];
yield 'English' => [EnglishInflectorFactory::class, Language::ENGLISH];
yield 'French' => [FrenchInflectorFactory::class, Language::FRENCH];
yield 'Norwegian Bokmal' => [NorwegianBokmalInflectorFactory::class, Language::NORWEGIAN_BOKMAL];
Expand Down
62 changes: 62 additions & 0 deletions tests/Doctrine/Tests/Inflector/Rules/Dutch/DutchFunctionalTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Inflector\Rules\Dutch;

use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Doctrine\Inflector\Language;
use Doctrine\Tests\Inflector\Rules\LanguageFunctionalTest;

class DutchFunctionalTest extends LanguageFunctionalTest
{
/**
* @return list<array{string, string}>
*/
public function dataSampleWords(): array
{
return [
['schip', 'schepen'],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need some extra tests here.

Suggested change
['schip', 'schepen'],
['schip', 'schepen'],
['idee', 'ideeën'],

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi you 2,

did swap my incorrectness and did add note for possible rule.

tests running again now.

Copy link

@TimoBakx TimoBakx Dec 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello! 👋

Please also add:

  • ['meer', 'meren'],
  • ['baas', 'bazen'],
  • ['oog', 'ogen'],
  • ['as', 'assen'],

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added but outcommented for now.

['stad', 'steden'],
['gelid', 'gelederen'],
greg0ire marked this conversation as resolved.
Show resolved Hide resolved
// @todo more
['weerman', 'weermannen'],
['ei', 'eieren'],
['rij', 'rijen'],
['mogelijkheid', 'mogelijkheden'],
['adres', 'adressen'],
['olie', 'oliën'],
['industrie', 'industrieën'],
['lid', 'leden'],
['smid', 'smeden'],
['kalf', 'kalveren'],
['lam', 'lammeren'],
['koe', 'koeien'],
['vlo', 'vlooien'],
['leerrede', 'leerredenen'],
['lende', 'lendenen'],
['genius', 'geniën'],
['aanbod', 'aanbiedingen'],
['dank', 'dankbetuigingen'],
['gedrag', 'gedragingen'],
['genot', 'genietingen'],
['lof', 'lofbetuigingen'],
['qaestrices', 'quaestrix'],
noud marked this conversation as resolved.
Show resolved Hide resolved
['matrices', 'matrix'],
noud marked this conversation as resolved.
Show resolved Hide resolved
['twitter', 'twitter'],
// @todo: multiplitudes array w/o sort order for multiple plural same possibilities
['epos', 'epen'],
// ['epos', 'epossen'],
// @todo: multiplitudes array w/o sort order for multiple plural different meenings
['beleg', 'belegeringen'], // @todo: meening: invest a city and then conquer it
// ['beleg', 'beleggen'], // @todo: meening: call a meeting
// ['beleg', 'belegjes'], // @todo: meening: the slices food on a sandwich
SenseException marked this conversation as resolved.
Show resolved Hide resolved
];
}

protected function createInflector(): Inflector
{
return InflectorFactory::createForLanguage(Language::DUTCH)->build();
}
}