TCA, DataHandler and FormEngine

The support for localization in the Core of TYPO3 is based around a scheme where a record in the default language (id "0") can have other records pointing to it, offering a translation of its content. This implies some requirements for the structure of any database tables which we want to be available for translation:

  1. A field holding the reference to the language defined in the site configuration must be defined.
  2. A field holding the reference to the default language record must be defined.
  3. Optionally, a field holding the reference to the record it was translated from can be defined.

These fields are declared in the "[ctrl]" section of the TCA for the table.

[languageField]

The first field is defined by the "languageField" property and contains a reference to the language the record is in.

Here is how such a field is configured (example from the tt_content table):

EXT:frontend/Configuration/TCA/tt_content.php
'sys_language_uid' => [
    'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language',
    'config' => [
        'type' => 'language'
    ]
]
Copied!

The entry for the [All] language gets automatically added with the TCA type language.

Deprecated since version 11.2

This field can only be used with the TCA type "language". All other field types will be automatically migrated on-the-fly possibly losing configurations. See Migration to the language type.

[transOrigPointerField]

The second field is defined by the "transOrigPointerField" property and contains a reference to the record in the default language.

Here is how such a field is configured (example from the tt_content table):

EXT:frontend/Configuration/TCA/tt_content.php
'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',
        'items' => [
            ['', 0]
        ],
        'foreign_table' => 'tt_content',
        'foreign_table_where' => 'AND {#tt_content}.{#pid}=###CURRENT_PID### AND {#tt_content}.{#sys_language_uid} IN (-1,0)',
        'default' => 0,
    ],
],
Copied!

Obviously, the table name has to be adapted in properties foreign_table and foreign_table_where for each table.

Notice the display condition set on the field, which will check the value of the sys_language_uid and display the l18n_parent field only when it is greater than zero (that means, it is a translation).

Another interesting point is that it will only look for a default language record in the current pid. This means that a default language record and all its translations must be on the same page! This principle is also respected by the API function \TYPO3\CMS\Core\Domain\Repository\PageRepository::getRecordOverlay() which fetches translations of records for the frontend display.

When the transOrigPointerField and languageField are defined for a table you will see references to the content of the default language record when editing the translation:

Original content in the Form Engine

Original content shows up under the input fields when translating a record

[transOrigDiffSourceField]

There is one more field which can be used on a translatable table, defined by the property "languageField". It is optional. It points to a blob database field which will store the content of the default language record as it was when the translation was made. Then this is used to display any changes that have occurred in the default record since translation happened. For example, in the tt_content table, it may look like this for the header field:

Changes in original content

Changes in the original content are highlighted below the input field

This allows a translator to spot easily what has changed in order to adjust the translation accordingly.

[translationSource]

The "translationSource" property defines a name of the field used by translations to point back to the original record (that means, the record in any language of which they are a translation). This property is often set to l10n_source in Core tables.

This property is similar to transOrigPointerField. In connected mode, while transOrigPointerField always contains the uid of the default language record, this field contains the uid of the record the translation was created from.

For example, if a tt_content record in default language (English) with uid 13 exists, this record is translated to French with uid 17, and the Danish translation is later created based on the French translation, then the Danish translation has uid 13 set as l10n_parent and 17 as l10n_source.

DataHandler commands for localization

Localizing a record can be done by the "localize" command of the DataHandler. This is the command that is sent when you press the translate buttons in Web > List or Web > Page for an element.

When this command is issued an ordinary copy is made but the fields languageField and transOrigPointerField are updated accordingly.

You can only localize a record which is not already localized and which is in "default" or "All" language (a record in "All" language is supposed to be somehow valid for any language, but can still be translated if needed).

It is possible to manually create two or more translations of the same record but this is considered an error in the system, although not fatal, yet producing unpredictable results.

Localization command options per field

Each field may contain special instructions which will affect the "localize" command. Most important is the "l10n_mode" field with which, for example, a field can be entirely excluded from the translation process.