Important: Strict language fallback for selected profiles 

Description 

When profiles are selected explicitly (by uid) for a list plugin ( academicpersons_list / academicpersons_listanddetail with the FlexForm "selected profiles" option), a site language configured with fallbackType: strict did not hide profiles that are not translated into the requested language. Such profiles were rendered in their default language instead of being removed, unless the plugin option "fallback for non translated" was enabled on purpose.

This is not an EXT:academic_persons bug. The extension already resolves the correct language overlay type from the site configuration and only relies on Extbase persistence to apply it. The behaviour is caused by a long-standing Extbase regression that ignored the resolved language overlay type and always overlaid single records with OVERLAYS_MIXED , so untranslated records were kept.

Core references (issue and both patches):

  • Forge issue #88886"DataMapper: Consider languageOverlayMode hideNonTranslated ..."
  • Gerrit change 66694"[BUGFIX] Respect language overlay type in Extbase" (TYPO3 main line)
  • Gerrit change 94935 — the TYPO3 14.3 backport (same Change-Id)

The fix is released with TYPO3 v14.3.6 and newer (and on the TYPO3 main development line). It is not part of TYPO3 v13 and, being a behavioural change, is not backported to the v13.4 LTS.

Impact 

On TYPO3 v14.3.6 and newer the behaviour is correct out of the box: untranslated selected profiles are removed under fallbackType: strict . No configuration or code change is required in EXT:academic_persons .

On TYPO3 v13.4 (and on TYPO3 v14.3.0 - v14.3.5, before the fix shipped) the affected Extbase code still overlays with OVERLAYS_MIXED , so untranslated selected profiles keep being shown in their default language when the site language uses fallbackType: strict and the plugin fallback option is not enabled.

The two functional tests covering this behaviour ( AcademicPersonsListPluginTest and AcademicPersonsListAndDetailPluginTest , test ...WithFallbackTypeStrictWhenNotAllProfilesAreLocalized ) are therefore skipped on TYPO3 below v14.3.6 and run only where the core fix is present.

Affected Installations 

Installations that use selected profiles in a list plugin with a site language configured as fallbackType: strict and expect untranslated profiles to be hidden, running on TYPO3 v13.4 or TYPO3 v14.3.0 - v14.3.5.

Solution 

Upgrade to TYPO3 v14.3.6 or newer, which contains the core fix.

If the correct behaviour is required before that, apply the core change as a composer patch against typo3/cms-extbase until it is part of the installed core version. Cleaned patches (narrowed to typo3/cms-extbase , derived from Gerrit changes 66694 / 94935) are shipped with this extension:

TYPO3 v13.4 patch 

Documentation/Patches/extbase-88886-respect-language-overlay-type-v13.patch

extbase-88886-respect-language-overlay-type-v13.patch (TYPO3 v13.4)
diff -ruN a/Classes/Persistence/Generic/Backend.php b/Classes/Persistence/Generic/Backend.php
--- a/Classes/Persistence/Generic/Backend.php
+++ b/Classes/Persistence/Generic/Backend.php
@@ -148,11 +148,14 @@
         // This allows to fetch IDs for languages for default language AND language IDs
         // This is especially important when using the PropertyMapper of the Extbase MVC part to get
         // an object of the translated version of the incoming ID of a record.
+        // "Free" mode (OVERLAYS_OFF) is mapped to OVERLAYS_MIXED - overlays need to be enabled for the
+        // identity lookup, but hiding untranslated records is not a configured intent in free mode.
+        // This is consistent with the same handling for related objects in DataMapper->getPreparedQuery().
         $languageAspect = $query->getQuerySettings()->getLanguageAspect();
         $languageAspect = new LanguageAspect(
             $languageAspect->getId(),
             $languageAspect->getContentId(),
-            $languageAspect->getOverlayType() === LanguageAspect::OVERLAYS_OFF ? LanguageAspect::OVERLAYS_ON_WITH_FLOATING : $languageAspect->getOverlayType(),
+            $languageAspect->getOverlayType() === LanguageAspect::OVERLAYS_OFF ? LanguageAspect::OVERLAYS_MIXED : $languageAspect->getOverlayType(),
             $languageAspect->getFallbackChain()
         );
         $query->getQuerySettings()->setLanguageAspect($languageAspect);
diff -ruN a/Classes/Persistence/Generic/Storage/Typo3DbBackend.php b/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
--- a/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
+++ b/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
@@ -580,9 +580,16 @@
                     $row['uid'] = $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']];
                     $row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']] = 0;
                 }
