From 71a7076e22c94e4bf484f8d0c92ff6dcf6c7c511 Mon Sep 17 00:00:00 2001 From: Mathias Brodala <mbrodala@pagemachine.de> Date: Mon, 29 May 2017 13:08:48 +0200 Subject: [PATCH] [FEATURE] EXT:form - support translation arguments Form element properties and finisher options can now use arguments in their translations. This is especially useful to pass values created dynamically via formDefinitionOverrides in TypoScript. Resolves: #81363 Releases: master Change-Id: Ie205ebc62bcf807e6740c54bbda0115435317604 Reviewed-on: https://review.typo3.org/52951 Reviewed-by: Daniel Lorenz <daniel.lorenz@extco.de> Tested-by: Daniel Lorenz <daniel.lorenz@extco.de> Reviewed-by: Carlos Meyer <cm@davitec.de> Tested-by: Carlos Meyer <cm@davitec.de> Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Susanne Moog <susanne.moog@typo3.org> Tested-by: Susanne Moog <susanne.moog@typo3.org> --- ...-AcceptFormElementTranslationArguments.rst | 92 +++++++++++++++++++ .../Classes/Service/TranslationService.php | 20 +++- .../Unit/Service/Fixtures/locallang_form.xlf | 8 ++ .../Unit/Service/TranslationServiceTest.php | 74 +++++++++++++++ 4 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-81363-AcceptFormElementTranslationArguments.rst diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-81363-AcceptFormElementTranslationArguments.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-81363-AcceptFormElementTranslationArguments.rst new file mode 100644 index 000000000000..9676da851241 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-81363-AcceptFormElementTranslationArguments.rst @@ -0,0 +1,92 @@ +.. include:: ../../Includes.txt + +======================================================================= +Feature: #81363 - EXT:form - support form element translation arguments +======================================================================= + +See :issue:`81363` + +Description +=========== + +Passing arguments to form element property translations is now supported to enrich +translations with variable values: + +.. code-block:: yaml + + renderables: + fieldWithTranslationArguments: + identifier: field-with-translation-arguments + type: Checkbox + label: This is a %s feature + renderingOptions: + translation: + translationFile: path/to/locallang.xlf + arguments: + label: + - useful + +Alternatively, translation arguments can be set via :typoscript:`formDefinitionOverrides` +in TypoScript: + +.. code-block: typoscript + + plugin.tx_form { + settings { + formDefinitionOverrides { + <form-id> { + renderables { + 0 { # Page + renderables { + fieldWithTranslationArguments { + renderingOptions { + translation { + arguments { + label { + 0 = TEXT + 0.typolink { + parameter = 42 + returnLast = url + } + } + } + } + } + } + } + } + } + } + } + } + } + +.. important:: + There must be at least one translation file with a translation for the configured form element property. Arguments are not inserted into default values defined in a form definition. + +The same goes for finisher options: + +.. code-block:: yaml + + + finishers: + finisherWithTranslationArguments: + identifier: EmailToReceiver + options: + subject: My %s subject + recipientAddress: foo@example.org + senderAddress: bar@example.org + translation: + translationFile: path/to/locallang.xlf + arguments: + subject: + - awesome + + +Impact +====== + +Form element property translations and finisher option translations can now use placeholders +to output translation arguments. + +.. index:: Frontend, TypoScript, NotScanned diff --git a/typo3/sysext/form/Classes/Service/TranslationService.php b/typo3/sysext/form/Classes/Service/TranslationService.php index b87956afe23d..99be366feb2a 100644 --- a/typo3/sysext/form/Classes/Service/TranslationService.php +++ b/typo3/sysext/form/Classes/Service/TranslationService.php @@ -258,13 +258,19 @@ class TranslationService implements SingletonInterface $language = $renderingOptions['language']; } + try { + $arguments = ArrayUtility::getValueByPath($renderingOptions['arguments'] ?? [], $optionKey, '.'); + } catch (\RuntimeException $e) { + $arguments = []; + } + $translationKeyChain = []; foreach ($translationFiles as $translationFile) { $translationKeyChain[] = sprintf('%s:%s.finisher.%s.%s', $translationFile, $formRuntime->getIdentifier(), $finisherIdentifier, $optionKey); $translationKeyChain[] = sprintf('%s:finisher.%s.%s', $translationFile, $finisherIdentifier, $optionKey); } - $translatedValue = $this->processTranslationChain($translationKeyChain, $language); + $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments); $translatedValue = (empty($translatedValue)) ? $optionValue : $translatedValue; return $translatedValue; @@ -337,6 +343,12 @@ class TranslationService implements SingletonInterface $language = $renderingOptions['translation']['language']; } + try { + $arguments = ArrayUtility::getValueByPath($renderingOptions['translation']['arguments'] ?? [], $propertyParts, '.'); + } catch (\RuntimeException $e) { + $arguments = []; + } + if ($property === 'options' && is_array($defaultValue)) { foreach ($defaultValue as $optionValue => &$optionLabel) { $translationKeyChain = []; @@ -346,7 +358,7 @@ class TranslationService implements SingletonInterface $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property, $optionValue); } - $translatedValue = $this->processTranslationChain($translationKeyChain, $language); + $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments); $optionLabel = (empty($translatedValue)) ? $optionLabel : $translatedValue; } $translatedValue = $defaultValue; @@ -359,7 +371,7 @@ class TranslationService implements SingletonInterface $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $propertyName); } - $translatedValue = $this->processTranslationChain($translationKeyChain, $language); + $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments); $propertyValue = (empty($translatedValue)) ? $propertyValue : $translatedValue; } $translatedValue = $defaultValue; @@ -371,7 +383,7 @@ class TranslationService implements SingletonInterface $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property); } - $translatedValue = $this->processTranslationChain($translationKeyChain, $language); + $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments); $translatedValue = (empty($translatedValue)) ? $defaultValue : $translatedValue; } diff --git a/typo3/sysext/form/Tests/Unit/Service/Fixtures/locallang_form.xlf b/typo3/sysext/form/Tests/Unit/Service/Fixtures/locallang_form.xlf index 898bde4244b3..94e8e2d9d134 100644 --- a/typo3/sysext/form/Tests/Unit/Service/Fixtures/locallang_form.xlf +++ b/typo3/sysext/form/Tests/Unit/Service/Fixtures/locallang_form.xlf @@ -49,9 +49,17 @@ <source>my-form-runtime-identifier my-form-element-identifier LABEL EN</source> </trans-unit> + <trans-unit id="element.my-form-element-with-translation-arguments.properties.label" xml:space="preserve"> + <source>See %s or %s</source> + </trans-unit> + <trans-unit id="my-form-runtime-identifier.finisher.SaveToDatabase.subject" xml:space="preserve"> <source>my-form-runtime-identifier form-element-identifier SaveToDatabase subject EN</source> </trans-unit> + + <trans-unit id="finisher.EmailToReceiverWithTranslationArguments.subject" xml:space="preserve"> + <source>My %s subject</source> + </trans-unit> </body> </file> </xliff> diff --git a/typo3/sysext/form/Tests/Unit/Service/TranslationServiceTest.php b/typo3/sysext/form/Tests/Unit/Service/TranslationServiceTest.php index 76377e931e87..2077cedea805 100644 --- a/typo3/sysext/form/Tests/Unit/Service/TranslationServiceTest.php +++ b/typo3/sysext/form/Tests/Unit/Service/TranslationServiceTest.php @@ -21,6 +21,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Configuration\ConfigurationManager; use TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement; use TYPO3\CMS\Form\Domain\Model\FormElements\Page; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; use TYPO3\CMS\Form\Service\TranslationService; @@ -876,6 +877,47 @@ class TranslationServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestC $this->assertEquals($expected, $this->mockTranslationService->_call('translateFormElementValue', $mockFormElement, ['label'], $mockFormRuntime)); } + /** + * @test + */ + public function supportsArgumentsForFormElementValueTranslations() + { + $formRuntimeXlfPath = 'EXT:form/Tests/Unit/Service/Fixtures/locallang_form.xlf'; + + $this->store->flushData($formRuntimeXlfPath); + + /** @var FormRuntime|\Prophecy\Prophecy\ObjectProphecy */ + $formRuntime = $this->prophesize(FormRuntime::class); + $formRuntime->getIdentifier()->willReturn('my-form-runtime-identifier'); + $formRuntime->getRenderingOptions()->willReturn([ + 'translation' => [ + 'translationFile' => $formRuntimeXlfPath, + 'translatePropertyValueIfEmpty' => true, + ], + ]); + + /** @var RootRenderableInterface|\Prophecy\Prophecy\ObjectProphecy */ + $element = $this->prophesize(RootRenderableInterface::class); + $element->getIdentifier()->willReturn('my-form-element-with-translation-arguments'); + $element->getType()->willReturn(RootRenderableInterface::class); + $element->getLabel()->willReturn('See %s or %s'); + $element->getRenderingOptions()->willReturn([ + 'translation' => [ + 'arguments' => [ + 'label' => [ + 'this', + 'that', + ], + ], + ], + ]); + + $expected = 'See this or that'; + $result = $this->mockTranslationService->_call('translateFormElementValue', $element->reveal(), ['label'], $formRuntime->reveal()); + + $this->assertEquals($expected, $result); + } + /** * @test */ @@ -908,6 +950,38 @@ class TranslationServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestC $this->assertEquals($expected, $this->mockTranslationService->_call('translateFinisherOption', $mockFormRuntime, $finisherIdentifier, 'subject', 'subject value', $finisherRenderingOptions)); } + /** + * @test + */ + public function supportsArgumentsForFinisherOptionTranslations() + { + $formRuntimeXlfPath = 'EXT:form/Tests/Unit/Service/Fixtures/locallang_form.xlf'; + + $this->store->flushData($formRuntimeXlfPath); + + /** @var FormRuntime|\Prophecy\Prophecy\ObjectProphecy */ + $formRuntime = $this->prophesize(FormRuntime::class); + $formRuntime->getIdentifier()->willReturn('my-form-runtime-identifier'); + $formRuntime->getRenderingOptions()->willReturn([ + 'translation' => [ + 'translationFile' => $formRuntimeXlfPath, + 'translatePropertyValueIfEmpty' => true, + ], + ]); + $renderingOptions = [ + 'arguments' => [ + 'subject' => [ + 'awesome', + ], + ], + ]; + + $expected = 'My awesome subject'; + $result = $this->mockTranslationService->_call('translateFinisherOption', $formRuntime->reveal(), 'EmailToReceiverWithTranslationArguments', 'subject', 'My %s subject', $renderingOptions); + + $this->assertEquals($expected, $result); + } + /** * @test */ -- GitLab