From 8685a8bbde221aa821c83e7ecd6470b8cd1954fb Mon Sep 17 00:00:00 2001 From: abbadon1334 Date: Mon, 15 Jul 2019 05:50:19 +0300 Subject: [PATCH 1/3] add Plural Interval --- .../Exception/TranslationSyntaxError.php | 11 ++ src/I18Next/Locale/Translations.php | 79 +++++++++++++ tests/Translator_CasePluralInterval_Test.php | 107 ++++++++++++++++++ tests/data/locales/en/key.json | 3 +- tests/data/utils/cases.php | 3 + 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/I18Next/Exception/TranslationSyntaxError.php create mode 100755 tests/Translator_CasePluralInterval_Test.php diff --git a/src/I18Next/Exception/TranslationSyntaxError.php b/src/I18Next/Exception/TranslationSyntaxError.php new file mode 100644 index 0000000..010a7b4 --- /dev/null +++ b/src/I18Next/Exception/TranslationSyntaxError.php @@ -0,0 +1,11 @@ +afterReadProcessForKeyCounters(); $this->afterReadProcessForKeyDeepInline(); + $this->afterReadAddNamespaceIfNeeded($configs, $fileInfo->getBasename('.'.$this->loader_format_ext)); // always reset config after every load @@ -105,6 +107,10 @@ private function afterReadProcessForKeyCounters(): void if ('plural' === $key_plural_definition || is_numeric($key_plural_definition)) { $this->processForCounterKey($key_plural_definition, $key, $value); } + + if ('interval' === $key_plural_definition) { + $this->processForIntervalKey($key_plural_definition, $key, $value); + } } } @@ -132,6 +138,79 @@ private function processForCounterKey(string $key_plural_definition, string $key $this->setConfig($cleared_key.'/'.(string) $counter, $value); } + + private function processForIntervalKey(string $key_plural_definition, string $key, string $value): void + { + $cleared_key = substr( + $key, + 0, + (strlen($key_plural_definition) + 1 /* the extra undescore before the plural_definition */) * -1 + ); + + $check_last = substr($value,-1) === ';'; + + if(!$check_last) + { + throw new TranslationSyntaxError([ + 'Interval declaration must end with ";" (' . $key .' => ' . $value . ')', + 'key' => $key, + 'value' => $value + ]); + } + + $count_intervals = count(explode(';', trim($value,';'))); + + $re = '/\((\S*)\)\{(.[^\}]*)\}/m'; + //$str = '(1){one item};(2-7){a few items};(7-inf){a lot of items};'; + + preg_match_all($re, $value, $matches, PREG_SET_ORDER, 0); + + if(count($matches) !== $count_intervals) + { + throw new TranslationSyntaxError([ + 'Interval declaration syntax error (' . $key .' => ' . $value . ')', + 'key' => $key, + 'value' => $value + ]); + } + + foreach($matches as $match) + { + if(count($match) < 3) + { + throw new TranslationSyntaxError([ + 'Interval value syntax incorrect : ' . $value, + 'key' => $key, + 'value' => $value, + 'matches' => $matches, + 'error_match' => $match + ]); + } + + $interval = explode('-', $match[1]); + + $interval_start = $interval[0]; + $interval_end = $interval[1] ?? $interval[0]; + if($interval_end === "inf") + { + $interval_end = $interval_start; + } + + $translation = $match[2]; + + if($interval_start === $interval_end) + { + $this->setConfig($cleared_key.'/'.(string) $interval_start, $translation); + continue; + } + + for($i = $interval_start; $i < $interval_end;$i++) + { + $this->setConfig($cleared_key.'/'.(string) $i, $translation); + } + } + } + private function afterReadProcessForKeyDeepInline(): void { $filtered = array_filter($this->config, function ($key) { diff --git a/tests/Translator_CasePluralInterval_Test.php b/tests/Translator_CasePluralInterval_Test.php new file mode 100755 index 0000000..930161a --- /dev/null +++ b/tests/Translator_CasePluralInterval_Test.php @@ -0,0 +1,107 @@ +setupTranslatorLanguages('en'); + + $result = $this->translator->_('key4'); + $this->assertEquals('one item', $result); + } + + public function testCount0() + { + $this->setupTranslatorLanguages('en'); + + $result = $this->translator->_('key4', ['count' => 0]); + $this->assertEquals('key4', $result); + } + + public function testCount1() + { + $this->setupTranslatorLanguages('en'); + + $result = $this->translator->_('key4', ['count' => 1]); + $this->assertEquals('one item', $result); + } + + public function testCount3() + { + $this->setupTranslatorLanguages('en'); + + $result = $this->translator->_('key4', ['count' => 3]); + $this->assertEquals('a few items', $result); + } + + public function testCount8() + { + $this->setupTranslatorLanguages('en'); + + $result = $this->translator->_('key4', ['count' => 8]); + $this->assertEquals('a lot of items', $result); + } + + public function testException1() + { + $this->expectException(TranslationSyntaxError::class); + + $path = '/tmp/locale_exception'; + @mkdir($path . '/en', 0777, true); + + file_put_contents( + $path . '/en/exception.json', + json_encode(["key4_interval" => "(1){one item};2-7){a few items};(7-inf){a lot of items};"]) + ); + + $this->translator = new Translator(); + $this->translator->setTranslationsPath($path); + $this->translator->setLanguagePrimary('en'); + } + + public function testException2() + { + $this->expectException(TranslationSyntaxError::class); + + $path = '/tmp/locale_exception'; + @mkdir($path . '/en', 0777, true); + + file_put_contents( + $path . '/en/exception.json', + json_encode(["key4_interval" => "(1){one item};(2-7){a few items};(7-inf){a lot of items}"]) + ); + + $this->translator = new Translator(); + $this->translator->setTranslationsPath($path); + $this->translator->setLanguagePrimary('en'); + } + + public function testException3() + { + $this->expectException(TranslationSyntaxError::class); + + $path = '/tmp/locale_exception'; + @mkdir($path . '/en', 0777, true); + + file_put_contents( + $path . '/en/exception.json', + json_encode(["key4_interval" => "(1){one item};"]) + ); + + $this->translator = new Translator(); + $this->translator->setTranslationsPath($path); + $this->translator->setLanguagePrimary('en'); + } +} diff --git a/tests/data/locales/en/key.json b/tests/data/locales/en/key.json index 8a8ab8d..ca835c2 100644 --- a/tests/data/locales/en/key.json +++ b/tests/data/locales/en/key.json @@ -23,5 +23,6 @@ "key_4": "many", "key_5": "other", "key_nesting_interpolate1": "hello world", - "key_nesting_interpolate2": "say {{val}}" + "key_nesting_interpolate2": "say {{val}}", + "key4_interval": "(1){one item};(2-7){a few items};(7-inf){a lot of items};" } \ No newline at end of file diff --git a/tests/data/utils/cases.php b/tests/data/utils/cases.php index 69559b1..36e5b97 100644 --- a/tests/data/utils/cases.php +++ b/tests/data/utils/cases.php @@ -131,6 +131,9 @@ 'en' => 'user : {{user.first_name}} {{user.last_name}} with email : {{address.email}}', 'it' => 'utente : {{user.first_name}} {{user.last_name}} con email : {{address.email}}', ], + "key4_interval" => [ + "en" => "(1){one item};(2-7){a few items};(7-inf){a lot of items};" + ] ]; $path_def = []; From 3f4355573f3360b946e09d051c5858f2215b2c7c Mon Sep 17 00:00:00 2001 From: abbadon1334 Date: Mon, 15 Jul 2019 05:51:24 +0300 Subject: [PATCH 2/3] removed exception if interval is defined with one rule only --- tests/Translator_CasePluralInterval_Test.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/Translator_CasePluralInterval_Test.php b/tests/Translator_CasePluralInterval_Test.php index 930161a..f5e69a3 100755 --- a/tests/Translator_CasePluralInterval_Test.php +++ b/tests/Translator_CasePluralInterval_Test.php @@ -87,21 +87,4 @@ public function testException2() $this->translator->setTranslationsPath($path); $this->translator->setLanguagePrimary('en'); } - - public function testException3() - { - $this->expectException(TranslationSyntaxError::class); - - $path = '/tmp/locale_exception'; - @mkdir($path . '/en', 0777, true); - - file_put_contents( - $path . '/en/exception.json', - json_encode(["key4_interval" => "(1){one item};"]) - ); - - $this->translator = new Translator(); - $this->translator->setTranslationsPath($path); - $this->translator->setLanguagePrimary('en'); - } } From 415cff3c0957db87888ddcac3359c388e3be4df8 Mon Sep 17 00:00:00 2001 From: Francesco Danti Date: Mon, 15 Jul 2019 03:36:41 +0000 Subject: [PATCH 3/3] Apply fixes from StyleCI --- src/I18Next/Locale/Processor.php | 3 +- src/I18Next/Locale/Translations.php | 42 ++++++++------------ tests/Translator_Atk4Trait_Test.php | 5 +-- tests/Translator_CasePluralInterval_Test.php | 15 ++++--- tests/data/utils/cases.php | 6 +-- 5 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/I18Next/Locale/Processor.php b/src/I18Next/Locale/Processor.php index e0d644c..1af8ca8 100644 --- a/src/I18Next/Locale/Processor.php +++ b/src/I18Next/Locale/Processor.php @@ -51,8 +51,7 @@ public function process(string $key, ?array $parameters = null, ?string $context $found_key = $this->processorValue->processValue($found_key, $parameters); - if ($found_key !== $key) - { + if ($found_key !== $key) { return $found_key; } diff --git a/src/I18Next/Locale/Translations.php b/src/I18Next/Locale/Translations.php index 3c2a71c..768cfea 100644 --- a/src/I18Next/Locale/Translations.php +++ b/src/I18Next/Locale/Translations.php @@ -138,7 +138,6 @@ private function processForCounterKey(string $key_plural_definition, string $key $this->setConfig($cleared_key.'/'.(string) $counter, $value); } - private function processForIntervalKey(string $key_plural_definition, string $key, string $value): void { $cleared_key = substr( @@ -147,65 +146,58 @@ private function processForIntervalKey(string $key_plural_definition, string $ke (strlen($key_plural_definition) + 1 /* the extra undescore before the plural_definition */) * -1 ); - $check_last = substr($value,-1) === ';'; + $check_last = substr($value, -1) === ';'; - if(!$check_last) - { + if (! $check_last) { throw new TranslationSyntaxError([ - 'Interval declaration must end with ";" (' . $key .' => ' . $value . ')', + 'Interval declaration must end with ";" ('.$key.' => '.$value.')', 'key' => $key, - 'value' => $value + 'value' => $value, ]); } - $count_intervals = count(explode(';', trim($value,';'))); + $count_intervals = count(explode(';', trim($value, ';'))); $re = '/\((\S*)\)\{(.[^\}]*)\}/m'; //$str = '(1){one item};(2-7){a few items};(7-inf){a lot of items};'; preg_match_all($re, $value, $matches, PREG_SET_ORDER, 0); - if(count($matches) !== $count_intervals) - { + if (count($matches) !== $count_intervals) { throw new TranslationSyntaxError([ - 'Interval declaration syntax error (' . $key .' => ' . $value . ')', + 'Interval declaration syntax error ('.$key.' => '.$value.')', 'key' => $key, - 'value' => $value + 'value' => $value, ]); } - foreach($matches as $match) - { - if(count($match) < 3) - { + foreach ($matches as $match) { + if (count($match) < 3) { throw new TranslationSyntaxError([ - 'Interval value syntax incorrect : ' . $value, + 'Interval value syntax incorrect : '.$value, 'key' => $key, 'value' => $value, 'matches' => $matches, - 'error_match' => $match + 'error_match' => $match, ]); } - $interval = explode('-', $match[1]); + $interval = explode('-', $match[1]); $interval_start = $interval[0]; - $interval_end = $interval[1] ?? $interval[0]; - if($interval_end === "inf") - { + $interval_end = $interval[1] ?? $interval[0]; + if ($interval_end === 'inf') { $interval_end = $interval_start; } $translation = $match[2]; - if($interval_start === $interval_end) - { + if ($interval_start === $interval_end) { $this->setConfig($cleared_key.'/'.(string) $interval_start, $translation); continue; } - for($i = $interval_start; $i < $interval_end;$i++) - { + for ($i = $interval_start; $i < $interval_end; $i++) { $this->setConfig($cleared_key.'/'.(string) $i, $translation); } } diff --git a/tests/Translator_Atk4Trait_Test.php b/tests/Translator_Atk4Trait_Test.php index 5bfa778..33389c6 100755 --- a/tests/Translator_Atk4Trait_Test.php +++ b/tests/Translator_Atk4Trait_Test.php @@ -22,12 +22,12 @@ public function testBase() $app->add($child = new ATK4ChildMock()); - $result = $child->_('friend', NULL, NULL, 'en'); + $result = $child->_('friend', null, null, 'en'); $this->assertEquals('A friend', $result); $this->translator->addLanguage('ro'); - $result = $child->_('friend', NULL, NULL, 'ro'); + $result = $child->_('friend', null, null, 'ro'); $this->assertEquals('Un prieten', $result); } @@ -47,7 +47,6 @@ public function testDirect() $result = $app->_('Ho trovato, $t(friend)'); $this->assertEquals('Ho trovato, Un conoscente', $result); - } } diff --git a/tests/Translator_CasePluralInterval_Test.php b/tests/Translator_CasePluralInterval_Test.php index f5e69a3..c82fc2b 100755 --- a/tests/Translator_CasePluralInterval_Test.php +++ b/tests/Translator_CasePluralInterval_Test.php @@ -9,11 +9,10 @@ class Translator_CasePluralInterval_Test extends TranslatorBaseCase { /** * Testing this : - * "key4_interval": "(1){one item};(2-7){a few items};(7-inf){a lot of items};" + * "key4_interval": "(1){one item};(2-7){a few items};(7-inf){a lot of items};". * * "key4_interval": "(1){one item};(2-7){a few items};(7-inf){a lot of items};" */ - public function testNoCount() { $this->setupTranslatorLanguages('en'); @@ -59,11 +58,11 @@ public function testException1() $this->expectException(TranslationSyntaxError::class); $path = '/tmp/locale_exception'; - @mkdir($path . '/en', 0777, true); + @mkdir($path.'/en', 0777, true); file_put_contents( - $path . '/en/exception.json', - json_encode(["key4_interval" => "(1){one item};2-7){a few items};(7-inf){a lot of items};"]) + $path.'/en/exception.json', + json_encode(['key4_interval' => '(1){one item};2-7){a few items};(7-inf){a lot of items};']) ); $this->translator = new Translator(); @@ -76,11 +75,11 @@ public function testException2() $this->expectException(TranslationSyntaxError::class); $path = '/tmp/locale_exception'; - @mkdir($path . '/en', 0777, true); + @mkdir($path.'/en', 0777, true); file_put_contents( - $path . '/en/exception.json', - json_encode(["key4_interval" => "(1){one item};(2-7){a few items};(7-inf){a lot of items}"]) + $path.'/en/exception.json', + json_encode(['key4_interval' => '(1){one item};(2-7){a few items};(7-inf){a lot of items}']) ); $this->translator = new Translator(); diff --git a/tests/data/utils/cases.php b/tests/data/utils/cases.php index 36e5b97..d6ec05f 100644 --- a/tests/data/utils/cases.php +++ b/tests/data/utils/cases.php @@ -131,9 +131,9 @@ 'en' => 'user : {{user.first_name}} {{user.last_name}} with email : {{address.email}}', 'it' => 'utente : {{user.first_name}} {{user.last_name}} con email : {{address.email}}', ], - "key4_interval" => [ - "en" => "(1){one item};(2-7){a few items};(7-inf){a lot of items};" - ] + 'key4_interval' => [ + 'en' => '(1){one item};(2-7){a few items};(7-inf){a lot of items};', + ], ]; $path_def = [];