Important: #82363 - Make Extbase translation handling consistent with TypoScript
See forge#82363
Description
Extbase now renders the translated records in the same way TypoScript rendering does.
The new behaviour is controlled by the Extbase feature switch 
        consistent.
config.tx_extbase.features.consistentTranslationOverlayHandling = 1The new behaviour is enabled by default in TYPO3 v9. The feature switch will be removed in TYPO3 v10, so there will be just one way of fetching records. You can override the setting using normal TypoScript.
Impact
Users relying on the old behaviour can disable the feature switch.
The change modifies how Extbase interprets the TypoScript settings
        config. and 
        config. and the
        Typo3Query properties 
        language and 
        language.
Changes in the rendering:
- 
    Setting Typo3Querydoes not influence how Extbase queries records anymore. The corresponding TypoScript settingSettings->language Mode config.is used by the core to decide what to do when a page is not translated to the given language (display 404, or try page with different language). Users who used to setsys_ language_ mode Typo3QuerytoSettings->language Mode strictshould useTypo3Queryto get translated records only.Settings->set Language Overlay Mode ('hide Non Translated') The old behavior was confusing, because languagehad different meaning and accepted different values in TS context and in Extbase context.Mode 
- 
    Setting Typo3QuerytoSettings->language Overlay Mode truemakes Extbase fetch records from default language and overlay them with translated values. So e.g. when a record is hidden in the default language, it will not be shown. Also records without translation parents will not be shown. For relations, Extbase reads relations from a translated record (so it’s not possible to inherit a field value from translation source) and then passes the related records through$page. So e.g. when you have a translatedRepository->get Record Overlay () tt_with FAL relation, Extbase will show only thosecontent sys_records which are connected to the translated record (not caring whether some of these files havefile_ reference l10n_set).parent Previously Typo3Queryhad no effect. Extbase always performed an overlay process on the result set.Settings->language Overlay Mode 
- 
    Setting Typo3QuerytoSettings->language Overlay Mode falsemakes Extbase fetch aggregate root records from a given language only. Extbase will follow relations (child records) as they are, without checking theirsys_fields, and then it will pass these records throughlanguage_ uid $page. This way the aggregate root record's sorting and visibility doesn't depend on default language records. Moreover, the relations of a record, which are often stored using default language uids, are translated in the final result set (so overlay happens).Repository->get Record Overlay () For example: Given a translated tt_having relation to 2 categories (in the mm table translated tt_content record is connected to category uid in default language), and one of the categories is translated. Extbase will return acontent tt_model with both categories. If you want to have just translated category shown, remove the relation in the translatedcontent tt_record in the TYPO3 Backend.content 
Note that by default 
        Typo3Query uses the global TypoScript configuration like
        config. and 
        $GLOBALS
(calculated based on 
        config. and 
        config.).
So you need to change 
        Typo3Query manually only if your Extbase code should
behave different than other tt_ rendering.
Setting 
        set on a query influences only fetching of the aggregate root. Relations are always
fetched with 
        set.
When querying data in translated language, and having 
        set, the relations
(child objects) are overlaid even if aggregate root is not translated.
See 
        Query.
Following examples show how to query data in Extbase in different scenarios, independent of the global TS settings:
- 
    Fetch records from the language uid=1 only, with no overlays. Previously ( consistent):Translation Overlay Handling = 0 It was not possible. Now ( consistent):Translation Overlay Handling = 1 
$querySettings = $query->getQuerySettings();
$querySettings->setLanguageUid(1);
$querySettings->setLanguageOverlayMode(false);- Fetch records from the language uid=1, with overlay, but hide non-translated records
Previously (consistent):Translation Overlay Handling = 0 
 $querySettings = $query->getQuerySettings();
 $querySettings->setLanguageUid(1);
 $querySettings->setLanguageMode('strict');
