diff --git a/typo3/sysext/core/Classes/Localization/Parser/XliffParser.php b/typo3/sysext/core/Classes/Localization/Parser/XliffParser.php index 7643bb70588cad07a4e297e56b5c821e725c9520..d84360cb7c81c4e4a5fc69f839d32285047bd28c 100644 --- a/typo3/sysext/core/Classes/Localization/Parser/XliffParser.php +++ b/typo3/sysext/core/Classes/Localization/Parser/XliffParser.php @@ -31,6 +31,7 @@ class XliffParser extends AbstractXmlParser { $parsedData = []; $bodyOfFileTag = $root->file->body; + $requireApprovedLocalizations = (bool)($GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['requireApprovedLocalizations'] ?? true); if ($bodyOfFileTag instanceof \SimpleXMLElement) { foreach ($bodyOfFileTag->children() as $translationElement) { /** @var \SimpleXMLElement $translationElement */ @@ -43,11 +44,13 @@ class XliffParser extends AbstractXmlParser 'target' => (string)$translationElement->source, ]; } else { - // @todo Support "approved" attribute - $parsedData[(string)$translationElement['id']][0] = [ - 'source' => (string)$translationElement->source, - 'target' => (string)$translationElement->target, - ]; + $approved = (string)($translationElement['approved'] ?? ''); + if (!$requireApprovedLocalizations || $approved === 'yes') { + $parsedData[(string)$translationElement['id']][0] = [ + 'source' => (string)$translationElement->source, + 'target' => (string)$translationElement->target, + ]; + } } } elseif ($translationElement->getName() === 'group' && isset($translationElement['restype']) && (string)$translationElement['restype'] === 'x-gettext-plurals') { // This is a translation with plural forms @@ -64,11 +67,13 @@ class XliffParser extends AbstractXmlParser 'target' => (string)$translationPluralForm->source, ]; } else { - // @todo Support "approved" attribute - $parsedTranslationElement[(int)$formIndex] = [ - 'source' => (string)$translationPluralForm->source, - 'target' => (string)$translationPluralForm->target, - ]; + $approved = (string)($translationPluralForm['approved'] ?? ''); + if (!$requireApprovedLocalizations || $approved === 'yes') { + $parsedTranslationElement[(int)$formIndex] = [ + 'source' => (string)$translationPluralForm->source, + 'target' => (string)$translationPluralForm->target, + ]; + } } } } diff --git a/typo3/sysext/core/Configuration/DefaultConfiguration.php b/typo3/sysext/core/Configuration/DefaultConfiguration.php index 324e9e9de8fb3c293f3bf3cace4682f05ed73691..1c204be9035c6100a28faf2e545b43e219804f50 100644 --- a/typo3/sysext/core/Configuration/DefaultConfiguration.php +++ b/typo3/sysext/core/Configuration/DefaultConfiguration.php @@ -45,6 +45,7 @@ return [ 'SYS' => [ // System related concerning both frontend and backend. 'lang' => [ + 'requireApprovedLocalizations' => true, 'format' => [ 'priority' => 'xlf', ], diff --git a/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml b/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml index ac6e26739a3c38172495c3356be14c6ec32178f6..5f02d9b6fea732ff3de83dcfbafc07665e76df92 100644 --- a/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml +++ b/typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml @@ -207,6 +207,12 @@ SYS: availablePasswordHashAlgorithms: type: array description: 'A list of available password hash mechanisms. Extensions may register additional mechanisms here. This is usually not extended in system/settings.php.' + lang: + type: container + description: 'Settings related to XLF labels' + requireApprovedLocalizations: + type: bool + description: 'If set, translations are only taken into account if the according "approved" attribute is set to "yes" within the XLF file. Otherwise, all available translations are used.' EXT: type: container description: 'Extension Installation' diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97729-SupportAttributeApprovedInXlfFiles.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97729-SupportAttributeApprovedInXlfFiles.rst new file mode 100644 index 0000000000000000000000000000000000000000..9ad0a5a63cc1d8b24f3e00a3aade06580b3fefb4 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97729-SupportAttributeApprovedInXlfFiles.rst @@ -0,0 +1,46 @@ +.. include:: /Includes.rst.txt + +.. _breaking-97729-1654627167 + +========================================================== +Breaking: #97729 - Respect attribute approved in XLF files +========================================================== + +See :issue:`97729` + +Description +=========== + +The new option :php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['requireApprovedLocalizations']` +controls whether only approved translations are taken into account when parsing XLF files. + +This option is enabled by default for new and existing TYPO3 installations. + + +Impact +====== + +If set to `true` - which is the default value - only approved translations are used. +Any non-approved translation will be ignored. + +.. code-block:: xml + + <trans-unit id="label2" resname="label2" approved="yes"> + <source>This is label #2</source> + <target>Ceci est le libellé no. 2</target> + </trans-unit> + + +Affected installations +====================== + +All TYPO3 translations using translations from XLF files. + + +Migration +========= + +Either set :php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['requireApprovedLocalizations']` +to `false` or add `approved="yes"` to all translations. + +.. index:: Backend, Fluid, Frontend, TCA, TypoScript, NotScanned, ext:core diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Feature-97729-SupportAttributeApprovedInXlfFiles.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Feature-97729-SupportAttributeApprovedInXlfFiles.rst new file mode 100644 index 0000000000000000000000000000000000000000..e4d25c6a056755c6087d19feae7cd978746d2c46 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/12.0/Feature-97729-SupportAttributeApprovedInXlfFiles.rst @@ -0,0 +1,41 @@ +.. include:: /Includes.rst.txt + +.. _feature-97729-1654626734 + +========================================================= +Feature: #97729 - Respect attribute approved in XLF files +========================================================= + +See :issue:`97729` + +Description +=========== + +The attribute `approved` of the XLIFF standard is now supported by TYPO3 when +parsing XLF files. This attribute can either have the value `yes` or `no` and +indicates whether the translation is final or not. + +.. code-block:: xml + + <trans-unit id="label2" resname="label2" approved="yes"> + <source>This is label #2</source> + <target>Ceci est le libellé no. 2</target> + </trans-unit> + +The setting :php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['requireApprovedLocalizations']` +can be used to control the behaviour. + +- If it is set to `true` (which is the default setting), only translations with the attribute + `approved` set to `yes` will be used. +- If it is set to `false`, all translations are used. + +This attribute is particularly useful when working with third-party software and translation agencies. +Allowing unapproved translations may increase the number of translations, possibly at the expense of their quality. + +Impact +====== + +Crowdin supports this attribute. Currently only approved translations are exported. +Therefore no change is expected for official translations. + +.. index:: Backend, Fluid, Frontend, TCA, TypoScript, ext:core diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/de.locallang_override.xlf b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/de.locallang_override.xlf index 0bae025497628a1622f158d1b75aa5c3073ce4a1..8f171cf41ac08afc91f8e15757e828ff5db2db66 100644 --- a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/de.locallang_override.xlf +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/de.locallang_override.xlf @@ -3,15 +3,15 @@ <file source-language="en" datatype="plaintext" original="EXT:test_localization/Resources/Private/Language/de.locallang_override.xlf" date="2021-10-24T11:57:33Z" product-name="tests" target-language="de"> <header/> <body> - <trans-unit id="label1" resname="label1"> + <trans-unit id="label1" resname="label1" approved="yes"> <source>This is label #1</source> <target>Das ist Beschriftung 1</target> </trans-unit> - <trans-unit id="label2" resname="label2"> + <trans-unit id="label2" resname="label2" approved="yes"> <source>This is label #2</source> <target>Das ist Beschriftung 2</target> </trans-unit> - <trans-unit id="label3" resname="label3"> + <trans-unit id="label3" resname="label3" approved="yes"> <source>This is label #3</source> <target>Das ist Beschriftung 3</target> </trans-unit> diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang.xlf b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang.xlf index eda46dd52a1241c2f2eb9449d191f55b4e132a53..b9953a5b3980b93d2c9738bd177054ff751099e8 100644 --- a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang.xlf +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang.xlf @@ -3,15 +3,15 @@ <file source-language="en" datatype="plaintext" original="EXT:test_localization/Resources/Private/Language/fr.locallang.xlf" date="2011-12-21T11:57:33Z" product-name="tests" target-language="fr"> <header/> <body> - <trans-unit id="label1" resname="label1"> + <trans-unit id="label1" resname="label1" approved="yes"> <source>This is label #1</source> <target>Ceci est le libellé no. 1</target> </trans-unit> - <trans-unit id="label2" resname="label2"> + <trans-unit id="label2" resname="label2" approved="yes"> <source>This is label #2</source> <target>Ceci est le libellé no. 2</target> </trans-unit> - <trans-unit id="label3" resname="label3"> + <trans-unit id="label3" resname="label3" approved="yes"> <source>This is label #3</source> <target>Ceci est le libellé no. 3</target> </trans-unit> diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang_common_override.xlf b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang_common_override.xlf index 517b5c60663c2e72d94ca3ae93d68e0364521527..c0a1d1c65a1697513d5c28db0dd8a20d393eb29b 100644 --- a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang_common_override.xlf +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang_common_override.xlf @@ -3,15 +3,15 @@ <file source-language="en" datatype="plaintext" original="EXT:core/Resources/Private/Language/locallang_common.xlf" date="2021-10-27T01:16:33Z" product-name="lang" target-language="fr"> <header/> <body> - <trans-unit id="about" resname="about"> + <trans-unit id="about" resname="about" approved="yes"> <source>About</source> <target>A propos</target> </trans-unit> - <trans-unit id="help" resname="help"> + <trans-unit id="help" resname="help" approved="yes"> <source>Help</source> <target>Aide</target> </trans-unit> - <trans-unit id="ok" resname="ok"> + <trans-unit id="ok" resname="ok" approved="yes"> <source>OK</source> <target>OK</target> </trans-unit> diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang_override.xlf b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang_override.xlf index c512171a895954a4196720589b6a685af6f192fb..3da6974a8840fd1426691dc2eac19562c2a47d02 100644 --- a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang_override.xlf +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_localization/Resources/Private/Language/fr.locallang_override.xlf @@ -3,11 +3,11 @@ <file source-language="en" datatype="plaintext" original="EXT:test_localization/Resources/Private/Language/fr.locallang_override.xlf" date="2011-12-21T11:57:33Z" product-name="tests" target-language="fr"> <header/> <body> - <trans-unit id="label1" resname="label1"> + <trans-unit id="label1" resname="label1" approved="yes"> <source>This is label #1</source> <target>Ceci est mon 1er libellé</target> </trans-unit> - <trans-unit id="label3" resname="label3"> + <trans-unit id="label3" resname="label3" approved="yes"> <source>This is label #3</source> <target>Ceci est mon 3e libellé</target> </trans-unit> diff --git a/typo3/sysext/core/Tests/Unit/Localization/Parser/Fixtures/fr.locallang.xlf b/typo3/sysext/core/Tests/Unit/Localization/Parser/Fixtures/fr.locallang.xlf index c860df7de1133dd1255810d4a5587f962dab9191..ff1c5661120d90b387cf3ac6191291347eeea4c0 100644 --- a/typo3/sysext/core/Tests/Unit/Localization/Parser/Fixtures/fr.locallang.xlf +++ b/typo3/sysext/core/Tests/Unit/Localization/Parser/Fixtures/fr.locallang.xlf @@ -7,13 +7,13 @@ <source>This is label #1</source> <target>Ceci est le libellé no. 1</target> </trans-unit> - <trans-unit id="label2" resname="label2"> + <trans-unit id="label2" resname="label2" approved="yes"> <source>This is label #2</source> - <target>Ceci est le libellé no. 2</target> + <target>Ceci est le libellé no. 2 [approved]</target> </trans-unit> - <trans-unit id="label3" resname="label3"> + <trans-unit id="label3" resname="label3" approved="no"> <source>This is label #3</source> - <target>Ceci est le libellé no. 3</target> + <target>Ceci est le libellé no. 3 [not approved]</target> </trans-unit> </body> </file> diff --git a/typo3/sysext/core/Tests/Unit/Localization/Parser/XliffParserTest.php b/typo3/sysext/core/Tests/Unit/Localization/Parser/XliffParserTest.php index 88da60926142c49ab028580f578e154deb98920d..1af0882b628a5f6687f30bea7ae5ee9cb781760b 100644 --- a/typo3/sysext/core/Tests/Unit/Localization/Parser/XliffParserTest.php +++ b/typo3/sysext/core/Tests/Unit/Localization/Parser/XliffParserTest.php @@ -29,8 +29,9 @@ class XliffParserTest extends UnitTestCase * @test * @dataProvider canParseXliffDataProvider */ - public function canParseXliff(string $languageKey, array $expectedLabels): void + public function canParseXliff(string $languageKey, array $expectedLabels, bool $requireApprovedLocalizations): void { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['lang']['requireApprovedLocalizations'] = $requireApprovedLocalizations; $LOCAL_LANG = (new XliffParser())->getParsedData(__DIR__ . '/Fixtures/locallang.xlf', $languageKey); self::assertArrayHasKey($languageKey, $LOCAL_LANG, sprintf('%s key not found in $LOCAL_LANG', $languageKey)); foreach ($expectedLabels as $key => $expectedLabel) { @@ -47,14 +48,23 @@ class XliffParserTest extends UnitTestCase 'label2' => 'This is label #2', 'label3' => 'This is label #3', ], + false, ]; - yield 'Can handle translation' => [ + yield 'Can handle translation with approved only' => [ + 'languageKey' => 'fr', + 'expectedLabels' => [ + 'label2' => 'Ceci est le libellé no. 2 [approved]', + ], + true, + ]; + yield 'Can handle translation with non approved' => [ 'languageKey' => 'fr', 'expectedLabels' => [ 'label1' => 'Ceci est le libellé no. 1', - 'label2' => 'Ceci est le libellé no. 2', - 'label3' => 'Ceci est le libellé no. 3', + 'label2' => 'Ceci est le libellé no. 2 [approved]', + 'label3' => 'Ceci est le libellé no. 3 [not approved]', ], + false, ]; } }