From 4dbe5bdc87e56ea8da764d2cbf039b4f917b3085 Mon Sep 17 00:00:00 2001 From: Markus Hoelzle <typo3@markus-hoelzle.de> Date: Thu, 7 Sep 2017 16:45:39 +0200 Subject: [PATCH] [FEATURE] Add possibility to get a label in a specific language Add possibility to get a label in a specific language in LocalizationUtility::translate() and the TranslateViewHelper Change-Id: I8589e2b155e57eed3124ed48b0d859fe7796ff3b Resolves: #82354 Related: #81834 Releases: master Reviewed-on: https://review.typo3.org/53967 Tested-by: TYPO3com <no-reply@typo3.com> Tested-by: Joerg Kummer <typo3@enobe.de> Reviewed-by: Markus Klein <markus.klein@typo3.org> Reviewed-by: Jigal van Hemert <jigal.van.hemert@typo3.org> Tested-by: Jigal van Hemert <jigal.van.hemert@typo3.org> --- ...sibilityToGetALabelInASpecificLanguage.rst | 40 +++ .../Classes/Utility/LocalizationUtility.php | 200 ++++++----- .../Unit/Utility/LocalizationUtilityTest.php | 336 +++++++++--------- .../ViewHelpers/TranslateViewHelper.php | 18 +- ...anslateViewHelperFixtureForEmptyString.php | 4 +- 5 files changed, 334 insertions(+), 264 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-82354-AddPossibilityToGetALabelInASpecificLanguage.rst diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-82354-AddPossibilityToGetALabelInASpecificLanguage.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-82354-AddPossibilityToGetALabelInASpecificLanguage.rst new file mode 100644 index 000000000000..51bb6de04dc1 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-82354-AddPossibilityToGetALabelInASpecificLanguage.rst @@ -0,0 +1,40 @@ +.. include:: ../../Includes.txt + +======================================================================= +Feature: #82354 - Add possibility to get a label in a specific language +======================================================================= + +See :issue:`82354` + +Description +=========== + +The extbase related LocalizationUtility now supports retrieving the localization +of a key in a different language than the initialized language of the given user. +This allows for instance rendering a text in french while the users language is +german. This feature works in the frontend and backend of TYPO3 and supports +to retrieve the labels via an extension name or an explicit specified +`LLL:path/locallang.xlf:label` key. + +The ViewHelper `<f:translate />` and the utility `LocalizationUtility::translate()` +do support now two new optional Parameters `languageKey` and `alternativeLanguageKeys` +to control the output language. + +Hint: The `alternativeLanguageKeys` will be used "reversed" (this behaviour was not changed here but could be confusing). +So if the `alternativeLanguageKeys` is defined with "fr,de", then "de" will used before "fr". + + +Basic Usage +=========== + +.. code-block:: php + + \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('someKey', 'extensionName', [], 'dk'); + + +.. code-block:: html + + <f:translate key="someKey" languageKey="dk" /> + + +.. index:: Fluid, PHP-API diff --git a/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php b/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php index 1dd951a214cd..404dd911bbe7 100644 --- a/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php +++ b/typo3/sysext/extbase/Classes/Utility/LocalizationUtility.php @@ -49,20 +49,6 @@ class LocalizationUtility */ protected static $LOCAL_LANG_UNSET = []; - /** - * Key of the language to use - * - * @var string - */ - protected static $languageKey = 'default'; - - /** - * Pointer to alternative fall-back language to use - * - * @var array - */ - protected static $alternativeLanguageKeys = []; - /** * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface */ @@ -73,16 +59,21 @@ class LocalizationUtility * * @param string $key The key from the LOCAL_LANG array for which to return the value. * @param string|null $extensionName The name of the extension - * @param array $arguments the arguments of the extension, being passed over to vsprintf - * @return string|NULL The value from LOCAL_LANG or NULL if no translation was found. + * @param array $arguments The arguments of the extension, being passed over to vsprintf + * @param string $languageKey The language key or null for using the current language from the system + * @param string[] $alternativeLanguageKeys The alternative language keys if no translation was found. If null and we are in the frontend, then the language_alt from TypoScript setup will be used + * @return string|null The value from LOCAL_LANG or null if no translation was found. * @api * @todo : If vsprintf gets a malformed string, it returns FALSE! Should we throw an exception there? */ - public static function translate($key, $extensionName = null, $arguments = null) + public static function translate($key, $extensionName = null, $arguments = null, string $languageKey = null, array $alternativeLanguageKeys = null) { $value = null; if (GeneralUtility::isFirstPartOfStr($key, 'LLL:')) { - $value = self::translateFileReference($key); + $keyParts = explode(':', $key); + unset($keyParts[0]); + $key = array_pop($keyParts); + $languageFilePath = implode(':', $keyParts); } else { if (empty($extensionName)) { throw new \InvalidArgumentException( @@ -90,33 +81,44 @@ class LocalizationUtility 1498144052 ); } - self::initializeLocalization($extensionName); - // The "from" charset of csConv() is only set for strings from TypoScript via _LOCAL_LANG - if (!empty(self::$LOCAL_LANG[$extensionName][self::$languageKey][$key][0]['target']) - || isset(self::$LOCAL_LANG_UNSET[$extensionName][self::$languageKey][$key]) - ) { - // Local language translation for key exists - $value = self::$LOCAL_LANG[$extensionName][self::$languageKey][$key][0]['target']; - } elseif (!empty(self::$alternativeLanguageKeys)) { - $languages = array_reverse(self::$alternativeLanguageKeys); - foreach ($languages as $language) { - if (!empty(self::$LOCAL_LANG[$extensionName][$language][$key][0]['target']) - || isset(self::$LOCAL_LANG_UNSET[$extensionName][$language][$key]) - ) { - // Alternative language translation for key exists - $value = self::$LOCAL_LANG[$extensionName][$language][$key][0]['target']; - break; - } + $languageFilePath = static::getLanguageFilePath($extensionName); + } + $languageFilePath = GeneralUtility::getFileAbsFileName($languageFilePath); + $languageKeys = static::getLanguageKeys(); + if ($languageKey === null) { + $languageKey = $languageKeys['languageKey']; + } + if (empty($alternativeLanguageKeys)) { + $alternativeLanguageKeys = $languageKeys['alternativeLanguageKeys']; + } + static::initializeLocalization($languageFilePath, $languageKey, $alternativeLanguageKeys, $extensionName); + + // The "from" charset of csConv() is only set for strings from TypoScript via _LOCAL_LANG + if (!empty(self::$LOCAL_LANG[$languageFilePath][$languageKey][$key][0]['target']) + || isset(self::$LOCAL_LANG_UNSET[$languageFilePath][$languageKey][$key]) + ) { + // Local language translation for key exists + $value = self::$LOCAL_LANG[$languageFilePath][$languageKey][$key][0]['target']; + } elseif (!empty($alternativeLanguageKeys)) { + $languages = array_reverse($alternativeLanguageKeys); + foreach ($languages as $language) { + if (!empty(self::$LOCAL_LANG[$languageFilePath][$language][$key][0]['target']) + || isset(self::$LOCAL_LANG_UNSET[$languageFilePath][$language][$key]) + ) { + // Alternative language translation for key exists + $value = self::$LOCAL_LANG[$languageFilePath][$language][$key][0]['target']; + break; } } - if ($value === null && (!empty(self::$LOCAL_LANG[$extensionName]['default'][$key][0]['target']) - || isset(self::$LOCAL_LANG_UNSET[$extensionName]['default'][$key])) - ) { - // Default language translation for key exists - // No charset conversion because default is English and thereby ASCII - $value = self::$LOCAL_LANG[$extensionName]['default'][$key][0]['target']; - } } + if ($value === null && (!empty(self::$LOCAL_LANG[$languageFilePath]['default'][$key][0]['target']) + || isset(self::$LOCAL_LANG_UNSET[$languageFilePath]['default'][$key])) + ) { + // Default language translation for key exists + // No charset conversion because default is English and thereby ASCII + $value = self::$LOCAL_LANG[$languageFilePath]['default'][$key][0]['target']; + } + if (is_array($arguments) && $value !== null) { return vsprintf($value, $arguments); } @@ -124,81 +126,86 @@ class LocalizationUtility } /** - * Returns the localized label of the LOCAL_LANG key, $key. + * Loads local-language values by looking for a "locallang.xlf" (or "locallang.xml") file in the plugin resources directory and if found includes it. + * Also locallang values set in the TypoScript property "_LOCAL_LANG" are merged onto the values found in the "locallang.xlf" file. * - * @param string $key The language key including the path to a custom locallang file ("LLL:path:key"). - * @return string The value from LOCAL_LANG or NULL if no translation was found. - * @see language::sL() - * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::sL() + * @param string $languageFilePath + * @param string $languageKey + * @param string[] $alternativeLanguageKeys + * @param string $extensionName */ - protected static function translateFileReference($key) + protected static function initializeLocalization(string $languageFilePath, string $languageKey, array $alternativeLanguageKeys, string $extensionName = null) { - if (TYPO3_MODE === 'FE') { - $value = self::getTypoScriptFrontendController()->sL($key); - return $value !== false ? $value : null; + $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class); + + if (empty(self::$LOCAL_LANG[$languageFilePath][$languageKey])) { + $parsedData = $languageFactory->getParsedData($languageFilePath, $languageKey); + foreach ($parsedData as $tempLanguageKey => $data) { + if (!empty($data)) { + self::$LOCAL_LANG[$languageFilePath][$tempLanguageKey] = $data; + } + } + } + if ($languageKey !== 'default') { + foreach ($alternativeLanguageKeys as $alternativeLanguageKey) { + if (empty(self::$LOCAL_LANG[$languageFilePath][$alternativeLanguageKey])) { + $tempLL = $languageFactory->getParsedData($languageFilePath, $alternativeLanguageKey); + if (isset($tempLL[$alternativeLanguageKey])) { + self::$LOCAL_LANG[$languageFilePath][$alternativeLanguageKey] = $tempLL[$alternativeLanguageKey]; + } + } + } } - if (is_object($GLOBALS['LANG'])) { - $value = self::getLanguageService()->sL($key); - return $value !== '' ? $value : null; + if (!empty($extensionName)) { + static::loadTypoScriptLabels($extensionName, $languageFilePath); } - return $key; } /** - * Loads local-language values by looking for a "locallang.xlf" (or "locallang.xml") file in the plugin resources directory and if found includes it. - * Also locallang values set in the TypoScript property "_LOCAL_LANG" are merged onto the values found in the "locallang.xlf" file. + * Returns the default path and filename for an extension * * @param string $extensionName + * @return string */ - protected static function initializeLocalization($extensionName) + protected static function getLanguageFilePath(string $extensionName): string { - if (isset(self::$LOCAL_LANG[$extensionName])) { - return; - } - $locallangPathAndFilename = 'EXT:' . GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName) . '/' . self::$locallangPath . 'locallang.xlf'; - self::setLanguageKeys(); - - /** @var $languageFactory LocalizationFactory */ - $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class); - - self::$LOCAL_LANG[$extensionName] = $languageFactory->getParsedData($locallangPathAndFilename, self::$languageKey); - foreach (self::$alternativeLanguageKeys as $language) { - $tempLL = $languageFactory->getParsedData($locallangPathAndFilename, $language); - if (self::$languageKey !== 'default' && isset($tempLL[$language])) { - self::$LOCAL_LANG[$extensionName][$language] = $tempLL[$language]; - } - } - self::loadTypoScriptLabels($extensionName); + return 'EXT:' . GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName) . '/' . self::$locallangPath . 'locallang.xlf'; } /** * Sets the currently active language/language_alt keys. - * Default values are "default" for language key and "" for language_alt key. + * Default values are "default" for language key and an empty array for language_alt key. + * + * @return array */ - protected static function setLanguageKeys() + protected static function getLanguageKeys(): array { - self::$languageKey = 'default'; - self::$alternativeLanguageKeys = []; + $languageKeys = [ + 'languageKey' => 'default', + 'alternativeLanguageKeys' => [], + ]; if (TYPO3_MODE === 'FE') { - if (isset(self::getTypoScriptFrontendController()->config['config']['language'])) { - self::$languageKey = self::getTypoScriptFrontendController()->config['config']['language']; - if (isset(self::getTypoScriptFrontendController()->config['config']['language_alt'])) { - self::$alternativeLanguageKeys[] = self::getTypoScriptFrontendController()->config['config']['language_alt']; + $tsfe = static::getTypoScriptFrontendController(); + if (isset($tsfe->config['config']['language'])) { + $languageKeys['languageKey'] = $tsfe->config['config']['language']; + if (isset($tsfe->config['config']['language_alt'])) { + $languageKeys['alternativeLanguageKeys'] = $tsfe->config['config']['language_alt']; } else { /** @var $locales \TYPO3\CMS\Core\Localization\Locales */ $locales = GeneralUtility::makeInstance(Locales::class); - if (in_array(self::$languageKey, $locales->getLocales())) { - foreach ($locales->getLocaleDependencies(self::$languageKey) as $language) { - self::$alternativeLanguageKeys[] = $language; + if (in_array($languageKeys['languageKey'], $locales->getLocales())) { + foreach ($locales->getLocaleDependencies($languageKeys['languageKey']) as $language) { + $languageKeys['alternativeLanguageKeys'] = $language; } } } } } elseif (!empty($GLOBALS['BE_USER']->uc['lang'])) { - self::$languageKey = $GLOBALS['BE_USER']->uc['lang']; - } elseif (!empty(self::getLanguageService()->lang)) { - self::$languageKey = self::getLanguageService()->lang; + $languageKeys['languageKey'] = $GLOBALS['BE_USER']->uc['lang']; + } elseif (!empty(static::getLanguageService()->lang)) { + $languageKeys['languageKey'] = static::getLanguageService()->lang; } + return $languageKeys; } /** @@ -207,31 +214,32 @@ class LocalizationUtility * plugin.tx_myextension._LOCAL_LANG.languageKey.key = value * * @param string $extensionName + * @param string $languageFilePath */ - protected static function loadTypoScriptLabels($extensionName) + protected static function loadTypoScriptLabels($extensionName, $languageFilePath) { $configurationManager = static::getConfigurationManager(); $frameworkConfiguration = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, $extensionName); if (!is_array($frameworkConfiguration['_LOCAL_LANG'])) { return; } - self::$LOCAL_LANG_UNSET[$extensionName] = []; + self::$LOCAL_LANG_UNSET[$languageFilePath] = []; foreach ($frameworkConfiguration['_LOCAL_LANG'] as $languageKey => $labels) { - if (!(is_array($labels) && isset(self::$LOCAL_LANG[$extensionName][$languageKey]))) { + if (!(is_array($labels) && isset(self::$LOCAL_LANG[$languageFilePath][$languageKey]))) { continue; } foreach ($labels as $labelKey => $labelValue) { if (is_string($labelValue)) { - self::$LOCAL_LANG[$extensionName][$languageKey][$labelKey][0]['target'] = $labelValue; + self::$LOCAL_LANG[$languageFilePath][$languageKey][$labelKey][0]['target'] = $labelValue; if ($labelValue === '') { - self::$LOCAL_LANG_UNSET[$extensionName][$languageKey][$labelKey] = ''; + self::$LOCAL_LANG_UNSET[$languageFilePath][$languageKey][$labelKey] = ''; } } elseif (is_array($labelValue)) { $labelValue = self::flattenTypoScriptLabelArray($labelValue, $labelKey); foreach ($labelValue as $key => $value) { - self::$LOCAL_LANG[$extensionName][$languageKey][$key][0]['target'] = $value; + self::$LOCAL_LANG[$languageFilePath][$languageKey][$key][0]['target'] = $value; if ($value === '') { - self::$LOCAL_LANG_UNSET[$extensionName][$languageKey][$key] = ''; + self::$LOCAL_LANG_UNSET[$languageFilePath][$languageKey][$key] = ''; } } } diff --git a/typo3/sysext/extbase/Tests/Unit/Utility/LocalizationUtilityTest.php b/typo3/sysext/extbase/Tests/Unit/Utility/LocalizationUtilityTest.php index f1719e8f4309..0a0c05c6dfd5 100644 --- a/typo3/sysext/extbase/Tests/Unit/Utility/LocalizationUtilityTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Utility/LocalizationUtilityTest.php @@ -33,124 +33,133 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest * * @var array */ - protected $LOCAL_LANG = [ - 'extensionKey' => [ - 'default' => [ - 'key1' => [ - [ - 'source' => 'English label for key1', - 'target' => 'English label for key1', - ] - ], - 'key2' => [ - [ - 'source' => 'English label for key2', - 'target' => 'English label for key2', - ] - ], - 'key3' => [ - [ - 'source' => 'English label for key3', - 'target' => 'English label for key3', - ] - ], - 'key4' => [ - [ - 'source' => 'English label for key4', - 'target' => 'English label for key4', - ] - ], - 'keyWithPlaceholder' => [ - [ - 'source' => 'English label with number %d', - 'target' => 'English label with number %d', - ] - ], - ], - 'dk' => [ - 'key1' => [ - [ - 'source' => 'English label for key1', - 'target' => 'Dansk label for key1', - ] - ], - // not translated in dk => no target (llxml) - 'key2' => [ - [ - 'source' => 'English label for key2', - ] - ], - 'key3' => [ - [ - 'source' => 'English label for key3', - ] - ], - // not translated in dk => empty target (xliff) - 'key4' => [ - [ - 'source' => 'English label for key4', - 'target' => '', - ] - ], - // not translated in dk => empty target (xliff) - 'key5' => [ - [ - 'source' => 'English label for key5', - 'target' => '', - ] - ], - 'keyWithPlaceholder' => [ - [ - 'source' => 'English label with number %d', - ] - ], - ], - // fallback language for labels which are not translated in dk - 'dk_alt' => [ - 'key1' => [ - [ - 'source' => 'English label for key1', - ] - ], - 'key2' => [ - [ - 'source' => 'English label for key2', - 'target' => 'Dansk alternative label for key2', - ] - ], - 'key3' => [ - [ - 'source' => 'English label for key3', - ] - ], - // not translated in dk_alt => empty target (xliff) - 'key4' => [ - [ - 'source' => 'English label for key4', - 'target' => '', - ] - ], - 'key5' => [ - [ - 'source' => 'English label for key5', - 'target' => 'Dansk alternative label for key5', - ] - ], - 'keyWithPlaceholder' => [ - [ - 'source' => 'English label with number %d', - ] - ], - ], + protected $LOCAL_LANG = []; - ], - ]; + /** + * File path of locallang for extension "core" + * @var string + */ + protected $languageFilePath = ''; /** * Prepare class mocking some dependencies */ protected function setUp() { + $this->languageFilePath = $this->getLanguageFilePath('core'); + $this->LOCAL_LANG = [ + $this->languageFilePath => [ + 'default' => [ + 'key1' => [ + [ + 'source' => 'English label for key1', + 'target' => 'English label for key1', + ] + ], + 'key2' => [ + [ + 'source' => 'English label for key2', + 'target' => 'English label for key2', + ] + ], + 'key3' => [ + [ + 'source' => 'English label for key3', + 'target' => 'English label for key3', + ] + ], + 'key4' => [ + [ + 'source' => 'English label for key4', + 'target' => 'English label for key4', + ] + ], + 'keyWithPlaceholder' => [ + [ + 'source' => 'English label with number %d', + 'target' => 'English label with number %d', + ] + ], + ], + 'dk' => [ + 'key1' => [ + [ + 'source' => 'English label for key1', + 'target' => 'Dansk label for key1', + ] + ], + // not translated in dk => no target (llxml) + 'key2' => [ + [ + 'source' => 'English label for key2', + ] + ], + 'key3' => [ + [ + 'source' => 'English label for key3', + ] + ], + // not translated in dk => empty target (xliff) + 'key4' => [ + [ + 'source' => 'English label for key4', + 'target' => '', + ] + ], + // not translated in dk => empty target (xliff) + 'key5' => [ + [ + 'source' => 'English label for key5', + 'target' => '', + ] + ], + 'keyWithPlaceholder' => [ + [ + 'source' => 'English label with number %d', + ] + ], + ], + // fallback language for labels which are not translated in dk + 'dk_alt' => [ + 'key1' => [ + [ + 'source' => 'English label for key1', + ] + ], + 'key2' => [ + [ + 'source' => 'English label for key2', + 'target' => 'Dansk alternative label for key2', + ] + ], + 'key3' => [ + [ + 'source' => 'English label for key3', + ] + ], + // not translated in dk_alt => empty target (xliff) + 'key4' => [ + [ + 'source' => 'English label for key4', + 'target' => '', + ] + ], + 'key5' => [ + [ + 'source' => 'English label for key5', + 'target' => 'Dansk alternative label for key5', + ] + ], + 'keyWithPlaceholder' => [ + [ + 'source' => 'English label with number %d', + ] + ], + ], + + ], + ]; + $reflectionClass = new \ReflectionClass(\TYPO3\CMS\Extbase\Utility\LocalizationUtility::class); $this->configurationManager = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Configuration\ConfigurationManager::class, ['getConfiguration']); @@ -173,14 +182,15 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest $property = $reflectionClass->getProperty('LOCAL_LANG'); $property->setAccessible(true); $property->setValue([]); + } - $property = $reflectionClass->getProperty('languageKey'); - $property->setAccessible(true); - $property->setValue('default'); - - $property = $reflectionClass->getProperty('alternativeLanguageKeys'); - $property->setAccessible(true); - $property->setValue([]); + /** + * @param string $extensionName + * @return string + */ + protected function getLanguageFilePath(string $extensionName): string + { + return PATH_typo3 . 'sysext/' . $extensionName . '/Resources/Private/Language/locallang.xlf'; } /** @@ -237,37 +247,36 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest { return [ 'get translated key' => - ['key1', $this->LOCAL_LANG, 'dk', 'Dansk label for key1'], + ['key1', 'dk', 'Dansk label for key1'], 'fallback to English when translation is missing for key' => - ['key2', $this->LOCAL_LANG, 'dk', 'English label for key2'], + ['key2', 'dk', 'English label for key2'], 'fallback to English for non existing language' => - ['key2', $this->LOCAL_LANG, 'xx', 'English label for key2'], + ['key2', 'xx', 'English label for key2'], 'replace placeholder with argument' => - ['keyWithPlaceholder', $this->LOCAL_LANG, 'en', 'English label with number 100', [], [100]], + ['keyWithPlaceholder', 'default', 'English label with number 100', [], [100]], 'get translated key from primary language' => - ['key1', $this->LOCAL_LANG, 'dk', 'Dansk label for key1', ['dk_alt']], + ['key1', 'dk', 'Dansk label for key1', ['dk_alt']], 'fallback to alternative language if translation is missing(llxml)' => - ['key2', $this->LOCAL_LANG, 'dk', 'Dansk alternative label for key2', ['dk_alt']], + ['key2', 'dk', 'Dansk alternative label for key2', ['dk_alt']], 'fallback to alternative language if translation is missing(xlif)' => - ['key5', $this->LOCAL_LANG, 'dk', 'Dansk alternative label for key5', ['dk_alt']], + ['key5', 'dk', 'Dansk alternative label for key5', ['dk_alt']], 'fallback to English for label not translated in dk and dk_alt(llxml)' => - ['key3', $this->LOCAL_LANG, 'dk', 'English label for key3', ['dk_alt']], + ['key3', 'dk', 'English label for key3', ['dk_alt']], 'fallback to English for label not translated in dk and dk_alt(xlif)' => - ['key4', $this->LOCAL_LANG, 'dk', 'English label for key4', ['dk_alt']], + ['key4', 'dk', 'English label for key4', ['dk_alt']], ]; } /** * @param string $key - * @param array $LOCAL_LANG * @param string $languageKey * @param string $expected * @param array $altLanguageKeys @@ -275,23 +284,38 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest * @dataProvider translateDataProvider * @test */ - public function translateTest($key, array $LOCAL_LANG, $languageKey, $expected, array $altLanguageKeys = [], array $arguments = null) + public function translateTestWithBackendUserLanguage($key, $languageKey, $expected, array $altLanguageKeys = [], array $arguments = null) { $reflectionClass = new \ReflectionClass(\TYPO3\CMS\Extbase\Utility\LocalizationUtility::class); $property = $reflectionClass->getProperty('LOCAL_LANG'); $property->setAccessible(true); - $property->setValue($LOCAL_LANG); + $property->setValue($this->LOCAL_LANG); - $property = $reflectionClass->getProperty('languageKey'); - $property->setAccessible(true); - $property->setValue($languageKey); + $oldBackendUserLanguage = $GLOBALS['BE_USER']->uc['lang']; + $GLOBALS['BE_USER']->uc['lang'] = $languageKey; + $this->assertEquals($expected, LocalizationUtility::translate($key, 'core', $arguments, null, $altLanguageKeys)); + $GLOBALS['BE_USER']->uc['lang'] = $oldBackendUserLanguage; + } - $property = $reflectionClass->getProperty('alternativeLanguageKeys'); + /** + * @param string $key + * @param string $languageKey + * @param string $expected + * @param array $altLanguageKeys + * @param array $arguments + * @dataProvider translateDataProvider + * @test + */ + public function translateTestWithExplicitLanguageParameters($key, $languageKey, $expected, array $altLanguageKeys = [], array $arguments = null) + { + $reflectionClass = new \ReflectionClass(\TYPO3\CMS\Extbase\Utility\LocalizationUtility::class); + + $property = $reflectionClass->getProperty('LOCAL_LANG'); $property->setAccessible(true); - $property->setValue($altLanguageKeys); + $property->setValue($this->LOCAL_LANG); - $this->assertEquals($expected, LocalizationUtility::translate($key, 'extensionKey', $arguments)); + $this->assertEquals($expected, LocalizationUtility::translate($key, 'core', $arguments, $languageKey, $altLanguageKeys)); } /** @@ -302,12 +326,12 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest return [ 'override labels with typoscript' => [ 'LOCAL_LANG' => [ - 'extensionKey' => [ + $this->getLanguageFilePath('core') => [ 'dk' => [ 'key1' => [ [ 'source' => 'English label for key1', - 'target' => 'Dansk label for key1 extensionKey', + 'target' => 'Dansk label for key1 core', ] ], 'key2' => [ @@ -322,12 +346,12 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest ], ], ], - 'extensionKey1' => [ + $this->getLanguageFilePath('backend') => [ 'dk' => [ 'key1' => [ [ 'source' => 'English label for key1', - 'target' => 'Dansk label for key1 extensionKey1', + 'target' => 'Dansk label for key1 backend', ] ], 'key2' => [ @@ -346,12 +370,12 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest 'typoscript LOCAL_LANG' => [ '_LOCAL_LANG' => [ 'dk' => [ - 'key1' => 'key1 value from TS extensionKey', + 'key1' => 'key1 value from TS core', 'key3' => [ - 'subkey1' => 'key3.subkey1 value from TS extensionKey', + 'subkey1' => 'key3.subkey1 value from TS core', // this key doesn't exist in xml files 'subkey2' => [ - 'subsubkey' => 'key3.subkey2.subsubkey value from TS extensionKey' + 'subsubkey' => 'key3.subkey2.subsubkey value from TS core' ] ] ] @@ -362,7 +386,7 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest 'key1' => [ [ 'source' => 'English label for key1', - 'target' => 'key1 value from TS extensionKey', + 'target' => 'key1 value from TS core', ] ], 'key2' => [ @@ -373,12 +397,12 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest 'key3.subkey1' => [ [ 'source' => 'English label for key3', - 'target' => 'key3.subkey1 value from TS extensionKey', + 'target' => 'key3.subkey1 value from TS core', ] ], 'key3.subkey2.subsubkey' => [ [ - 'target' => 'key3.subkey2.subsubkey value from TS extensionKey', + 'target' => 'key3.subkey2.subsubkey value from TS core', ] ], ], @@ -404,22 +428,18 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest $property->setAccessible(true); $property->setValue($LOCAL_LANG); - $property = $reflectionClass->getProperty('languageKey'); - $property->setAccessible(true); - $property->setValue($languageKey); - $configurationType = \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK; - $this->configurationManager->expects($this->at(0))->method('getConfiguration')->with($configurationType, 'extensionKey', null)->will($this->returnValue($typoScriptLocalLang)); + $this->configurationManager->expects($this->at(0))->method('getConfiguration')->with($configurationType, 'core', null)->will($this->returnValue($typoScriptLocalLang)); $method = $reflectionClass->getMethod('loadTypoScriptLabels'); $method->setAccessible(true); - $method->invoke(null, 'extensionKey'); + $method->invoke(null, 'core', $this->languageFilePath); $property = $reflectionClass->getProperty('LOCAL_LANG'); $property->setAccessible(true); $result = $property->getValue(); - $this->assertEquals($expected, $result['extensionKey'][$languageKey]); + $this->assertEquals($expected, $result[$this->languageFilePath][$languageKey]); } /** @@ -433,10 +453,6 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest $property->setAccessible(true); $property->setValue($this->LOCAL_LANG); - $property = $reflectionClass->getProperty('languageKey'); - $property->setAccessible(true); - $property->setValue('dk'); - $typoScriptLocalLang = [ '_LOCAL_LANG' => [ 'dk' => [ @@ -446,13 +462,13 @@ class LocalizationUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTest ]; $configurationType = \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK; - $this->configurationManager->expects($this->at(0))->method('getConfiguration')->with($configurationType, 'extensionKey', null)->will($this->returnValue($typoScriptLocalLang)); + $this->configurationManager->expects($this->at(0))->method('getConfiguration')->with($configurationType, 'core', null)->will($this->returnValue($typoScriptLocalLang)); $method = $reflectionClass->getMethod('loadTypoScriptLabels'); $method->setAccessible(true); - $method->invoke(null, 'extensionKey'); + $method->invoke(null, 'core', $this->languageFilePath); - $result = LocalizationUtility::translate('key1', 'extensionKey'); + $result = LocalizationUtility::translate('key1', 'core', null, 'dk'); $this->assertNotNull($result); $this->assertEquals('', $result); } diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php index ea2d9c488bc6..70dab1757b6f 100644 --- a/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php +++ b/typo3/sysext/fluid/Classes/ViewHelpers/TranslateViewHelper.php @@ -94,6 +94,8 @@ class TranslateViewHelper extends AbstractViewHelper $this->registerArgument('default', 'string', 'If the given locallang key could not be found, this value is used. If this argument is not set, child nodes will be used to render the default'); $this->registerArgument('arguments', 'array', 'Arguments to be replaced in the resulting string'); $this->registerArgument('extensionName', 'string', 'UpperCamelCased extension key (for example BlogExample)'); + $this->registerArgument('languageKey', 'string', 'Language key ("dk" for example) or "default" to use for this translation. If this argument is empty, we use the current language'); + $this->registerArgument('alternativeLanguageKeys', 'array', 'Alternative language keys if no translation does exist'); } /** @@ -111,7 +113,7 @@ class TranslateViewHelper extends AbstractViewHelper $id = $arguments['id']; $default = $arguments['default']; $extensionName = $arguments['extensionName']; - $arguments = $arguments['arguments']; + $translateArguments = $arguments['arguments']; // Wrapper including a compatibility layer for TYPO3 Flow Translation if ($id === null) { @@ -125,14 +127,14 @@ class TranslateViewHelper extends AbstractViewHelper $request = $renderingContext->getControllerContext()->getRequest(); $extensionName = $extensionName === null ? $request->getControllerExtensionName() : $extensionName; try { - $value = static::translate($id, $extensionName, $arguments); + $value = static::translate($id, $extensionName, $translateArguments, $arguments['languageKey'], $arguments['alternativeLanguageKeys']); } catch (\InvalidArgumentException $e) { $value = null; } if ($value === null) { $value = $default !== null ? $default : $renderChildrenClosure(); - if (!empty($arguments)) { - $value = vsprintf($value, $arguments); + if (!empty($translateArguments)) { + $value = vsprintf($value, $translateArguments); } } return $value; @@ -144,11 +146,13 @@ class TranslateViewHelper extends AbstractViewHelper * @param string $id Translation Key compatible to TYPO3 Flow * @param string $extensionName UpperCamelCased extension key (for example BlogExample) * @param array $arguments Arguments to be replaced in the resulting string + * @param string $languageKey Language key to use for this translation + * @param string[] $alternativeLanguageKeys Alternative language keys if no translation does exist * - * @return NULL|string + * @return null|string */ - protected static function translate($id, $extensionName, $arguments) + protected static function translate($id, $extensionName, $arguments, $languageKey, $alternativeLanguageKeys) { - return LocalizationUtility::translate($id, $extensionName, $arguments); + return LocalizationUtility::translate($id, $extensionName, $arguments, $languageKey, $alternativeLanguageKeys); } } diff --git a/typo3/sysext/fluid/Tests/Unit/ViewHelpers/Fixtures/TranslateViewHelperFixtureForEmptyString.php b/typo3/sysext/fluid/Tests/Unit/ViewHelpers/Fixtures/TranslateViewHelperFixtureForEmptyString.php index 7a810001c757..543302cd084a 100644 --- a/typo3/sysext/fluid/Tests/Unit/ViewHelpers/Fixtures/TranslateViewHelperFixtureForEmptyString.php +++ b/typo3/sysext/fluid/Tests/Unit/ViewHelpers/Fixtures/TranslateViewHelperFixtureForEmptyString.php @@ -26,10 +26,12 @@ class TranslateViewHelperFixtureForEmptyString extends TranslateViewHelper * @param string $id Translation Key compatible to TYPO3 Flow * @param string $extensionName UpperCamelCased extension key (for example BlogExample) * @param array $arguments Arguments to be replaced in the resulting string + * @param string $languageKey Language key to use for this translation + * @param string[] $alternativeLanguageKeys Alternative language keys if no translation does exist * * @return NULL */ - protected static function translate($id, $extensionName, $arguments) + protected static function translate($id, $extensionName, $arguments, $languageKey, $alternativeLanguageKeys) { return null; } -- GitLab