From 0d1db41c78cbbe5e8e490f89da3df2413458fcdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Buchmann?= <andy.schliesser@gmail.com>
Date: Wed, 12 Jul 2023 15:29:31 +0200
Subject: [PATCH] [BUGFIX] Localize children of all languages element
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

When the parent record has been set to sys_language_uid=-1, but the
child record has translations, it should be displayed in the correct
language. Setting the languageUid for the querySettings to -1 results
in display of the default language record of the child.

Resolves: #92768
Releases: main, 12.4, 11.5
Change-Id: I25bcdbb182c1b35cbc08e5f91339453b0986cdcc
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/80012
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Stefan Bürk <stefan@buerk.tech>
---
 composer.json                                 |   1 +
 .../Persistence/Generic/Mapper/DataMapper.php |   6 +-
 .../Classes/Controller/MainController.php     |  36 +++++
 .../Classes/Domain/Model/Child.php            |  44 ++++++
 .../Classes/Domain/Model/Main.php             | 135 ++++++++++++++++++
 .../Classes/Domain/Model/Squeeze.php          |  65 +++++++++
 .../Domain/Repository/MainRepository.php      |  43 ++++++
 .../Configuration/Services.yaml               |   9 ++
 .../TCA/Overrides/sys_template.php            |   7 +
 .../TCA/Overrides/tt_content.php              |  11 ++
 ...entchildtranslation_domain_model_child.php |  72 ++++++++++
 ...rentchildtranslation_domain_model_main.php |  97 +++++++++++++
 ...tchildtranslation_domain_model_squeeze.php |  90 ++++++++++++
 .../TypoScript/constants.typoscript           |  10 ++
 .../Configuration/TypoScript/setup.typoscript |  10 ++
 .../Resources/Private/Templates/List.html     |  20 +++
 .../parent_child_translation/composer.json    |  19 +++
 .../parent_child_translation/ext_emconf.php   |  21 +++
 .../ext_localconf.php                         |  18 +++
 .../parent_child_translation/ext_tables.sql   |  24 ++++
 .../parentChildTranslationExampleData.csv     |  24 ++++
 .../ParentChildTranslationTest.php            |  95 ++++++++++++
 22 files changed, 856 insertions(+), 1 deletion(-)
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Controller/MainController.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Child.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Main.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Squeeze.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Repository/MainRepository.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/Services.yaml
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/Overrides/sys_template.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/Overrides/tt_content.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_child.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_main.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_squeeze.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TypoScript/constants.typoscript
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TypoScript/setup.typoscript
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Resources/Private/Templates/List.html
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/composer.json
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_emconf.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_localconf.php
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_tables.sql
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/parentChildTranslationExampleData.csv
 create mode 100644 typo3/sysext/extbase/Tests/Functional/Persistence/ParentChildTranslationTest.php

diff --git a/composer.json b/composer.json
index b005717236ed..d46d9a8ac8b4 100644
--- a/composer.json
+++ b/composer.json
@@ -299,6 +299,7 @@
 			"TYPO3Tests\\ActionControllerArgumentTest\\": "typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Extension/action_controller_argument_test/Classes/",
 			"TYPO3Tests\\ActionControllerTest\\": "typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Extension/action_controller_test/Classes/",
 			"TYPO3Tests\\BlogExample\\": "typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/",
+			"TYPO3Tests\\ParentChildTranslation\\": "typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/",
 			"TYPO3Tests\\TestEid\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_eid/Classes/",
 			"TYPO3Tests\\FluidTest\\": "typo3/sysext/fluid/Tests/Functional/Fixtures/Extensions/fluid_test/Classes/",
 			"TYPO3Tests\\FormCachingTests\\" : "typo3/sysext/form/Tests/Functional/RequestHandling/Fixtures/Extensions/form_caching_tests/Classes/",
diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php
index 319643af4a03..386889ab4347 100644
--- a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php
+++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php
@@ -437,7 +437,11 @@ class DataMapper
                 //pass language of parent record to child objects, so they can be overlaid correctly in case
                 //e.g. findByUid is used.
                 //the languageUid is used for getRecordOverlay later on, despite RespectSysLanguage being false
