diff --git a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php index 723560f53d2fcab6985613673eb9d4eb4338398a..e57a1260ad0bc8c5a8ad4095194fbd82b44b539a 100644 --- a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php +++ b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php @@ -89,28 +89,35 @@ trait TranslatorAwareTrait /** * Translate a string (or string-castable object) * - * @param string|object $str String to translate - * @param array $tokens Tokens to inject into the translated string - * @param string $default Default value to use if no translation is found - * (null for no default). + * @param string|object|array $target String to translate or an array of text + * domain and string to translate + * @param array $tokens Tokens to inject into the translated + * string + * @param string $default Default value to use if no translation is + * found (null for no default). * * @return string */ - public function translate($str, $tokens = [], $default = null) + public function translate($target, $tokens = [], $default = null) { + // Figure out the text domain for the string: + list($domain, $str) = $this->extractTextDomain($target); + // Special case: deal with objects with a designated display value: if ($str instanceof \VuFind\I18n\TranslatableStringInterface) { - $translated = $this->translateString((string)$str, $tokens, $default); + // On this pass, don't use the $default, since we want to fail over + // to getDisplayString before giving up: + $translated = $this + ->translateString((string)$str, $tokens, null, $domain); if ($translated !== (string)$str) { return $translated; } - return $this->translateString( - $str->getDisplayString(), $tokens, $default - ); + // Override $domain/$str using getDisplayString() before proceeding: + list($domain, $str) = $this->extractTextDomain($str->getDisplayString()); } // Default case: deal with ordinary strings (or string-castable objects): - return $this->translateString((string)$str, $tokens, $default); + return $this->translateString((string)$str, $tokens, $default, $domain); } /** @@ -118,15 +125,15 @@ trait TranslatorAwareTrait * * @param string $str String to translate * @param array $tokens Tokens to inject into the translated string - * @param string $default Default value to use if no translation is found (null - * for no default). + * @param string $default Default value to use if no translation is found + * (null for no default). + * @param string $domain Text domain (omit for default) * * @return string */ - protected function translateString($str, $tokens = [], $default = null) - { - // Figure out the text domain for the string: - list($domain, $str) = $this->extractTextDomain($str); + protected function translateString($str, $tokens = [], $default = null, + $domain = 'default' + ) { $msg = (null === $this->translator) ? $str : $this->translator->translate($str, $domain); @@ -153,16 +160,27 @@ trait TranslatorAwareTrait * Given a translation string with or without a text domain, return an * array with the raw string and the text domain separated. * - * @param string $str String to parse + * @param string|object|array $target String to translate or an array of text + * domain and string to translate * * @return array */ - protected function extractTextDomain($str) + protected function extractTextDomain($target) { - $parts = explode('::', $str); + $parts = is_array($target) ? $target : explode('::', $target); + if (count($parts) < 1 || count($parts) > 2) { + throw new \Exception('Unexpected value sent to translator!'); + } if (count($parts) == 2) { + if (empty($parts[0])) { + $parts[0] = 'default'; + } + if ($target instanceof \VuFind\I18n\TranslatableStringInterface) { + $class = get_class($target); + $parts[1] = new $class($parts[1], $target->getDisplayString()); + } return $parts; } - return ['default', $str]; + return ['default', is_array($target) ? $parts[0] : $target]; } } diff --git a/module/VuFind/src/VuFind/Search/Solr/Params.php b/module/VuFind/src/VuFind/Search/Solr/Params.php index 1b27627baec2ace7b4f000b04d457a3bc2cbb117..e76bc991e39d85dc82029059b5c62bdb590d543f 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Params.php +++ b/module/VuFind/src/VuFind/Search/Solr/Params.php @@ -619,6 +619,14 @@ class Params extends \VuFind\Search\Base\Params $filter['displayText'] = $facetHelper->formatDisplayText( $filter['displayText'], true, $separator ); + if ($translate) { + $domain = $this->getOptions()->getTextDomainForTranslatedFacet( + $field + ); + $filter['displayText'] = $this->translate( + [$domain, $filter['displayText']] + ); + } } return $filter; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php index 0c4347b9f79af4fa2cfc5c2ebaae2103b6248cb8..9651f09bc22effe4cb4f75a3c149bb85f4c1a443 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php @@ -27,6 +27,7 @@ */ namespace VuFindTest\View\Helper\Root; use VuFind\View\Helper\Root\Translate; +use VuFind\I18n\TranslatableString; /** * Translate view helper Test Class (and by extension, the TranslatorAwareTrait) @@ -61,10 +62,9 @@ class TranslateTest extends \PHPUnit_Framework_TestCase public function testTranslateWithTranslator() { $translate = new Translate(); - $translator = $this->getMock('Zend\I18n\Translator\TranslatorInterface'); - $translator->expects($this->once())->method('translate') - ->with($this->equalTo('foo'))->will($this->returnValue('%%token%%')); - $translate->setTranslator($translator); + $translate->setTranslator( + $this->getMockTranslator(['default' => ['foo' => '%%token%%']]) + ); // Simple case that tests default values and tokens in a single pass: $this->assertEquals('baz', $translate->__invoke( @@ -72,6 +72,92 @@ class TranslateTest extends \PHPUnit_Framework_TestCase ); } + /** + * Test translation of a TranslatableString object with a loaded translator + * + * @return void + */ + public function testTranslateTranslatableStringWithTranslator() + { + $translate = new Translate(); + $translate->setTranslator( + $this->getMockTranslator(['default' => ['foo' => '%%token%%']]) + ); + + // Test a TranslatableString with a translation. + $str1 = new TranslatableString('foo', 'bar'); + // Simple case that tests default values and tokens in a single pass: + $this->assertEquals('baz', $translate->__invoke( + $str1, ['%%token%%' => 'baz'], 'failure') + ); + + // Test a TranslatableString with a fallback. + $str2 = new TranslatableString('bar', 'foo'); + // Simple case that tests default values and tokens in a single pass: + $this->assertEquals('baz', $translate->__invoke( + $str2, ['%%token%%' => 'baz'], 'failure') + ); + + // Test a TranslatableString with no fallback. + $str3 = new TranslatableString('xyzzy', 'bar'); + // Simple case that tests default values and tokens in a single pass: + $this->assertEquals('failure', $translate->__invoke( + $str3, ['%%token%%' => 'baz'], 'failure') + ); + } + + /** + * Test translation of a TranslatableString object using text domains with a + * loaded translator + * + * @return void + */ + public function testTranslateTranslatableStringAndTextDomainsWithTranslator() + { + $translate = new Translate(); + $translate->setTranslator( + $this->getMockTranslator( + [ + 'd1' => ['f1' => 'str1'], + 'd2' => ['f2' => 'str2'], + ] + ) + ); + + // Primary string translatable + $str1 = new TranslatableString('d1::f1', 'd2::f2'); + $this->assertEquals('str1', $translate->__invoke($str1)); + // Secondary string translatable + $str2 = new TranslatableString('d1::f2', 'd2::f2'); + $this->assertEquals('str2', $translate->__invoke($str2)); + // No string translatable + $str3 = new TranslatableString('d1::f2', 'd2::f1'); + $this->assertEquals('failure', $translate->__invoke($str3, [], 'failure')); + } + + /** + * Test translation with a loaded translator and a text domain + * + * @return void + */ + public function testTranslateTextDomainWithTranslator() + { + $translate = new Translate(); + $translate->setTranslator( + $this->getMockTranslator(['zap' => ['foo' => '%%token%%']]) + ); + + // This one will work -- TextDomain defined above + $this->assertEquals('baz', $translate->__invoke( + 'zap::foo', ['%%token%%' => 'baz'], 'failure') + ); + + // This one will use incoming string -- TextDomain undefined + $this->assertEquals('failure', $translate->__invoke( + 'undefined::foo', ['%%token%%' => 'baz'], 'failure') + ); + } + /** * Test locale retrieval without a loaded translator * @@ -110,4 +196,23 @@ class TranslateTest extends \PHPUnit_Framework_TestCase $translate->setTranslator($translator); $this->assertEquals($translator, $translate->getTranslator()); } + + /** + * Get mock translator. + * + * @param array $translations Key => value translation map. + * + * @return \Zend\I18n\Translator\TranslatorInterface + */ + protected function getMockTranslator($translations) + { + $callback = function ($str, $domain) use ($translations) { + return isset($translations[$domain][$str]) + ? $translations[$domain][$str] : $str; + }; + $translator = $this->getMock('Zend\I18n\Translator\TranslatorInterface'); + $translator->expects($this->any())->method('translate') + ->will($this->returnCallback($callback)); + return $translator; + } } \ No newline at end of file