-                // Currently this needs to return the default record (OVERLAYS_MIXED) if no translation is found
-                //however this is a hack and should actually use the overlay functionality as given in the original LanguageAspect.
-                $customLanguageAspect = new LanguageAspect($languageUid, $languageUid, LanguageAspect::OVERLAYS_MIXED, $languageAspect->getFallbackChain());
+                // The overlay type (and fallback chain) of the language aspect is respected, so translation
+                // behavior is consistent with the regular page / content rendering. The content language
+                // however may have been adjusted above to the language of the actually fetched record
+                // (see Note #1 and the respectSysLanguage handling), so a custom aspect is passed here.
+                $customLanguageAspect = new LanguageAspect(
+                    $languageAspect->getId(),
+                    $languageUid,
+                    $languageAspect->getOverlayType(),
+                    $languageAspect->getFallbackChain()
+                );
                 $row = $pageRepository->getLanguageOverlay($tableName, $row, $customLanguageAspect);
             }
         } elseif (is_array($row)) {
Copied!

TYPO3 v14.3 patch (v14.3.0 - v14.3.5) 

Documentation/Patches/extbase-88886-respect-language-overlay-type-v14.patch

extbase-88886-respect-language-overlay-type-v14.patch (TYPO3 v14.3.0 - v14.3.5)
--- a/Classes/Persistence/Generic/Backend.php
+++ b/Classes/Persistence/Generic/Backend.php
@@ -165,11 +165,14 @@
         // This allows to fetch IDs for languages for default language AND language IDs
         // This is especially important when using the PropertyMapper of the Extbase MVC part to get
         // an object of the translated version of the incoming ID of a record.
+        // "Free" mode (OVERLAYS_OFF) is mapped to OVERLAYS_MIXED - overlays need to be enabled for the
+        // identity lookup, but hiding untranslated records is not a configured intent in free mode.
+        // This is consistent with the same handling for related objects in DataMapper->getPreparedQuery().
         $languageAspect = $query->getQuerySettings()->getLanguageAspect();
         $languageAspect = new LanguageAspect(
             $languageAspect->getId(),
             $languageAspect->getContentId(),
-            $languageAspect->getOverlayType() === LanguageAspect::OVERLAYS_OFF ? LanguageAspect::OVERLAYS_ON_WITH_FLOATING : $languageAspect->getOverlayType(),
+            $languageAspect->getOverlayType() === LanguageAspect::OVERLAYS_OFF ? LanguageAspect::OVERLAYS_MIXED : $languageAspect->getOverlayType(),
             $languageAspect->getFallbackChain()
         );
 
--- a/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
+++ b/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
@@ -590,9 +590,16 @@
                     $row['uid'] = $row[$translationParentPointerField];
                     $row[$languageField] = 0;
                 }
-                // Currently this needs to return the default record (OVERLAYS_MIXED) if no translation is found
-                //however this is a hack and should actually use the overlay functionality as given in the original LanguageAspect.
-                $customLanguageAspect = new LanguageAspect($languageUid, $languageUid, LanguageAspect::OVERLAYS_MIXED, $languageAspect->getFallbackChain());
+                // The overlay type (and fallback chain) of the language aspect is respected, so translation
+                // behavior is consistent with the regular page / content rendering. The content language
+                // however may have been adjusted above to the language of the actually fetched record
+                // (see Note #1 and the respectSysLanguage handling), so a custom aspect is passed here.
+                $customLanguageAspect = new LanguageAspect(
+                    $languageAspect->getId(),
+                    $languageUid,
+                    $languageAspect->getOverlayType(),
+                    $languageAspect->getFallbackChain()
+                );
                 $row = $pageRepository->getLanguageOverlay($tableName, $row, $customLanguageAspect);
             }
         } elseif (is_array($row)) {
Copied!

Apply the matching patch with the composer plugin cweagans/composer-patches (see its README for installation and usage). Copy the patch into the project (for example into a patches/ directory) and reference it:

{
    "require": {
        "cweagans/composer-patches": "^1.7"
    },
    "extra": {
        "patches": {
            "typo3/cms-extbase": {
                "Respect language overlay type in Extbase (forge #88886, review 66694)": "patches/extbase-88886-respect-language-overlay-type-v13.patch"
            }
        }
    }
}
Copied!

Both patches touch only typo3/cms-extbase (Classes/Persistence/Generic/Storage/Typo3DbBackend.php and Classes/Persistence/Generic/Backend.php); EXT:academic_persons itself needs no change.