From 086fb67b0740fd8d0de47078ea6c74dfaaaddb84 Mon Sep 17 00:00:00 2001 From: Denis Portnov Date: Thu, 28 Jun 2012 06:24:07 +0400 Subject: [PATCH 1/6] fixed HelperConfiguration class name --- src/View/Helper/DateFormat.php | 5 ++--- src/View/Helper/Translate.php | 1 + src/View/Helper/TranslatePlural.php | 1 + src/View/HelperConfiguration.php | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/View/Helper/DateFormat.php b/src/View/Helper/DateFormat.php index ffad8089..fd6a18ff 100644 --- a/src/View/Helper/DateFormat.php +++ b/src/View/Helper/DateFormat.php @@ -63,6 +63,7 @@ public function addFormatter($name, IntlDateFormatter $formatter) * @param DateTime|integer|array $date * @param string $formatterName * @return string + * @throws Exception\RuntimeException */ public function __invoke($date, $formatterName) { @@ -74,9 +75,7 @@ public function __invoke($date, $formatterName) } // DateTime support for IntlDateFormatter::format() was only added in 5.3.4 - if ($date instanceof DateTime - && version_compare(PHP_VERSION, '5.3.4', '<') - ) { + if ($date instanceof DateTime && version_compare(PHP_VERSION, '5.3.4', '<')) { $date = $date->getTimestamp(); } diff --git a/src/View/Helper/Translate.php b/src/View/Helper/Translate.php index 15c75f9c..59da76c6 100644 --- a/src/View/Helper/Translate.php +++ b/src/View/Helper/Translate.php @@ -62,6 +62,7 @@ public function setTranslator(Translator $translator) * @param string $textDomain * @param string $locale * @return string + * @throws Exception\RuntimeException */ public function __invoke($message, $textDomain = 'default', $locale = null) { diff --git a/src/View/Helper/TranslatePlural.php b/src/View/Helper/TranslatePlural.php index f7fca86b..188f1089 100644 --- a/src/View/Helper/TranslatePlural.php +++ b/src/View/Helper/TranslatePlural.php @@ -64,6 +64,7 @@ public function setTranslator(Translator $translator) * @param string $textDomain * @param string $locale * @return string + * @throws Exception\RuntimeException */ public function __invoke( $singular, diff --git a/src/View/HelperConfiguration.php b/src/View/HelperConfiguration.php index 272ecc68..fdb22768 100644 --- a/src/View/HelperConfiguration.php +++ b/src/View/HelperConfiguration.php @@ -33,7 +33,7 @@ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ -class HelperLoader implements ConfigurationInterface +class HelperConfiguration implements ConfigurationInterface { /** * @var array Pre-aliased view helpers From eb980dd7aef542e6f77a3b4fb4c1d9242671e413 Mon Sep 17 00:00:00 2001 From: Denis Portnov Date: Thu, 28 Jun 2012 19:51:04 +0400 Subject: [PATCH 2/6] CS fixes --- src/Exception/InvalidArgumentException.php | 5 +++-- src/Exception/OutOfBoundsException.php | 3 ++- src/Translator/Translator.php | 3 ++- src/View/Helper/DateFormat.php | 4 +++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index 912f3d6d..f497a756 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -28,5 +28,6 @@ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ -class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface -{} +class InvalidArgumentException extends \InvalidArgumentException implements + ExceptionInterface +{} \ No newline at end of file diff --git a/src/Exception/OutOfBoundsException.php b/src/Exception/OutOfBoundsException.php index d01888b8..ee50c2d1 100644 --- a/src/Exception/OutOfBoundsException.php +++ b/src/Exception/OutOfBoundsException.php @@ -28,5 +28,6 @@ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ -class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +class OutOfBoundsException extends \OutOfBoundsException implements + ExceptionInterface {} diff --git a/src/Translator/Translator.php b/src/Translator/Translator.php index a6e6e90a..40c12b26 100644 --- a/src/Translator/Translator.php +++ b/src/Translator/Translator.php @@ -373,7 +373,8 @@ protected function loadMessages($textDomain, $locale) // Try to load from pattern if (isset($this->patterns[$textDomain])) { foreach ($this->patterns[$textDomain] as $pattern) { - $filename = $pattern['baseDir'] . '/' . sprintf($pattern['pattern'], $locale); + $filename = $pattern['baseDir'] + . '/' . sprintf($pattern['pattern'], $locale); if (is_file($filename)) { $this->messages[$textDomain][$locale] = $this->getPluginManager() ->get($pattern['type']) diff --git a/src/View/Helper/DateFormat.php b/src/View/Helper/DateFormat.php index fd6a18ff..a870bcde 100644 --- a/src/View/Helper/DateFormat.php +++ b/src/View/Helper/DateFormat.php @@ -75,7 +75,9 @@ public function __invoke($date, $formatterName) } // DateTime support for IntlDateFormatter::format() was only added in 5.3.4 - if ($date instanceof DateTime && version_compare(PHP_VERSION, '5.3.4', '<')) { + if ($date instanceof DateTime + && version_compare(PHP_VERSION, '5.3.4', '<')) + { $date = $date->getTimestamp(); } From 45dae735e28f1ae063c34bc6de2a0b0027bf9e2e Mon Sep 17 00:00:00 2001 From: Denis Portnov Date: Fri, 29 Jun 2012 04:23:03 +0400 Subject: [PATCH 3/6] translator factory --- src/Translator/Translator.php | 100 +++++++++++++++++- test/Translator/TranslatorTest.php | 66 +++++++++++- test/Translator/_files/failed.mo | Bin 0 -> 420 bytes test/Translator/_files/failed.php | 22 ++++ .../_files/testarray/translation-en_US.php | 27 +++++ .../_files/testarray/translation-ja.php | 27 +++++ .../testmo/de_AT/LC_TEST/translation-de_DE.mo | Bin 0 -> 593 bytes .../en_GB/LC_OTHER/translation-en_US.mo | Bin 0 -> 928 bytes .../_files/translation_bigendian.mo | Bin 0 -> 420 bytes test/Translator/_files/translation_empty.mo | Bin 0 -> 484 bytes test/Translator/_files/translation_empty.php | 22 ++++ test/Translator/_files/translation_empty.po | 16 +++ test/Translator/_files/translation_en.mo | Bin 0 -> 928 bytes test/Translator/_files/translation_en.php | 32 ++++++ 14 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 test/Translator/_files/failed.mo create mode 100644 test/Translator/_files/failed.php create mode 100644 test/Translator/_files/testarray/translation-en_US.php create mode 100644 test/Translator/_files/testarray/translation-ja.php create mode 100644 test/Translator/_files/testmo/de_AT/LC_TEST/translation-de_DE.mo create mode 100644 test/Translator/_files/testmo/en_GB/LC_OTHER/translation-en_US.mo create mode 100644 test/Translator/_files/translation_bigendian.mo create mode 100644 test/Translator/_files/translation_empty.mo create mode 100644 test/Translator/_files/translation_empty.php create mode 100644 test/Translator/_files/translation_empty.po create mode 100644 test/Translator/_files/translation_en.mo create mode 100644 test/Translator/_files/translation_en.php diff --git a/src/Translator/Translator.php b/src/Translator/Translator.php index 40c12b26..862d4918 100644 --- a/src/Translator/Translator.php +++ b/src/Translator/Translator.php @@ -22,6 +22,9 @@ namespace Zend\I18n\Translator; use Locale; +use Traversable; +use Zend\Stdlib\ArrayUtils; +use Zend\Cache; use Zend\Cache\Storage\StorageInterface as CacheStorage; use Zend\I18n\Exception; @@ -85,6 +88,101 @@ class Translator */ protected $pluginManager; + /** + * Instantiate a translator + * + * @param array|Traversable $options + * @return Translator + * @throws Exception\InvalidArgumentException + */ + public static function factory($options) + { + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } elseif (!is_array($options)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects an array or Traversable object; received "%s"', + __METHOD__, + (is_object($options) ? get_class($options) : gettype($options)) + )); + } + + $translator = new static(); + + // locales + if (isset($options['locale'])) { + $translator->setLocale($options['locale']); + } + if (isset($options['fallback_locale'])) { + $translator->setFallbackLocale($options['fallback_locale']); + } + + // patterns + if (isset($options['translation_patterns'])) { + if (!is_array($options['translation_patterns'])) { + throw new Exception\InvalidArgumentException( + '"translation_patterns" should be an array' + ); + } + + $requiredKeys = array('type', 'base_dir', 'pattern'); + foreach ($options['translation_patterns'] as $pattern) { + foreach ($requiredKeys as $key) { + if (!isset($pattern[$key])) { + throw new Exception\InvalidArgumentException( + "'{$key}' is missing for translation pattern options" + ); + } + } + + $translator->addTranslationPattern( + $pattern['type'], + $pattern['base_dir'], + $pattern['pattern'], + isset($pattern['text_domain']) ? $pattern['text_domain'] : 'default' + ); + } + } + + // files + if (isset($options['translation_files'])) { + if (!is_array($options['translation_files'])) { + throw new Exception\InvalidArgumentException( + '"translation_files" should be an array' + ); + } + + $requiredKeys = array('type', 'filename'); + foreach ($options['translation_files'] as $file) { + foreach ($requiredKeys as $key) { + if (!isset($file[$key])) { + throw new Exception\InvalidArgumentException( + "'{$key}' is missing for translation file options" + ); + } + } + + $translator->addTranslationFile( + $file['type'], + $file['filename'], + isset($file['text_domain']) ? $file['text_domain'] : 'default', + isset($file['locale']) ? $file['locale'] : null + ); + } + } + + // cache + if (isset($options['cache'])) { + if ($options['cache'] instanceof CacheStorage) { + $translator->setCache($options['cache']); + } else { + $translator->setCache(Cache\StorageFactory::factory($options['cache'])); + } + } + + return $translator; + } + /** * Set the default locale. * @@ -341,7 +439,7 @@ public function addTranslationPattern( $this->patterns[$textDomain][] = array( 'type' => $type, - 'baseDir' => rtrim($baseDir . '/'), + 'baseDir' => rtrim($baseDir, '/'), 'pattern' => $pattern, ); diff --git a/test/Translator/TranslatorTest.php b/test/Translator/TranslatorTest.php index eca4447e..bf7e8585 100644 --- a/test/Translator/TranslatorTest.php +++ b/test/Translator/TranslatorTest.php @@ -6,20 +6,33 @@ use Locale; use Zend\I18n\Translator\Translator; use Zend\I18n\Translator\TextDomain; -use Zend\I18n\Translator\Translator\Loader\LoaderInterface; use ZendTest\I18n\Translator\TestAsset\Loader as TestLoader; class TranslatorTest extends TestCase { - protected $originalLocale; + /** + * @var Translator + */ protected $translator; + /** + * @var string + */ + protected $originalLocale; + + /** + * @var string + */ + protected $testFilesDir; + public function setUp() { $this->originalLocale = Locale::getDefault(); $this->translator = new Translator(); Locale::setDefault('en_EN'); + + $this->testFilesDir = __DIR__ . '/_files'; } public function tearDown() @@ -47,4 +60,53 @@ public function testTranslate() $this->assertEquals('bar', $this->translator->translate('foo')); } + + public function testFactoryCreatesTranslator() + { + $translator = Translator::factory(array( + 'locale' => 'de_DE', + 'patterns' => array( + array( + 'type' => 'phparray', + 'base_dir' => $this->testFilesDir . '/testarray', + 'pattern' => 'translation-%s.php' + ) + ), + 'files' => array( + array( + 'type' => 'phparray', + 'filename' => $this->testFilesDir . '/translation_en.php', + ) + ) + )); + + $this->assertInstanceOf('Zend\I18n\Translator\Translator', $translator); + $this->assertEquals('de_DE', $translator->getLocale()); + } + + public function testFactoryCreatesTranslatorWithCache() + { + $translator = Translator::factory(array( + 'locale' => 'de_DE', + 'patterns' => array( + array( + 'type' => 'phparray', + 'base_dir' => $this->testFilesDir . '/testarray', + 'pattern' => 'translation-%s.php' + ) + ), + 'files' => array( + array( + 'type' => 'phparray', + 'filename' => $this->testFilesDir . '/translation_en.php', + ) + ), + 'cache' => array( + 'adapter' => 'memory' + ) + )); + + $this->assertInstanceOf('Zend\I18n\Translator\Translator', $translator); + $this->assertInstanceOf('Zend\Cache\Storage\StorageInterface', $translator->getCache()); + } } diff --git a/test/Translator/_files/failed.mo b/test/Translator/_files/failed.mo new file mode 100644 index 0000000000000000000000000000000000000000..e057750ff2869c7922d9fab869c15a4841c4a1a1 GIT binary patch literal 420 zcmaKoO-lnY5QbB~4vQxbo;>6(v!+$bvVI`86$-7EvMP8orqgYfZBmk~AOFF-_<#Hz z{uXCzp*IH}-j~eq&Jf(%`(b~zNpe7(5ZlCnsOyQ?B_y#!7{(g=q!kg7{~)D)Xi?|&p(HIxj1sM8VPvEM`J!CfJ z(dagVj>BaNbY;Mlw}fbeNTAi?qM06xLqRVz2xENFb>zQu1L*@~P8wgzU|q@|Y@sx& z8NZRPGUWK#Yiw5jGM`Ib#>&nwqG5m7TVLIbMbxn-prN?;1>F_!EgUUMsm&=@GwD2r q^SkUClJ#7@c8U&qM%jrrdCHS#9U`{+DpeqRl%>t9f_we_#eM;>pKVhB literal 0 HcmV?d00001 diff --git a/test/Translator/_files/failed.php b/test/Translator/_files/failed.php new file mode 100644 index 00000000..be530bc2 --- /dev/null +++ b/test/Translator/_files/failed.php @@ -0,0 +1,22 @@ + 'Message 1 (en)', + 'Message 2' => 'Message 2 (en)', + 'Message 3' => 'Message 3 (en)', + 'Message 4' => 'Message 4 (en)', +); diff --git a/test/Translator/_files/testarray/translation-ja.php b/test/Translator/_files/testarray/translation-ja.php new file mode 100644 index 00000000..f95a0b7d --- /dev/null +++ b/test/Translator/_files/testarray/translation-ja.php @@ -0,0 +1,27 @@ + 'Message 1 (ja)', + 'Message 2' => 'Message 2 (ja)', + 'Message 3' => 'Message 3 (ja)', + 'Message 4' => 'Message 4 (ja)', +); diff --git a/test/Translator/_files/testmo/de_AT/LC_TEST/translation-de_DE.mo b/test/Translator/_files/testmo/de_AT/LC_TEST/translation-de_DE.mo new file mode 100644 index 0000000000000000000000000000000000000000..d3ff7c6b9457a067ca225e54e529ab110ad91ec4 GIT binary patch literal 593 zcmYLGO>fjN5Di}k!i_VB;Xp-I&0=RGHDoshu`E)hF04uf+`uH$tYKrXY^N(|cpT8ISGX5AXhrIPNeWF;d1o#u=lJFO2&P^_Rah|GE`L9rFXm^V=gY znR$8W!!!ghMbV19R}F#uud-^G_cp4Rw*oY&Um5XgttH=oJG=d`mf;L10;##deA zv9vkF>vttC9I3#5e60fIkO*;%LZUndIn9#U6c-&o3t;URV>z zaL*q*9xl*zc(&UrZDvqyl=BpdE?i)?_w{FAki(^^Y^_a`L-tjNc)jhM+Tt7QTAzd2 z4XH1tGZ&q_rZJz!%C} zD1V{q3$?pc(4VA6Oi178H!^axJESk9v58gzIBDI~#;>KS9LSkAj`nPv4C#Y3wsfEa zsPvx=Xvaw5Fi#%}Cj;;pYf#21ZvgiL&!eo)7zXKqJWjH6m$@coP0H)w`GMcqVH|T$ zg>`gbgt8OivC2OrA&rO8fd%R*WMNW{;@uBMyn*MMS8kd>2-Vi%lq>ht8}z- zQsJ5JXLva$Pu$(vL?o&O(O4KOU6{HNZ7y@!yGPQ{eHG~;c0GXRQQ|5^o9H8LrZxaI z%e!VnzXgR1x78ia!jQw(7G#p$4mpQ=zyRau_d@UNQ9G<>GOu85&fxU4D8US?VhzU{ zuiC7QvMZS1x8RCr+!B5HEnur3I~}< zXgI#fpl{_O1unUl3GFi00~SNKOIas5Wcv(Xa1bW)S+$Y>&W*V9kXf!>#l5i!y*H&$ zQq1Tzw~ajSB<*PrKS6u01DrVd| r=}&L7D~MNf?b@ldFwnx}N*4)@pOg>D@~cEC$pf!U(UjDw?=SKT|6gr` literal 0 HcmV?d00001 diff --git a/test/Translator/_files/translation_empty.mo b/test/Translator/_files/translation_empty.mo new file mode 100644 index 0000000000000000000000000000000000000000..28f7bc39531675e27221a694a7a6efdadff0d3c1 GIT binary patch literal 484 zcmYL_(MkeA6o!ks+RLuHI6+BL$7WW=b#*N=10hjtsmqwwY29#VWM(u2U!#}kp?a3i zD&>KnIY0CLhr{_fI{ezB+9w^8PDux(OHxq*>6<>A^cKv2jf{SmiNpQ`pQN#wRv|cP zU2fACsl2hAYvXWW(`^7;ki#61Y>Fg8NjnQ+>K5Knb3EIi9>qNIVg+xW`I*554k${DS) zq>|{o;|=\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Language-Team: \n" + diff --git a/test/Translator/_files/translation_en.mo b/test/Translator/_files/translation_en.mo new file mode 100644 index 0000000000000000000000000000000000000000..9f451f8ce567e3b4deaf356204d6b7aca6cbf9e2 GIT binary patch literal 928 zcmZWnU279T6dko{jUZUT2kFE0!O~DB&g`ZnY_e61Re}aX4cZ4mrklxTHJJ%Bv$aD1 zhHw53pM1-U{szGx;G5vHc=w};(SgG`XZGCL*}M1q#@YvhYaOwPc!ju%_=w2X7sPeM zSHv~MH^ddhkCK0t{JZ4!bA;SNy@5POP{a-NKhG0#6ZztW;+)NrcS;UQ?jk?N+#xcS z5-cNYSsenm$R$L!E*F&FJC_};-zF-CQEF7;QX|Qe`R8aX74+txj^u>&q_rZJz!%C} zD1V{q3$?pc(4VA6Oi178H!^axJESk9v58gzIBDI~#;>KS9LSkAj`nPv4C#Y3wsfEa zsPvx=Xvaw5Fi#%}Cj;;pYf#21ZvgiL&!eo)7zXKqJWjH6m$@coP0H)w`GMcqVH|T$ zg>`gbgt8OivC2OrA&rO8fd%R*WMNW{;@uBMyn*MMS8kd>2-Vi%lq>ht8}z- zQsJ5JXLva$Pu$(vL?o&O(O4KOU6{HNZ7y@!yGPQ{eHG~;c0GXRQQ|5^o9H8LrZxaI z%e!VnzXgR1x78ia!jQw(7G#p$4mpQ=zyRau_d@UNQ9G<>GOu85&fxU4D8US?VhzU{ zuiC 'Message 1 (en)', + 'Message 2' => 'Message 2 (en)', + 'Message 3' => 'Message 3 (en)', + 'Message 4' => 'Message 4 (en)', + 'Cooking furniture' => 'Küchen Möbel (en)', + 'Küchen Möbel' => 'Cooking furniture (en)', + '' => array( + 'plural_forms' => 'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);' + ), +); From 38aa29c13187ba107eda57ab2156e59dc96d47be Mon Sep 17 00:00:00 2001 From: Denis Portnov Date: Fri, 29 Jun 2012 06:31:45 +0400 Subject: [PATCH 4/6] fixed gettext endianes, tests for PhpArray and Gettext --- ...ntimeEception.php => RuntimeException.php} | 0 src/Translator/Loader/Gettext.php | 8 +- src/Translator/Translator.php | 2 +- test/Translator/Loader/GettextTest.php | 82 +++++++++++++++++ test/Translator/Loader/PhpArrayTest.php | 83 ++++++++++++++++++ ...anslation-ja.php => translation-de_DE.php} | 8 +- .../{de_AT/LC_TEST => }/translation-de_DE.mo | Bin .../{en_GB/LC_OTHER => }/translation-en_US.mo | Bin test/Translator/_files/translation_en.po | 48 ++++++++++ 9 files changed, 223 insertions(+), 8 deletions(-) rename src/Exception/{RuntimeEception.php => RuntimeException.php} (100%) create mode 100644 test/Translator/Loader/GettextTest.php create mode 100644 test/Translator/Loader/PhpArrayTest.php rename test/Translator/_files/testarray/{translation-ja.php => translation-de_DE.php} (82%) rename test/Translator/_files/testmo/{de_AT/LC_TEST => }/translation-de_DE.mo (100%) rename test/Translator/_files/testmo/{en_GB/LC_OTHER => }/translation-en_US.mo (100%) create mode 100644 test/Translator/_files/translation_en.po diff --git a/src/Exception/RuntimeEception.php b/src/Exception/RuntimeException.php similarity index 100% rename from src/Exception/RuntimeEception.php rename to src/Exception/RuntimeException.php diff --git a/src/Translator/Loader/Gettext.php b/src/Translator/Loader/Gettext.php index 82ebf878..ee27cd43 100644 --- a/src/Translator/Loader/Gettext.php +++ b/src/Translator/Loader/Gettext.php @@ -73,6 +73,10 @@ public function load($filename, $locale) // Verify magic number $magic = fread($this->file, 4); + // 32-bit machine is little-endian + if (PHP_INT_SIZE < 8) { + $magic = strrev($magic); + } if ($magic === "\x95\x04\x12\xde") { $this->littleEndian = true; @@ -145,10 +149,10 @@ public function load($filename, $locale) // Read header entries if (array_key_exists('', $textDomain)) { - $rawHeaders = explode("\n", $textDomain['']); + $rawHeaders = explode("\n", trim($textDomain[''])); foreach ($rawHeaders as $rawHeader) { - list($header, $content) = explode(':', $rawHeader, 1); + list($header, $content) = explode(':', $rawHeader, 2); if (trim(strtolower($header)) === 'plural-forms') { $textDomain->setPluralRule(PluralRule::fromString($content)); diff --git a/src/Translator/Translator.php b/src/Translator/Translator.php index 862d4918..7d6ed7c3 100644 --- a/src/Translator/Translator.php +++ b/src/Translator/Translator.php @@ -382,7 +382,7 @@ protected function getTranslatedMessage( $this->loadMessages($textDomain, $locale); } - if (!array_key_exists($message, $this->messages[$textDomain][$locale])) { + if (!isset($this->messages[$textDomain][$locale][$message])) { return null; } diff --git a/test/Translator/Loader/GettextTest.php b/test/Translator/Loader/GettextTest.php new file mode 100644 index 00000000..f14a94bc --- /dev/null +++ b/test/Translator/Loader/GettextTest.php @@ -0,0 +1,82 @@ +originalLocale = Locale::getDefault(); + $this->translator = new Translator(); + + Locale::setDefault('en_EN'); + + $this->testFilesDir = realpath(__DIR__ . '/../_files'); + } + + public function tearDown() + { + Locale::setDefault($this->originalLocale); + } + + public function testLoaderFailsToLoadMissingFile() + { + $loader = new GettextLoader(); + $this->setExpectedException('Zend\I18n\Exception\InvalidArgumentException', 'Could not open file'); + $loader->load('missing', 'en_EN'); + } + + public function testLoaderFailsToLoadBadFile() + { + $loader = new GettextLoader(); + $this->setExpectedException('Zend\I18n\Exception\InvalidArgumentException', + 'is not a valid gettext file'); + $loader->load($this->testFilesDir . '/failed.mo', 'en_EN'); + } + + public function testLoaderLoadsEmptyFile() + { + $loader = new GettextLoader(); + $textDomain = $loader->load($this->testFilesDir . '/translation_empty.mo', 'en_EN'); + $this->assertInstanceOf('Zend\I18n\Translator\TextDomain', $textDomain); + } + + public function testTranslatorAddsFile() + { + $this->translator->addTranslationFile('gettext', $this->testFilesDir . '/translation_en.mo'); + + $this->assertEquals('Message 1 (en)', $this->translator->translate('Message 1')); + $this->assertEquals('Message 6', $this->translator->translate('Message 6')); + } + + public function testTranslatorAddsFileToTextDomain() + { + $this->translator->addTranslationFile('gettext', $this->testFilesDir . '/translation_en.mo', 'user'); + + $this->assertEquals('Message 2 (en)', $this->translator->translate('Message 2', 'user')); + } + + public function testTranslatorAddsPattern() + { + $this->translator->addTranslationPattern( + 'gettext', + $this->testFilesDir . '/testmo', + 'translation-%s.mo' + ); + + $this->assertEquals('Message 1 (en)', $this->translator->translate('Message 1', 'default', 'en_US')); + $this->assertEquals('Nachricht 1', $this->translator->translate('Message 1', 'default', 'de_DE')); + } +} diff --git a/test/Translator/Loader/PhpArrayTest.php b/test/Translator/Loader/PhpArrayTest.php new file mode 100644 index 00000000..836ce4de --- /dev/null +++ b/test/Translator/Loader/PhpArrayTest.php @@ -0,0 +1,83 @@ +originalLocale = Locale::getDefault(); + $this->translator = new Translator(); + + Locale::setDefault('en_EN'); + + $this->testFilesDir = realpath(__DIR__ . '/../_files'); + } + + public function tearDown() + { + Locale::setDefault($this->originalLocale); + } + + public function testLoaderFailsToLoadMissingFile() + { + $loader = new PhpArrayLoader(); + $this->setExpectedException('Zend\I18n\Exception\InvalidArgumentException', 'Could not open file'); + $loader->load('missing', 'en_EN'); + } + + public function testLoaderFailsToLoadNonArray() + { + $loader = new PhpArrayLoader(); + $this->setExpectedException('Zend\I18n\Exception\InvalidArgumentException', + 'Expected an array, but received'); + $loader->load($this->testFilesDir . '/failed.php', 'en_EN'); + } + + public function testLoaderLoadsEmptyArray() + { + $loader = new PhpArrayLoader(); + $textDomain = $loader->load($this->testFilesDir . '/translation_empty.php', 'en_EN'); + $this->assertInstanceOf('Zend\I18n\Translator\TextDomain', $textDomain); + } + + public function testTranslatorAddsFile() + { + $this->translator->addTranslationFile('phparray', $this->testFilesDir . '/translation_en.php'); + + $this->assertEquals('Message 1 (en)', $this->translator->translate('Message 1')); + $this->assertEquals('Message 6', $this->translator->translate('Message 6')); + } + + public function testTranslatorAddsFileToTextDomain() + { + $this->translator->addTranslationFile('phparray', $this->testFilesDir . '/translation_en.php', 'user'); + + $this->assertEquals('Message 2 (en)', $this->translator->translate('Message 2', 'user')); + } + + public function testTranslatorAddsPattern() + { + $this->translator->addTranslationPattern( + 'phparray', + $this->testFilesDir . '/testarray', + 'translation-%s.php' + ); + + $this->assertEquals('Message 1 (en)', $this->translator->translate('Message 1', 'default', 'en_US')); + $this->assertEquals('Nachricht 1', $this->translator->translate('Message 1', 'default', 'de_DE')); + } + +} diff --git a/test/Translator/_files/testarray/translation-ja.php b/test/Translator/_files/testarray/translation-de_DE.php similarity index 82% rename from test/Translator/_files/testarray/translation-ja.php rename to test/Translator/_files/testarray/translation-de_DE.php index f95a0b7d..55683aa6 100644 --- a/test/Translator/_files/testarray/translation-ja.php +++ b/test/Translator/_files/testarray/translation-de_DE.php @@ -20,8 +20,6 @@ */ return array( - 'Message 1' => 'Message 1 (ja)', - 'Message 2' => 'Message 2 (ja)', - 'Message 3' => 'Message 3 (ja)', - 'Message 4' => 'Message 4 (ja)', -); + 'Message 1' => 'Nachricht 1', + 'Message 8' => 'Nachricht 8' +); \ No newline at end of file diff --git a/test/Translator/_files/testmo/de_AT/LC_TEST/translation-de_DE.mo b/test/Translator/_files/testmo/translation-de_DE.mo similarity index 100% rename from test/Translator/_files/testmo/de_AT/LC_TEST/translation-de_DE.mo rename to test/Translator/_files/testmo/translation-de_DE.mo diff --git a/test/Translator/_files/testmo/en_GB/LC_OTHER/translation-en_US.mo b/test/Translator/_files/testmo/translation-en_US.mo similarity index 100% rename from test/Translator/_files/testmo/en_GB/LC_OTHER/translation-en_US.mo rename to test/Translator/_files/testmo/translation-en_US.mo diff --git a/test/Translator/_files/translation_en.po b/test/Translator/_files/translation_en.po new file mode 100644 index 00000000..0d57cb97 --- /dev/null +++ b/test/Translator/_files/translation_en.po @@ -0,0 +1,48 @@ +# translation of testmsg_en.po into English +# $Id: testmsg_en.po,v 1.0 2007/01/16 12:05:55 tokul Exp $ +# +msgid "" +msgstr "" +"Project-Id-Version: testmsg_en\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-01-17 13:22-0400\n" +"PO-Revision-Date: 2008-08-14 23:37+0100\n" +"Last-Translator: Thomas Weidner \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Language-Team: \n" +"X-Poedit-SourceCharset: utf-8\n" + +#: test.php:0 +msgid "Message 1" +msgstr "Message 1 (en)" + +#: test.php:0 +msgid "Message 2" +msgstr "Message 2 (en)" + +#: test.php:0 +msgid "Message 3" +msgstr "Message 3 (en)" + +#: test.php:0 +msgid "Message 4" +msgstr "Message 4 (en)" + +#: test.php:0 +msgid "Message 5" +msgid_plural "Message 5 Plural" +msgstr[0] "Message 5 (en) Plural 0" +msgstr[1] "Message 5 (en) Plural 1" +msgstr[2] "Message 5 (en) Plural 2" + +#: test.php:0 +msgid "Cooking furniture" +msgstr "Küchen Möbel (en)" + +#: test.php:0 +msgid "Küchen Möbel" +msgstr "Cooking furniture (en)" + From 7ebbd12b33d2c082ed7962639edf16619911103a Mon Sep 17 00:00:00 2001 From: Denis Portnov Date: Fri, 29 Jun 2012 22:40:01 +0400 Subject: [PATCH 5/6] plural translation tests --- src/Translator/Loader/Gettext.php | 3 +- src/Translator/Translator.php | 12 ++-- test/Translator/Loader/GettextTest.php | 67 +++++++++++++++++------ test/Translator/Loader/PhpArrayTest.php | 55 ++++++++++++++----- test/Translator/TranslatorTest.php | 48 +++++++--------- test/Translator/_files/translation_en.php | 11 +++- 6 files changed, 129 insertions(+), 67 deletions(-) diff --git a/src/Translator/Loader/Gettext.php b/src/Translator/Loader/Gettext.php index ee27cd43..6320c10a 100644 --- a/src/Translator/Loader/Gettext.php +++ b/src/Translator/Loader/Gettext.php @@ -56,7 +56,8 @@ class Gettext implements LoaderInterface * @see LoaderInterface::load() * @param string $filename * @param string $locale - * @return TextDomain|null + * @return TextDomain + * @throws Exception\InvalidArgumentException */ public function load($filename, $locale) { diff --git a/src/Translator/Translator.php b/src/Translator/Translator.php index 7d6ed7c3..89e50e4b 100644 --- a/src/Translator/Translator.php +++ b/src/Translator/Translator.php @@ -314,11 +314,11 @@ public function translate($message, $textDomain = 'default', $locale = null) /** * Translate a plural message. * - * @param string $singular - * @param string $plural - * @param int $number - * @param string $textDomain - * @param string $locale + * @param string $singular + * @param string $plural + * @param int $number + * @param string $textDomain + * @param string|null $locale * @return string * @throws Exception\OutOfBoundsException */ @@ -349,7 +349,7 @@ public function translatePlural( } $index = $this->messages[$textDomain][$locale] - ->pluralRule() + ->getPluralRule() ->evaluate($number); if (!isset($translation[$index])) { diff --git a/test/Translator/Loader/GettextTest.php b/test/Translator/Loader/GettextTest.php index f14a94bc..5d38c984 100644 --- a/test/Translator/Loader/GettextTest.php +++ b/test/Translator/Loader/GettextTest.php @@ -9,18 +9,12 @@ class GettextTest extends TestCase { - /** - * @var Translator - */ - protected $translator; protected $testFilesDir; protected $originalLocale; public function setUp() { $this->originalLocale = Locale::getDefault(); - $this->translator = new Translator(); - Locale::setDefault('en_EN'); $this->testFilesDir = realpath(__DIR__ . '/../_files'); @@ -49,34 +43,75 @@ public function testLoaderFailsToLoadBadFile() public function testLoaderLoadsEmptyFile() { $loader = new GettextLoader(); - $textDomain = $loader->load($this->testFilesDir . '/translation_empty.mo', 'en_EN'); - $this->assertInstanceOf('Zend\I18n\Translator\TextDomain', $textDomain); + $domain = $loader->load($this->testFilesDir . '/translation_empty.mo', 'en_EN'); + $this->assertInstanceOf('Zend\I18n\Translator\TextDomain', $domain); + } + + public function testLoaderLoadsBigEndianFile() + { + $loader = new GettextLoader(); + $domain = $loader->load($this->testFilesDir . '/translation_bigendian.mo', 'en_EN'); + $this->assertInstanceOf('Zend\I18n\Translator\TextDomain', $domain); } public function testTranslatorAddsFile() { - $this->translator->addTranslationFile('gettext', $this->testFilesDir . '/translation_en.mo'); + $translator = new Translator(); + $translator->addTranslationFile('gettext', $this->testFilesDir . '/translation_en.mo'); - $this->assertEquals('Message 1 (en)', $this->translator->translate('Message 1')); - $this->assertEquals('Message 6', $this->translator->translate('Message 6')); + $this->assertEquals('Message 1 (en)', $translator->translate('Message 1')); + $this->assertEquals('Message 6', $translator->translate('Message 6')); } public function testTranslatorAddsFileToTextDomain() { - $this->translator->addTranslationFile('gettext', $this->testFilesDir . '/translation_en.mo', 'user'); + $translator = new Translator(); + $translator->addTranslationFile('gettext', $this->testFilesDir . '/translation_en.mo', 'user'); - $this->assertEquals('Message 2 (en)', $this->translator->translate('Message 2', 'user')); + $this->assertEquals('Message 2 (en)', $translator->translate('Message 2', 'user')); } public function testTranslatorAddsPattern() { - $this->translator->addTranslationPattern( + $translator = new Translator(); + $translator->addTranslationPattern( 'gettext', $this->testFilesDir . '/testmo', 'translation-%s.mo' ); - $this->assertEquals('Message 1 (en)', $this->translator->translate('Message 1', 'default', 'en_US')); - $this->assertEquals('Nachricht 1', $this->translator->translate('Message 1', 'default', 'de_DE')); + $this->assertEquals('Message 1 (en)', $translator->translate('Message 1', 'default', 'en_US')); + $this->assertEquals('Nachricht 1', $translator->translate('Message 1', 'default', 'de_DE')); + } + + public function testLoaderLoadsPluralRules() + { + $loader = new GettextLoader(); + $domain = $loader->load($this->testFilesDir . '/translation_en.mo', 'en_EN'); + + $this->assertEquals(2, $domain->getPluralRule()->evaluate(0)); + $this->assertEquals(0, $domain->getPluralRule()->evaluate(1)); + $this->assertEquals(1, $domain->getPluralRule()->evaluate(2)); + $this->assertEquals(2, $domain->getPluralRule()->evaluate(10)); + } + + public function testTranslatorTranslatesPlurals() + { + $translator = new Translator(); + $translator->setLocale('en_EN'); + $translator->addTranslationFile( + 'gettext', + $this->testFilesDir . '/translation_en.mo', + 'default', + 'en_EN' + ); + + $pl0 = $translator->translatePlural('Message 5', 'Message 5 Plural', 1); + $pl1 = $translator->translatePlural('Message 5', 'Message 5 Plural', 2); + $pl2 = $translator->translatePlural('Message 5', 'Message 5 Plural', 10); + + $this->assertEquals('Message 5 (en) Plural 0', $pl0); + $this->assertEquals('Message 5 (en) Plural 1', $pl1); + $this->assertEquals('Message 5 (en) Plural 2', $pl2); } } diff --git a/test/Translator/Loader/PhpArrayTest.php b/test/Translator/Loader/PhpArrayTest.php index 836ce4de..346d6f08 100644 --- a/test/Translator/Loader/PhpArrayTest.php +++ b/test/Translator/Loader/PhpArrayTest.php @@ -9,18 +9,12 @@ class PhpArrayTest extends TestCase { - /** - * @var Translator - */ - protected $translator; protected $testFilesDir; protected $originalLocale; public function setUp() { $this->originalLocale = Locale::getDefault(); - $this->translator = new Translator(); - Locale::setDefault('en_EN'); $this->testFilesDir = realpath(__DIR__ . '/../_files'); @@ -55,29 +49,62 @@ public function testLoaderLoadsEmptyArray() public function testTranslatorAddsFile() { - $this->translator->addTranslationFile('phparray', $this->testFilesDir . '/translation_en.php'); + $translator = new Translator(); + $translator->addTranslationFile('phparray', $this->testFilesDir . '/translation_en.php'); - $this->assertEquals('Message 1 (en)', $this->translator->translate('Message 1')); - $this->assertEquals('Message 6', $this->translator->translate('Message 6')); + $this->assertEquals('Message 1 (en)', $translator->translate('Message 1')); + $this->assertEquals('Message 6', $translator->translate('Message 6')); } public function testTranslatorAddsFileToTextDomain() { - $this->translator->addTranslationFile('phparray', $this->testFilesDir . '/translation_en.php', 'user'); + $translator = new Translator(); + $translator->addTranslationFile('phparray', $this->testFilesDir . '/translation_en.php', 'user'); - $this->assertEquals('Message 2 (en)', $this->translator->translate('Message 2', 'user')); + $this->assertEquals('Message 2 (en)', $translator->translate('Message 2', 'user')); } public function testTranslatorAddsPattern() { - $this->translator->addTranslationPattern( + $translator = new Translator(); + $translator->addTranslationPattern( 'phparray', $this->testFilesDir . '/testarray', 'translation-%s.php' ); - $this->assertEquals('Message 1 (en)', $this->translator->translate('Message 1', 'default', 'en_US')); - $this->assertEquals('Nachricht 1', $this->translator->translate('Message 1', 'default', 'de_DE')); + $this->assertEquals('Message 1 (en)', $translator->translate('Message 1', 'default', 'en_US')); + $this->assertEquals('Nachricht 1', $translator->translate('Message 1', 'default', 'de_DE')); + } + + public function testLoaderLoadsPluralRules() + { + $loader = new PhpArrayLoader(); + $domain = $loader->load($this->testFilesDir . '/translation_en.php', 'en_EN'); + + $this->assertEquals(2, $domain->getPluralRule()->evaluate(0)); + $this->assertEquals(0, $domain->getPluralRule()->evaluate(1)); + $this->assertEquals(1, $domain->getPluralRule()->evaluate(2)); + $this->assertEquals(2, $domain->getPluralRule()->evaluate(10)); } + public function testTranslatorTranslatesPlurals() + { + $translator = new Translator(); + $translator->setLocale('en_EN'); + $translator->addTranslationFile( + 'phparray', + $this->testFilesDir . '/translation_en.php', + 'default', + 'en_EN' + ); + + $pl0 = $translator->translatePlural('Message 5', 'Message 5 Plural', 1); + $pl1 = $translator->translatePlural('Message 5', 'Message 5 Plural', 2); + $pl2 = $translator->translatePlural('Message 5', 'Message 5 Plural', 10); + + $this->assertEquals('Message 5 (en) Plural 0', $pl0); + $this->assertEquals('Message 5 (en) Plural 1', $pl1); + $this->assertEquals('Message 5 (en) Plural 2', $pl2); + } } diff --git a/test/Translator/TranslatorTest.php b/test/Translator/TranslatorTest.php index bf7e8585..43abbce9 100644 --- a/test/Translator/TranslatorTest.php +++ b/test/Translator/TranslatorTest.php @@ -40,27 +40,6 @@ public function tearDown() Locale::setDefault($this->originalLocale); } - public function testDefaultLocale() - { - $this->assertEquals('en_EN', $this->translator->getLocale()); - } - - public function testForcedLocale() - { - $this->translator->setLocale('de_DE'); - $this->assertEquals('de_DE', $this->translator->getLocale()); - } - - public function testTranslate() - { - $loader = new TestLoader(); - $loader->textDomain = new TextDomain(array('foo' => 'bar')); - $this->translator->getPluginManager()->setService('test', $loader); - $this->translator->addTranslationFile('test', null); - - $this->assertEquals('bar', $this->translator->translate('foo')); - } - public function testFactoryCreatesTranslator() { $translator = Translator::factory(array( @@ -95,12 +74,6 @@ public function testFactoryCreatesTranslatorWithCache() 'pattern' => 'translation-%s.php' ) ), - 'files' => array( - array( - 'type' => 'phparray', - 'filename' => $this->testFilesDir . '/translation_en.php', - ) - ), 'cache' => array( 'adapter' => 'memory' ) @@ -109,4 +82,25 @@ public function testFactoryCreatesTranslatorWithCache() $this->assertInstanceOf('Zend\I18n\Translator\Translator', $translator); $this->assertInstanceOf('Zend\Cache\Storage\StorageInterface', $translator->getCache()); } + + public function testDefaultLocale() + { + $this->assertEquals('en_EN', $this->translator->getLocale()); + } + + public function testForcedLocale() + { + $this->translator->setLocale('de_DE'); + $this->assertEquals('de_DE', $this->translator->getLocale()); + } + + public function testTranslate() + { + $loader = new TestLoader(); + $loader->textDomain = new TextDomain(array('foo' => 'bar')); + $this->translator->getPluginManager()->setService('test', $loader); + $this->translator->addTranslationFile('test', null); + + $this->assertEquals('bar', $this->translator->translate('foo')); + } } diff --git a/test/Translator/_files/translation_en.php b/test/Translator/_files/translation_en.php index 7a7beaab..e67a8e1c 100644 --- a/test/Translator/_files/translation_en.php +++ b/test/Translator/_files/translation_en.php @@ -20,13 +20,18 @@ */ return array( + '' => array( + 'plural_forms' => 'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);' + ), 'Message 1' => 'Message 1 (en)', 'Message 2' => 'Message 2 (en)', 'Message 3' => 'Message 3 (en)', 'Message 4' => 'Message 4 (en)', + 'Message 5' => array( + 0 => 'Message 5 (en) Plural 0', + 1 => 'Message 5 (en) Plural 1', + 2 => 'Message 5 (en) Plural 2' + ), 'Cooking furniture' => 'Küchen Möbel (en)', 'Küchen Möbel' => 'Cooking furniture (en)', - '' => array( - 'plural_forms' => 'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);' - ), ); From 00fc0f45cbd73391212a887b64e3a30e57662d7e Mon Sep 17 00:00:00 2001 From: Denis Portnov Date: Fri, 29 Jun 2012 23:32:31 +0400 Subject: [PATCH 6/6] better factory configuration --- src/Translator/Loader/Gettext.php | 10 +++------- src/Translator/Translator.php | 9 +++++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Translator/Loader/Gettext.php b/src/Translator/Loader/Gettext.php index 6320c10a..2b47e414 100644 --- a/src/Translator/Loader/Gettext.php +++ b/src/Translator/Loader/Gettext.php @@ -74,15 +74,11 @@ public function load($filename, $locale) // Verify magic number $magic = fread($this->file, 4); - // 32-bit machine is little-endian - if (PHP_INT_SIZE < 8) { - $magic = strrev($magic); - } - if ($magic === "\x95\x04\x12\xde") { - $this->littleEndian = true; - } elseif ($magic === "\xde\x12\x04\x95") { + if ($magic == "\x95\x04\x12\xde") { $this->littleEndian = false; + } elseif ($magic == "\xde\x12\x04\x95") { + $this->littleEndian = true; } else { fclose($this->file); throw new Exception\InvalidArgumentException(sprintf( diff --git a/src/Translator/Translator.php b/src/Translator/Translator.php index 89e50e4b..5e1452e4 100644 --- a/src/Translator/Translator.php +++ b/src/Translator/Translator.php @@ -111,10 +111,11 @@ public static function factory($options) // locales if (isset($options['locale'])) { - $translator->setLocale($options['locale']); - } - if (isset($options['fallback_locale'])) { - $translator->setFallbackLocale($options['fallback_locale']); + $locales = (array) $options['locale']; + $translator->setLocale(array_shift($locales)); + if (count($locales) > 0) { + $translator->setFallbackLocale(array_shift($locales)); + } } // patterns