-                $languageUid = (int)$parentObject->_getProperty(AbstractDomainObject::PROPERTY_LANGUAGE_UID);
+                $parentLanguageUid = (int)$parentObject->_getProperty(AbstractDomainObject::PROPERTY_LANGUAGE_UID);
+                // do not override the language when the parent language uid is set to all languages (-1)
+                if ($parentLanguageUid !== -1) {
+                    $languageUid = $parentLanguageUid;
+                }
             }
         }
 
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Controller/MainController.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Controller/MainController.php
new file mode 100644
index 000000000000..dcf1a04dcaf4
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Controller/MainController.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3Tests\ParentChildTranslation\Controller;
+
+use Psr\Http\Message\ResponseInterface;
+use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
+use TYPO3Tests\ParentChildTranslation\Domain\Repository\MainRepository;
+
+final class MainController extends ActionController
+{
+    public function __construct(readonly MainRepository $mainRepository)
+    {
+    }
+
+    public function listAction(): ResponseInterface
+    {
+        $this->view->assign('items', $this->mainRepository->findAll());
+
+        return $this->htmlResponse();
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Child.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Child.php
new file mode 100644
index 000000000000..a130ac04dd3b
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Child.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3Tests\ParentChildTranslation\Domain\Model;
+
+use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
+
+class Child extends AbstractEntity
+{
+    /**
+     * title
+     */
+    protected string $title = '';
+
+    /**
+     * Returns the title
+     */
+    public function getTitle(): string
+    {
+        return $this->title;
+    }
+
+    /**
+     * Sets the title
+     */
+    public function setTitle(string $title): void
+    {
+        $this->title = $title;
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Main.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Main.php
new file mode 100644
index 000000000000..36c5e31aade0
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Main.php
@@ -0,0 +1,135 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3Tests\ParentChildTranslation\Domain\Model;
+
+use TYPO3\CMS\Extbase\Annotation\ORM\Cascade;
+use TYPO3\CMS\Extbase\Annotation\ORM\Lazy;
+use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
+use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
+
+class Main extends AbstractEntity
+{
+    /**
+     * title
+     */
+    protected string $title = '';
+
+    /**
+     * child
+     */
+    protected Child|null $child = null;
+
+    /**
+     * squeeze
+     *
+     * @var ObjectStorage<Squeeze>
+     *
+     * @Cascade("remove")
+     *
+     * @Lazy
+     */
+    protected ObjectStorage $squeeze;
+
+    /**
+     * __construct
+     */
+    public function __construct()
+    {
+        // Do not remove the next line: It would break the functionality
+        $this->initializeObject();
+    }
+
+    /**
+     * Initializes all ObjectStorage properties when model is reconstructed from DB (where __construct is not called)
+     * Do not modify this method!
+     * It will be rewritten on each save in the extension builder
+     * You may modify the constructor of this class instead
+     */
+    public function initializeObject(): void
+    {
+        $this->squeeze = new ObjectStorage();
+    }
+
+    /**
+     * Returns the title
+     */
+    public function getTitle(): string
+    {
+        return $this->title;
+    }
+
+    /**
+     * Sets the title
+     */
+    public function setTitle(string $title): void
+    {
+        $this->title = $title;
+    }
+
+    /**
+     * Returns the child
+     */
+    public function getChild(): Child|null
+    {
+        return $this->child;
+    }
+
+    /**
+     * Sets the child
+     */
+    public function setChild(Child $child): void
+    {
+        $this->child = $child;
+    }
+
+    /**
+     * Adds a Squeeze
+     */
+    public function addSqueeze(Squeeze $squeeze): void
+    {
+        $this->squeeze->attach($squeeze);
+    }
+
+    /**
+     * Removes a Squeeze
+     */
+    public function removeSqueeze(Squeeze $squeezeToRemove): void
+    {
+        $this->squeeze->detach($squeezeToRemove);
+    }
+
+    /**
+     * Returns the squeeze
+     *
+     * @return ObjectStorage<Squeeze>
+     */
+    public function getSqueeze(): ObjectStorage
+    {
+        return $this->squeeze;
+    }
+
+    /**
+     * Sets the squeeze
+     *
+     * @param ObjectStorage<Squeeze> $squeeze
+     */
+    public function setSqueeze(ObjectStorage $squeeze): void
+    {
+        $this->squeeze = $squeeze;
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Squeeze.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Squeeze.php
new file mode 100644
index 000000000000..8e2a0591ef3b
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Model/Squeeze.php
@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3Tests\ParentChildTranslation\Domain\Model;
+
+use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
+
+class Squeeze extends AbstractEntity
+{
+    /**
+     * title
+     */
+    protected string $title = '';
+
+    /**
+     * child
+     */
+    protected Child|null $child = null;
+
+    /**
+     * Returns the title
+     */
+    public function getTitle(): string
+    {
+        return $this->title;
+    }
+
+    /**
+     * Sets the title
+     */
+    public function setTitle(string $title): void
+    {
+        $this->title = $title;
+    }
+
+    /**
+     * Returns the child
+     */
+    public function getChild(): ?Child
+    {
+        return $this->child;
+    }
+
+    /**
+     * Sets the child
+     */
+    public function setChild(Child $child): void
+    {
+        $this->child = $child;
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Repository/MainRepository.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Repository/MainRepository.php
new file mode 100644
index 000000000000..5d08cc5abb96
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Classes/Domain/Repository/MainRepository.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3Tests\ParentChildTranslation\Domain\Repository;
+
+use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
+use TYPO3\CMS\Extbase\Persistence\QueryInterface;
+use TYPO3\CMS\Extbase\Persistence\Repository;
+use TYPO3Tests\ParentChildTranslation\Domain\Model\Main;
+
+/**
+ * The repository for Mains
+ *
+ * @extends Repository<Main>
+ */
+class MainRepository extends Repository
+{
+    protected $defaultOrderings = [
+        'uid' => QueryInterface::ORDER_ASCENDING,
+    ];
+
+    public function initializeObject(): void
+    {
+        /** @var Typo3QuerySettings $defaultQuerySettings */
+        $defaultQuerySettings = $this->createQuery()->getQuerySettings();
+        $defaultQuerySettings->setRespectStoragePage(false);
+        $this->setDefaultQuerySettings($defaultQuerySettings);
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/Services.yaml b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/Services.yaml
new file mode 100644
index 000000000000..5d18bcd3e8ef
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/Services.yaml
@@ -0,0 +1,9 @@
+services:
+  _defaults:
+    autowire: true
+    autoconfigure: true
+    public: false
+
+  TYPO3Tests\ParentChildTranslation\:
+    resource: '../Classes/*'
+    exclude: '../Classes/Domain/{Model,Validator}'
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/Overrides/sys_template.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/Overrides/sys_template.php
new file mode 100644
index 000000000000..f85e5327fb85
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/Overrides/sys_template.php
@@ -0,0 +1,7 @@
+<?php
+
+use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
+
+defined('TYPO3') or die();
+
+ExtensionManagementUtility::addStaticFile('parent_child_translation', 'Configuration/TypoScript', 'Parent Child Translation');
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/Overrides/tt_content.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/Overrides/tt_content.php
new file mode 100644
index 000000000000..f8163b1f890f
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/Overrides/tt_content.php
@@ -0,0 +1,11 @@
+<?php
+
+use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
+
+defined('TYPO3') or die();
+
+ExtensionUtility::registerPlugin(
+    'ParentChildTranslation',
+    'ParentChildTranslation',
+    'ParentChildTranslation'
+);
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_child.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_child.php
new file mode 100644
index 000000000000..b7c3722cd15f
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_child.php
@@ -0,0 +1,72 @@
+<?php
+
+return [
+    'ctrl' => [
+        'title' => 'Child',
+        'label' => 'title',
+        'tstamp' => 'tstamp',
+        'crdate' => 'crdate',
+        'versioningWS' => true,
+        'languageField' => 'sys_language_uid',
+        'transOrigPointerField' => 'l10n_parent',
+        'transOrigDiffSourceField' => 'l10n_diffsource',
+        'delete' => 'deleted',
+        'enablecolumns' => [
+            'disabled' => 'hidden',
+        ],
+        'searchFields' => 'title',
+        'security' => [
+            'ignorePageTypeRestriction' => true,
+        ],
+    ],
+    'types' => [
+        '1' => ['showitem' => 'title,
+        --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, sys_language_uid, l10n_parent, l10n_diffsource, --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden',
+        ],
+    ],
+    'columns' => [
+        'sys_language_uid' => [
+            'exclude' => true,
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
+            'config' => [
+                'type' => 'language',
+            ],
+        ],
+        'l10n_parent' => [
+            'displayCond' => 'FIELD:sys_language_uid:>:0',
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
+            'config' => [
+                'type' => 'select',
+                'renderType' => 'selectSingle',
+                'default' => 0,
+                'items' => [
+                    ['label' => '', 'value' => 0],
+                ],
+                'foreign_table' => 'tx_parentchildtranslation_domain_model_main',
+                'foreign_table_where' => 'AND {#tx_parentchildtranslation_domain_model_main}.{#pid}=###CURRENT_PID### AND {#tx_parentchildtranslation_domain_model_main}.{#sys_language_uid} IN (-1,0)',
+            ],
+        ],
+        'l10n_diffsource' => [
+            'config' => [
+                'type' => 'passthrough',
+            ],
+        ],
+        'hidden' => [
+            'exclude' => true,
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.hidden',
+            'config' => [
+                'type' => 'check',
+            ],
+        ],
+        'title' => [
+            'exclude' => true,
+            'label' => 'Title',
+            'config' => [
+                'type' => 'input',
+                'size' => 30,
+                'eval' => 'trim',
+                'default' => '',
+            ],
+        ],
+    ],
+];
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_main.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_main.php
new file mode 100644
index 000000000000..263bcb1e0d53
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_main.php
@@ -0,0 +1,97 @@
+<?php
+
+return [
+    'ctrl' => [
+        'title' => 'Parent',
+        'label' => 'title',
+        'tstamp' => 'tstamp',
+        'crdate' => 'crdate',
+        'versioningWS' => true,
+        'languageField' => 'sys_language_uid',
+        'transOrigPointerField' => 'l10n_parent',
+        'transOrigDiffSourceField' => 'l10n_diffsource',
+        'delete' => 'deleted',
+        'enablecolumns' => [
+            'disabled' => 'hidden',
+        ],
+        'searchFields' => 'title',
+        'security' => [
+            'ignorePageTypeRestriction' => true,
+        ],
+    ],
+    'types' => [
+        '1' => ['showitem' => 'title, child, squeeze,
+        --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, sys_language_uid, l10n_parent, l10n_diffsource, --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden',
+        ],
+    ],
+    'columns' => [
+        'sys_language_uid' => [
+            'exclude' => true,
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
+            'config' => [
+                'type' => 'language',
+            ],
+        ],
+        'l10n_parent' => [
+            'displayCond' => 'FIELD:sys_language_uid:>:0',
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
+            'config' => [
+                'type' => 'select',
+                'renderType' => 'selectSingle',
+                'default' => 0,
+                'items' => [
+                    ['label' => '', 'value' => 0],
+                ],
+                'foreign_table' => 'tx_parentchildtranslation_domain_model_main',
+                'foreign_table_where' => 'AND {#tx_parentchildtranslation_domain_model_main}.{#pid}=###CURRENT_PID### AND {#tx_parentchildtranslation_domain_model_main}.{#sys_language_uid} IN (-1,0)',
+            ],
+        ],
+        'l10n_diffsource' => [
+            'config' => [
+                'type' => 'passthrough',
+            ],
+        ],
+        'hidden' => [
+            'exclude' => true,
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.hidden',
+            'config' => [
+                'type' => 'check',
+            ],
+        ],
+        'title' => [
+            'exclude' => true,
+            'label' => 'Title',
+            'config' => [
+                'type' => 'input',
+                'size' => 30,
+                'eval' => 'trim',
+                'default' => '',
+            ],
+        ],
+        'child' => [
+            'exclude' => true,
+            'label' => 'Child',
+            'config' => [
+                'type' => 'select',
+                'renderType' => 'selectSingle',
+                'foreign_table' => 'tx_parentchildtranslation_domain_model_child',
+                'foreign_table_where' => 'AND {#tx_parentchildtranslation_domain_model_child}.{#sys_language_uid} IN (0,-1)',
+                'default' => 0,
+                'minitems' => 0,
+                'maxitems' => 1,
+            ],
+        ],
+        'squeeze' => [
+            'exclude' => true,
+            'label' => 'Squeeze',
+            'config' => [
+                'type' => 'inline',
+                'foreign_table' => 'tx_parentchildtranslation_domain_model_squeeze',
+                'foreign_table_where' => 'AND {#tx_parentchildtranslation_domain_model_squeeze}.{#sys_language_uid} IN (0,-1)',
+                'foreign_field' => 'parent',
+                'maxitems' => 1,
+                'default' => 0,
+            ],
+        ],
+    ],
+];
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_squeeze.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_squeeze.php
new file mode 100644
index 000000000000..519ef024f954
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TCA/tx_parentchildtranslation_domain_model_squeeze.php
@@ -0,0 +1,90 @@
+<?php
+
+return [
+    'ctrl' => [
+        'title' => 'Squeeze',
+        'label' => 'title',
+        'tstamp' => 'tstamp',
+        'crdate' => 'crdate',
+        'versioningWS' => true,
+        'languageField' => 'sys_language_uid',
+        'transOrigPointerField' => 'l10n_parent',
+        'transOrigDiffSourceField' => 'l10n_diffsource',
+        'delete' => 'deleted',
+        'enablecolumns' => [
+            'disabled' => 'hidden',
+        ],
+        'searchFields' => 'title',
+        'security' => [
+            'ignorePageTypeRestriction' => true,
+        ],
+    ],
+    'types' => [
+        '1' => ['showitem' => 'title, child,
+        --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, sys_language_uid, l10n_parent, l10n_diffsource, --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden',
+        ],
+    ],
+    'columns' => [
+        'sys_language_uid' => [
+            'exclude' => true,
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
+            'config' => [
+                'type' => 'language',
+            ],
+        ],
+        'l10n_parent' => [
+            'displayCond' => 'FIELD:sys_language_uid:>:0',
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent',
+            'config' => [
+                'type' => 'select',
+                'renderType' => 'selectSingle',
+                'default' => 0,
+                'items' => [
+                    ['label' => '', 'value' => 0],
+                ],
+                'foreign_table' => 'tx_parentchildtranslation_domain_model_main',
+                'foreign_table_where' => 'AND {#tx_parentchildtranslation_domain_model_main}.{#pid}=###CURRENT_PID### AND {#tx_parentchildtranslation_domain_model_main}.{#sys_language_uid} IN (-1,0)',
+            ],
+        ],
+        'l10n_diffsource' => [
+            'config' => [
+                'type' => 'passthrough',
+            ],
+        ],
+        'hidden' => [
+            'exclude' => true,
+            'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.hidden',
+            'config' => [
+                'type' => 'check',
+            ],
+        ],
+        'title' => [
+            'exclude' => true,
+            'label' => 'Title',
+            'config' => [
+                'type' => 'input',
+                'size' => 30,
+                'eval' => 'trim',
+                'default' => '',
+            ],
+        ],
+        'child' => [
+            'exclude' => true,
+            'label' => 'Child',
+            'config' => [
+                'type' => 'select',
+                'renderType' => 'selectSingle',
+                'foreign_table' => 'tx_parentchildtranslation_domain_model_child',
+                'foreign_table_where' => 'AND {#tx_parentchildtranslation_domain_model_child}.{#sys_language_uid} IN (0,-1)',
+                'default' => 0,
+                'minitems' => 0,
+                'maxitems' => 1,
+            ],
+        ],
+        'parent' => [
+            'config' => [
+                'type' => 'passthrough',
+            ],
+        ],
+    ],
+];
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TypoScript/constants.typoscript b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TypoScript/constants.typoscript
new file mode 100644
index 000000000000..65cd115122d3
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TypoScript/constants.typoscript
@@ -0,0 +1,10 @@
+plugin.tx_parentchildtranslation_parentchildtranslation {
+    view {
+        # cat=plugin.tx_parentchildtranslation_parentchildtranslation/file; type=string; label=Path to template root (FE)
+        templateRootPath = EXT:parent_child_translation/Resources/Private/Templates/
+        # cat=plugin.tx_parentchildtranslation_parentchildtranslation/file; type=string; label=Path to template partials (FE)
+        partialRootPath = EXT:parent_child_translation/Resources/Private/Partials/
+        # cat=plugin.tx_parentchildtranslation_parentchildtranslation/file; type=string; label=Path to template layouts (FE)
+        layoutRootPath = EXT:parent_child_translation/Resources/Private/Layouts/
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TypoScript/setup.typoscript b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TypoScript/setup.typoscript
new file mode 100644
index 000000000000..6ce2f16a2b21
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Configuration/TypoScript/setup.typoscript
@@ -0,0 +1,10 @@
+plugin.tx_parentchildtranslation_parentchildtranslation {
+    view {
+        templateRootPaths.0 = EXT:parent_child_translation/Resources/Private/Templates/
+        templateRootPaths.1 = {$plugin.tx_parentchildtranslation_parentchildtranslation.view.templateRootPath}
+        partialRootPaths.0 = EXT:parent_child_translation/Resources/Private/Partials/
+        partialRootPaths.1 = {$plugin.tx_parentchildtranslation_parentchildtranslation.view.partialRootPath}
+        layoutRootPaths.0 = EXT:parent_child_translation/Resources/Private/Layouts/
+        layoutRootPaths.1 = {$plugin.tx_parentchildtranslation_parentchildtranslation.view.layoutRootPath}
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Resources/Private/Templates/List.html b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Resources/Private/Templates/List.html
new file mode 100644
index 000000000000..bb389a748fcc
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/Resources/Private/Templates/List.html
@@ -0,0 +1,20 @@
+<html xmlns:f="https://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
+<ul>
+    <f:for as="item" each="{items}" iteration="iteration">
+        <li>Parents:
+            <ul>
+                <li>Parent: {item.title}</li>
+                <li>Child: {item.child.title}</li>
+                <li>Squeezes:
+                    <ul>
+                        <f:for as="squeeze" each="{item.squeeze}" iteration="iteration">
+                            <li>Squeeze: {squeeze.title}
+                            <li>Child: {squeeze.child.title}</li>
+                        </f:for>
+                    </ul>
+                </li>
+            </ul>
+        </li>
+    </f:for>
+</ul>
+</html>
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/composer.json b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/composer.json
new file mode 100644
index 000000000000..3dafef50c9cf
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/composer.json
@@ -0,0 +1,19 @@
+{
+    "name": "typo3tests/parent-child-translation",
+    "type": "typo3-cms-extension",
+    "description": "Test extension for Forge issue #92768",
+	"license": "GPL-2.0-or-later",
+	"require": {
+		"typo3/cms-core": "13.0.*@dev"
+	},
+    "autoload": {
+        "psr-4": {
+            "TYPO3Tests\\ParentChildTranslation\\": "Classes/"
+        }
+    },
+    "extra": {
+        "typo3/cms": {
+            "extension-key": "parent_child_translation"
+        }
+    }
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_emconf.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_emconf.php
new file mode 100644
index 000000000000..41968970ebd3
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_emconf.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+$EM_CONF[$_EXTKEY] = [
+    'title' => 'parent_child_translation',
+    'description' => 'Test extension for Forge issue #92768',
+    'category' => 'example',
+    'author' => 'TYPO3 core team',
+    'author_company' => '',
+    'author_email' => '',
+    'state' => 'stable',
+    'version' => '13.0.0',
+    'constraints' => [
+        'depends' => [
+            'typo3' => '13.0.0',
+        ],
+        'conflicts' => [],
+        'suggests' => [],
+    ],
+];
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_localconf.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_localconf.php
new file mode 100644
index 000000000000..c02e4dc1030c
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_localconf.php
@@ -0,0 +1,18 @@
+<?php
+
+use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
+use TYPO3Tests\ParentChildTranslation\Controller\MainController;
+
+defined('TYPO3') or die();
+
+ExtensionUtility::configurePlugin(
+    'ParentChildTranslation',
+    'ParentChildTranslation',
+    [
+        MainController::class => 'list',
+    ],
+    // non-cacheable actions
+    [
+        MainController::class => 'list',
+    ]
+);
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_tables.sql b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_tables.sql
new file mode 100644
index 000000000000..2e839042fbf9
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation/ext_tables.sql
@@ -0,0 +1,24 @@
+#
+# Table structure for table 'tx_parentchildtranslation_domain_model_main'
+#
+CREATE TABLE tx_parentchildtranslation_domain_model_main (
+	title varchar(255) NOT NULL DEFAULT '',
+	child int(11) unsigned DEFAULT '0' NOT NULL,
+	squeeze int(11) unsigned DEFAULT '0' NOT NULL,
+);
+
+#
+# Table structure for table 'tx_parentchildtranslation_domain_model_squeeze'
+#
+CREATE TABLE tx_parentchildtranslation_domain_model_squeeze (
+	title varchar(255) NOT NULL DEFAULT '',
+	parent int(11) unsigned DEFAULT '0' NOT NULL,
+	child int(11) unsigned DEFAULT '0' NOT NULL,
+);
+
+#
+# Table structure for table 'tx_parentchildtranslation_domain_model_child'
+#
+CREATE TABLE tx_parentchildtranslation_domain_model_child (
+	title varchar(255) NOT NULL DEFAULT ''
+);
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/parentChildTranslationExampleData.csv b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/parentChildTranslationExampleData.csv
new file mode 100644
index 000000000000..5395f1c45058
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/parentChildTranslationExampleData.csv
@@ -0,0 +1,24 @@
+pages,,,,,,,
+,uid,pid,sys_language_uid,l10n_parent,title,,
+,1,0,0,0,ParentChildTranslation,,
+,2,0,1,1,ElternKindÃœbersetzung,,
+tx_parentchildtranslation_domain_model_child,,,,,,,
+,uid,pid,sys_language_uid,l10n_parent,title,,
+,1,1,0,0,Child 1 EN,,
+,2,1,0,0,Child 2 EN,,
+,3,1,1,1,Kind 1 DE,,
+,4,1,1,2,Kind 2 DE,,
+,5,1,0,0,Child 3 EN,,
+,6,1,1,5,Kind 3 DE,,
+tx_parentchildtranslation_domain_model_main,,,,,,,
+,uid,pid,sys_language_uid,l10n_parent,title,child,squeeze
+,1,1,-1,0,Parent 1,1,1
+,2,1,-1,0,Parent 2,2,1
+tx_parentchildtranslation_domain_model_squeeze,,,,,,,
+,uid,pid,sys_language_uid,l10n_parent,title,parent,child
+,1,1,-1,0,Squeeze 1,1,1
+,2,1,-1,0,Squeeze 2,2,5
+tt_content,,,,,,,
+,uid,pid,sys_language_uid,l18n_parent,header,CType,list_type
+,1,1,0,0,Parent Child Translation,list,parentchildtranslation_parentchildtranslation
+,2,1,1,1,Eltern Kind Ãœbersetzung,list,parentchildtranslation_parentchildtranslation
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/ParentChildTranslationTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/ParentChildTranslationTest.php
new file mode 100644
index 000000000000..516431e79c17
--- /dev/null
+++ b/typo3/sysext/extbase/Tests/Functional/Persistence/ParentChildTranslationTest.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Extbase\Tests\Functional\Persistence;
+
+use TYPO3\CMS\Core\Context\LanguageAspect;
+use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+use TYPO3Tests\ParentChildTranslation\Domain\Repository\MainRepository;
+
+use function PHPUnit\Framework\assertCount;
+
+final class ParentChildTranslationTest extends FunctionalTestCase
+{
+    protected array $testExtensionsToLoad = ['typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/parent_child_translation'];
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->importCSVDataSet(__DIR__ . '/Fixtures/parentChildTranslationExampleData.csv');
+    }
+
+    /**
+     * @test
+     */
+    public function localizeChildrenOfAllLanguageElementToDefaultLanguage(): void
+    {
+        $query = $this->get(MainRepository::class)->createQuery();
+        $results = $query->execute();
+
+        assertCount(2, $results);
+
+        $children = [];
+        foreach ($results as $main) {
+            $children[] = $main->getChild()->getTitle();
+            $children[] = $main->getSqueeze()[0]->getChild()->getTitle();
+        }
+
+        self::assertSame(
+            [
+                'Child 1 EN',
+                'Child 1 EN',
+                'Child 2 EN',
+                'Child 3 EN',
+            ],
+            $children
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function localizesChildrenOfAllLanguageElementToTranslation(): void
+    {
+        $query = $this->get(MainRepository::class)->createQuery();
+        $querySettings = $query->getQuerySettings();
+        $querySettings->setStoragePageIds([1]);
+        $querySettings->setRespectSysLanguage(true);
+        $querySettings->setLanguageAspect(new LanguageAspect(1, 1, LanguageAspect::OVERLAYS_OFF));
+
+        $results = $query->execute();
+
+        assertCount(2, $results);
+
+        $children = [];
+        foreach ($results as $main) {
+            $children[] = $main->getChild()->getTitle();
+            $children[] = $main->getSqueeze()[0]->getChild()->getTitle();
+        }
+
+        self::assertSame(
+            [
+                'Kind 1 DE',
+                'Kind 1 DE',
+                'Kind 2 DE',
+                'Kind 3 DE',
+            ],
+            $children
+        );
+    }
+}
-- 
GitLab