Respect sorting of original language in TYPO3 Frontend (like defLangBinding) - typo3

I have some messed up values for tt_content.sorting in TYPO3 6.2 for translated records (sys_language_uid=1).
That means that styles.content.get displays the right order (as visible in the backend) in the original language, but not in the translated language (where in the backend everything looks fine, as it follows defLangBinding but in the frontend, the wrong values from the sorting fields are used, so the sorting is messed up.
We did modify some settings in the last few weeks
// before
// config.sys_language_mode = content_fallback:1,0
// config.sys_language_overlay = hideNonTranslated
// currently
config.sys_language_mode = strict
config.sys_language_overlay =0
but I am unsure if that is connected. The sorting in the DB is really wrong for some fields. It is correctable by moving individual CEs in Page or List module.
There are many related ancient bugs on forge.
My questions:
Is there a script that fixes the sorting for the translated CEs for the entire site in accordance to the main language?
Is there something like defLangBinding for the frontend - that would help as well, as the wrong sorting values would become meaningless. Or a modification for the styles.content.get query?

Why don't you just try to bring your content records in sync again? This could be done by pure SQL if there are no or only a few exclusive records in the second language. If not, you are going to mess it up even more.
This query will give you a table with the default language and the translated record side by side, including the sorting value:
select a.header, a.uid, a.sorting, b.sorting, b.uid, b.header from tt_content as a inner join tt_content as b on a.uid=b.l18n_parent where b.l18n_parent > 0 and a.sys_language_uid = 0 and not a.deleted and not b.deleted;
You could then adapt this query to an update query that sets the sorting value of table b to the one in table a.
To check if you have records without a parent in the default language, you can use this query:
select * from tt_content where sys_language_uid > 0 AND l18n_parent = 0 AND NOT deleted
As for your configuration, have a look at the manual for sys_language_overlay:
If set, records from certain tables selected by the CONTENT cObject
using the “languageField” setting will select the default language (0)
instead of any language set by sys_language_uid / sys_language_mode.
In addition the system will look for a translation of the selected
record and overlay configured fields.
So this seems to be connected to your problem: Before, TYPO3 was (maybe - you didn't post the setting for sys_language_overlay) configured to first fetch records in the default language, try to overlay them with a translation and then output it.
Now that sys_language_overlay is deactivated, the records are fetched in the translated version right away and their sorting is considered.

Related

TYPO3 extbase - query with multiple languages - how to define language for subtables / persistance\ObjectStorage

I have a table with multiple 1:n subtables that have translations.
Now the client wants to list all records on all language versions of the website. The problem is that Extbase caches the values of the different ObjectStorages and does not differentiate between the languages. Now if I have two records with the same record the first one will fetch the title and this one is then being used for all records. So I have a crazy language mis-match.
I would like to define the language overlay of the subtables fixed for sys_language_uid and don't know how. This code: https://gist.github.com/hhoechtl/a374f7526440ba987c19 does work perfectly for a single-select but it does not work for my listview..
Is there any way I could force the language of the subqueries?
As an example. This is an italian record (sys_l = 1) and it gets shown on the german detailview with german subinfo: (https://jobfinder.look4u.it/de/job?tx_jobfinder_stellenanzeigen%5Baction%5D=show&tx_jobfinder_stellenanzeigen%5Bcontroller%5D=Stellenanzeigen&tx_jobfinder_stellenanzeigen%5Bstellenausschreibung%5D=149&cHash=6da4a876cac6125e3b0f5b0924f35463)
When you open it on the italian page ( https://jobfinder.look4u.it/it/job?tx_jobfinder_stellenanzeigen%5Baction%5D=show&tx_jobfinder_stellenanzeigen%5Bcontroller%5D=Stellenanzeigen&tx_jobfinder_stellenanzeigen%5Bstellenausschreibung%5D=149&cHash=6da4a876cac6125e3b0f5b0924f35463 ) the additional info is nicely in italian thanks to the code from hhoechtl above.
But the problem is in the listview where german (0) and italian gets mixed: https://jobfinder.look4u.it/de/anzeigen-arbeitgeber/agenturen
Extbase takes the values of 2 properties from the first record (sales controller) in italian and then keeps that lookup for all folowing records. And I'm not able to change the lookup here in the listview. The code from hhoechtl somehow doesnt work in this query.
I have found a solution. The patch released in July 2022 actually solves exactly this problem!
https://review.typo3.org/c/Packages/TYPO3.CMS/+/75099/2
Since I'm running v10 on this site I have manually implemented the patch on my T3 version and now Extbase is behaving as it should.

How to change page languages automatically for all pages on Typo3

We have changed the primary language for our site https://staging.venicexplorer.net from English to Italian. But the problem is it is showing English text when we selected Italian and vice versa. We are now altering the languages of the content. But this is a very time consuming job. Can anyone please tell me whether there is any shortcut method?
Thanks
Prasun
Here are some hints for updating the language of content and pages in the database:
Content
The language of a content element (table tt_content) is in the field sys_language_uid. Default language is always 0, "all languages" is -1.
Each translated record (sys_language_uid NOT IN (0,1)) can (aka "connected mode") have a link to its default language counterpart. This is set in the field l10n_parent.
Pages
First of all for TYPO3v9 this depends on the feature toggle unifiedPageTranslationHandling. I will assume that your instance is already fully migrated to not use pages_language_overlay any more.
The same rules as for tt_content apply: sys_language_uid is the language, l10n_parent the default language record.
Other records
The same applies to all other translated records in TYPO3 or its extensions.
Technically, these fields are defined for each table in TCA in [ctrl][languageField] and [ctrl][transOrigPointerField] but sys_language_uid and l10n_parent are used mostly.
Notice
This is not an exhaustive list. There are fields that are only set in the default language (e.g. access restrictions). Make a backup first! But that should cover most bases.

Getting translated records in CommandController

I've searched and debugged the last couple of days how to obtain the
translated version of a DomainModel object in a CommandController in Typo3 v8.7.
In Typo3 4.5/4.7 I've done the following:
- input: DomainModel in default language
- build a query that finds the record with the l10n_parent matching the
given domain model
- obtain a new domain model with the desired sys_language_uid
Unfortunately this does not work in Typo3 v8.7 any more. I always get
the domain model for the default language.
I've traced this down to the method Typo3DbBackend::doLanguageAndWorkspaceOverlay
called via Typo3DbBackend::getObjectDataByQuery
The query returns the correct (translated) row (seen in the debugger and also the mysql query log), but then the variable
$row gets overwritten in doLanguageAndWorkspaceOverlay no matter how I
set the querySettings setLanguageOverlayMode and setLanguageMode.
So what is the correct way to get a translated domain model in a
CommandController?
UPDATE:
I think I'm a step further. If I add ->setQueryLanguage(1) to the query settings, doLanguageAndWorkspaceOverlay() tries to fetch the translated record for language = 1. But in order to succeed I need to trick the FrontendGroupRestriction class by setting $GLOBALS['TSFE']->gr_list = "0,-2";.
The array returned by doLanguageAndWorkspaceOverlay() now contains all translated entries, except the uid, which is still the uid from the record in the main language. The uid of the translated record is stored in _LOCALIZED_UID.
Now my problem now is that I still get the record in the main laguage because DataMapper->mapSingleRow() (called via DataMapper->map()) has some kind of object cache and thus returns the object in the default language (because the uid is still the one of the record in the main language).
All this seems a little hackish. So again my question: what is the correct way to get a translated domain model in a CommandController?
thanks,
mika
p.s.: I've set up the second language in the backend and creating a translated record works just fine. My question is just how to I get an existing translated record in a CommandController.
alternative solution:
based on the solution above, I decided that I can do almost everything
on my own. So what I do now is
i) create an independend querybuilder for the according table:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName);`
ii) select the record with the desired l10n_parent and sys_language_uid
$query = $queryBuilder->select('*')
->from($tableName)
->where($queryBuilder->expr()->eq('sys_language_uid', $langId))
->andWhere($queryBuilder->expr()->eq('l10n_parent', $parentUid))
->execute();
iii) fetch all records into an array
$rows = $query->fetchAll();
iv) invoke the DataMapper manually to get the object
$dataMapper = $this->objectManager->get(DataMapper::class);
$translated = $dataMapper->map($className, $rows);
I know it has nothing to do with the ModelRepository any more, but it
works quite fine for now...
that's all folks
My solution to the issue described above:
To avoid invoking the DataMapper as part of the query from
Typo3DbBackend, I used a raw query (argument for ->execute()) and get
back an array, that already went through language overlay etc.
BUT: in
the array the '_LOCALIZED_UID' is still available. So I overwrite the
uid with the value from '_LOCALIZED_UID' and invoke the DataMapper
manually. Quite cumbersome and very hackish to overcome the Typo3
backend shortcomings...

TYPO3 tt_content structure: t3_origuid vs l18n_parent

I had to import a few posts from one TYPO3 site into another, which lead into some exploring of the DB structure. A specific question arose:
In localised content elements (tt_content entries with sys_language_uid = 1), the fields t3_origuid and l18n_parent are redundantly filled. l18n_parent is required for the backend localisation view to work.
Do they always have the same value? Or is there a use case where the values of the fields can differ?
l10n_parent / l18n_parent
The field configured in TCA as transOrigPointerField (usually l10n_parent or l18n_parent) is used for localization.
It always contains an id of the record in the default language (even if the record was translated from a record in non-default language), see https://docs.typo3.org/typo3cms/TCAReference/singlehtml/#transorigpointerfield
t3_origuid
The field configured in TCA as origUid (usually t3_origuid) is filled when record is copied or translated, and contains an id of the source record, see https://docs.typo3.org/typo3cms/TCAReference/singlehtml/#origuid
The fields will have the same value in some cases (e.g. translating a record from default language), but in other will have different value. E.g. when copying a record on the same page t3_origuid will be different than the l10n_parent.
To allow localization it is required to have transOrigPointerField (l10n_parent). Having origUid (t3_origuid) field is not hard requirement but a good practice as as some additional features may require it to work. Especially in newer versions of TYPO3. For example the Translation Wizard is currently using t3_origuid field.
l10n_source (since TYPO3 8.6)
Since TYPO3 8.6 a new database field l10n_source for tt_content table has been introduced together with a new TCA ctrl configuration translationSource. The translationSource field contains an uid of the record used as a translation source, no matter whether the record was translated in the free or connected mode.
see more in the documentation on l10n_source field
Those fields can have different values.
t3_origuid is a generic field pointing to a record from which the current one was derived in some way. For example by copying or localizing it. Here is some documentation for it.
The field l18n_parent is reserved for localization purposes.
Just as a addition to Jost's post:
To determine which field you should use check the value of:
$TCA['tx_yourtable']['ctrl']['transOrigPointerField']
ie. for tt_content it's:
$TCA['tt_content']['ctrl']['transOrigPointerField'] = 'l18n_parent';

How to delete tt_content elements which do not have any connection to a page

I want to find all tt_content elements from a TYPO3 installation, which do not have a connection to a page. How would you do that?
Every tt_content record has connection to the page via pid field, the only reason for opposite situation is manipulating with tt_content records manually (i.e. creating them by some external script or even adding to DB with phpMyAdmin).
For permanent deleting contents from pages that was deleted via TYPO3's backend (and also any other types of records), you can just use Recycler extension - it's system one, so just go to Extension Manager and enable it. Then select the highest page in your tree, choose depth Infinite, and you'll find all soft-deleted records by type, deleting them in the Recycler will remove them totally from DB.
If for some reason you have such situation that pages record with given uid doesn't exists at all, although tt_content uses its pid, the Recycler won't find it. These can be only found by SQL query:
SELECT tt_content.uid, tt_content.pid, pages.uid page_uid
FROM tt_content LEFT JOIN pages ON (tt_content.pid=pages.uid)
WHERE pages.uid IS NULL
Disclaimer: Manual DB manipulation should be avoided as long as possible. TYPO3's backend is able to maintain deleted records very well and first try built-in tools. I do not response for any damages caused by manual DB changes, for your own safety make a DB backup before deleting/modifying ANY records.