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,
         ];
     }
 }