Now (:typoscript:`consistentTranslationOverlayHandling = 1`):$querySettings = $query->getQuerySettings();
$querySettings->setLanguageUid(1);
$querySettings->setLanguageOverlayMode('hideNonTranslated');| QuerySettings property | old behaviour | new behaviour | default value (TSFE|Extbase) | 
|---|---|---|---|
| languageUid | same | 0 | |
| respectSysLanguage | same | true | |
| languageOverlayMode | not used | values: true,false,hide | 0 | true | 
| languageMode | documented values: null,content_,strictorignore.
Onlystrictwas evaluated. SettingLanguagetostrictcaused passinghideparam togetin
        Typo3Dband changing the query to work similar to TypoScriptsys_ | not used | null | 
Identifiers
Domain models have a main identifier uid and two additional properties _localized and _versioned.
Depending on whether the language mode is enabled (true or 'hide) or disabled (false),
the identifier contains different values.
When language is enabled then uid property contains uid value of the default language record,
the uid of the translated record is kept in the _localized.
| Context | Record in language 0 | Translated record | 
|---|---|---|
| Database | uid:2 | uid:11, l10n_parent:2 | 
| Domain Object values with languageenabled | uid:2, _localizedUid:2 | uid:2, _localizedUid:11 | 
| Domain Object values with languagedisabled | uid:2, _localizedUid:2 | uid:11, _localizedUid:11 | 
See tests in extbase/.
The 
        $repository->find (or 
        $persistence) method takes current
rendering language into account (e.g. L=1). It does not take default set on the repository into account.
This method always performs an overlay.
Values in braces show previous behaviour (disabled flag) if different than current.
The bottom line is that with the feature flag on, you can now use  
        find using translated record uid to get
translated content independently from language set in global context.
| L=0 | L=1 | ||||
|---|---|---|---|---|---|
| repository method | property | Overlay | No overlay | Overlay | No overlay | 
| findByUid(2) | title | Post 2 | Post 2 | Post 2 - DA | Post 2 - DA | 
| uid | 2 | 2 | 2 | 2 | |
| _localizedUid | 2 | 2 | 11 | 11 | |
| findByUid(11) | title | Post 2 - DA (Post 2) | Post 2 - DA (Post 2) | Post 2 - DA | Post 2 - DA | 
| uid | 2 | 2 | 2 | 2 | |
| _localizedUid | 11 (2) | 11 (2) | 11 | 11 | |
Note
Note that 
        $repository->find internally sets 
        respect so it behaves differently
than a regular query by an uid like 
        $query->matching
The regular query will return 
        null if passed uid doesn't match
the language set in the 
        $query method.
Filtering & sorting
When filtering by aggregate root property like Post->title,
both filtering and sorting takes translated values into account and you will get correct results, also with pagination.
When filtering or ordering by child object property, then Extbase does a left join between aggregate root
table and child record table.
Then the filter is applied as where clause. This means that filtering or ordering by child record property
only takes values from child records which uids are stored in db (in most cases its default language record).
See 
        Translation
This limitation also applies to Extbase with feature flag being disabled.
Summary of the important code changes
- Datagets a- Mapper - Queryas a constructor parameter. This allows to use aggregate root- Query(language) when fetching child records/relations. Later, in a separate patch we can pass other settings too e.g.- Settings - setto fix issue around this setting. See- Ignore - Enable - Fields - Datamethod.- Mapper->get - Prepared - Query 
- Datais passed to- Mapper - Lazyand- Loading - Proxy - Lazy, so the settings don't get lost when fetching data lazily.- Object - Storage 
- Queryobject gets a new property- parentwhich is useful to detect whether we're fetching aggregate root or child object.- Query 
- Extbase model for  FileusesReference _localizedfor fetchingUid OriginalResource 
- Dataforces child records to be fetched using- Mapper - set.- Language - Overlay - Mode - (true) 
- When getRespectSysLanguage is set,  Datauses aggregate root language to overlay child records to correct language.Mapper 
- The whereclause used for finding translated records in overlay mode (true,hide) has been fixed. It filters out the non translated records on db side in caseNon Translated hideis set. It allows for filtering and sorting by translated values. SeeNon Translated Typo3DbQuery Parser->get Language Statement () 
Most important known issues (this patch doesn't solve)
- Persistence session uses the same key for default language record and the translation - https://forge.typo3.org/issues/59992
- Extbase allows to fetch deleted/hidden records - https://forge.typo3.org/issues/86307
For more information about rendering please refer to the TypoScript reference.