Introduction

About this document

Creating a website in multiple languages with TYPO3 can be done in a variety of ways - as usual. Unfortunately, all the options are hard to understand unless described in a context where used. In addition, even if you understand the context you might like to get some suggestions for what others have found to be best-practices.

This document tries to document everything you need to know about localizing websites with TYPO3. Ideally, it should mention every feature and what it is good for. However, be sure to use reference documents like the TypoScript Reference, TYPO3 Explained, etc. to look up the exact syntax for the features mentioned.

Since the main goal of this document is to include all knowledge areas of localization, it might suffer from a poor composition where advanced content is mixed here and there. A later revision could maybe make up for this by better prioritizing the content. For now that has not been a priority. On the other hand, you will come out as an expert in the other end.

Localization and Translation

The two terms are often used to express the same. Also in this document. But more precisely this is how we understand the difference:

Translation
means that a specific composition of words are translated to another language. In other words: If there is a header and an image in the default language, so there will be in the translation. No more, no less.
Localization
means more broadly that a page is represented in another language. This means information in that language ("translation") but could also include alternative templates, additional composition of content directed to another audience, etc. In other words: There might be another number of headers and images than in the default language.

TYPO3 handles both types of approaches, but this flexibility has the price that you need to read this document to find out which approach to choose for your project!

Credits

This document was originally written by Kasper Skårhøj.

Dedication

I want to dedicate this document to every native English speaker who has over the years learned to live with my gazillions of documents full of weird syntactical compositions and understood that it was a privilege that a non-native like me after all chose to communicate in a foreign language to the best of my abilities.

-- Kasper

Character sets

All TYPO3 websites use UTF-8 as their character set. Using UTF-8 means that you have a consistent data storage and can store any glyph from any language without worrying further about character sets.

Database field lengths

The TYPO3 Core is compatible with UTF-8.

However, you might face the problem that the database field lengths of some extensions need to be extended. For example, each Chinese glyph takes three bytes. So if a field is a varchar(10) and an author enters 10 Chinese glyphs, only the first 3 glyphs will be stored (as they require 9 bytes). UTF-8 is tricky in this respect, as all ASCII characters only need 1 byte, while European special characters usually need 2 and Asian character sets 3 bytes - but some special characters can even need 5-6 bytes!

Setting up languages

Languages are defined in the site configuration for each root page. When creating a new page on root level via TYPO3 backend, a very basic site configuration is generated on the fly. It prevents immediate errors due to missing configuration and can also serve as a starting point for all further actions.

Site Management

The Introduction Package's default languages are English, Danish and German languages. Adding a new language is done in the Site Management > Sites module of the backend.

Editing a site configuration

Editing form for a site configuration

Once you have defined at least one additional language, you will be able to translate pages and content. For example, the Web > List module will show links for translation if you have translated the page.

Content elements with localize links

The Web > List view, with page translations and localize links for content elements

Translated elements appears nested "under" their default language parent element in the Web > List view.

Nested translations

The Web > List view shows translations nested under their parent

Translating pages

In order to translate a page in the page tree, create a copy of it with a new language relation. Use the module Page and on the top of the right side Language Comparison and the lower dropdown Create a new translation of this page. As soon as you choose a language of this dropdown the translation process will start with the copy of this page.

The page copy will be generated in the default language with page property fields like title or description prefixed by a text similar to "[Translate to Language X:]". The translated content needs to be put here, replacing the placeholder text (including the prefix and the copied default language contents).

Select the Language Comparison function in the upper menu bar of the Web > Page module. Then use the select box to create a new translation of the page:

Create a new translation of the page

Create a new translation of the page

The form with the fields for the translated page will then be displayed:

Editing the page fields for an alternative page language

Editing the page fields for an alternative page language

Most of the fields are the same as for the page of the default language. Below the fields the content of the default language is shown.

As long as a page has no translated page record, its contents cannot be translated, no matter how many system languages have been defined.

Localization overview

Introduction

You can get a complete overview of the page tree and page translations by using the Web > Info module and choosing the Localization Overview function.

Localization Overview in Web > Info module

Overview of existing translations in a blank Introduction Package

All the green entries indicate which languages the respective page is available in. The gray areas mean that no translation is available, but access to the page specifying this language is tolerated. It is also fast to create new translations from this module: use the checkboxes and hit the Create new translation headers button.

Translating several pages at once

Creating translations for a whole selection of pages at once

Hiding pages if no translation exist

If there is no translation of a page, it will still appear by default in the website menu when this language is used. Here the home page of the Introduction Package is in German (as can be seen from the translate page title in <title> tag), and all other (untranslated) pages still appear in the menu, albeit in the default language (English).

The menu with untranslated pages

The German home page with untranslated pages appearing in the menu

This behaviour can be changed. Let us edit the "Customizing" page. In the Language tab, check the Hide page if no translation for current language exists:

Hiding the page if not translated

Hide the page if it is not translated

This is reflected in the Localization Overview:

Hidden translation in the overview

The Localization Overview reflects the change in visibility of the "Customizing" page

From this it is clear that when viewing the website in German the "Customizing" page is not accessible. The menu reflects this in the frontend.

Hidden translation in the menu

The "Customizing" page does not appear in the menu anymore

Trying to access the "Customizing" page directly with its ID will produce an error.

Missing translation generates error

An error message indicates a missing translation

Hiding default translation of pages

If you want pages only in the alternative languages you must still create a default language page in the page tree which simply acts as a placeholder. Setting this status is done by selecting the checkbox Hide default translation of page:

Hiding the page in default language

Hide the page in the default language

This is reflected in the Localization Overview:

Hidden default language in the overview

The Localization Overview shows the page as being unavailable in the default language

On the web site, the menu now looks like this in German:

German menu with language-specific page

The German-specific page shows up in the German version of the web site

and in English:

English menu not showing language-specific page

The German-specific page does not show up in the default version of the web site

Trying to access the "Nur für Deutschland" page in the default language will yield an error.

Hiding pages in the default language is probably a rare thing to do, but it is possible to imagine cases where the topic of a page or section is only relevant in one of the alternative languages. Especially, if a language of the site is not only a translation but may serve subsidiaries of a company in a local context.

Inverse control of hidden translations

It is also possible to hide all untranslated pages by default with a global setting:

EXT:site_package/ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['FE']['hidePagesIfNotTranslatedByDefault'] = 1;
Copied!

The Localization Overview reflects this immediately:

All untranslated pages hidden by default

The Localization Overview shows that all untranslated pages are hidden by default

Note how the behaviour of specific localization setting for the "Customizing" pages has been inverted. We had earlier defined the page to be hidden if no translation existed. Now it is forced to display even if no translation exists (this is reflected by its background being gray instead of red).

When editing the page properties, the label of the corresponding checkbox has changed:

Show page even if no translation exists

Inverted behaviour: choose to show the page even if no translation exists

Final notes

Summary of colors in the Localization Overview:

Green background
Page is translated and viewable in this language. For translations it means that an active Alternative Page Language overlay record is present.
Red background
Page cannot be viewed in this language and you will see an error message if you try. Menus should automatically filter out links to pages with this translation.
Gray background (not available for default language)
Page will fall back to the specified fallback mode for content. It depends on the language fallback configuration set in your site configuration.

Localized content

Introduction

There are two strategies for handling the translation of content on pages:

Connected mode
Translating content will create a direct connection between the original language and the language you translate to. This means that moving an element or setting meta information like start time or end time will be taken from the original content and you will not be able to set these values on a translated content element. Translated records will hold the connection to their original language record in the l10n_parent field. This mode is best for a strict translation workflow.
Free mode
Copying content will take the content elements from the source language and create copies in a different language. This means that you will be able to move content elements around freely, but you will not have the benefit of being able to compare changes made in the source language later on. Use this mode, when the translated pages content can largely differ from the original language and you want to have that freedom in designing your translated website.

When you start translating a page, the Web > Page module will ask you about this choice when clicking on the Translate button:

The translation wizard showing localization strategies

The translation wizard asks for a choice of localization strategy

The Translate button corresponds to the connected mode, the Copy button to the free mode. Furthers details on how to proceed with translations are found in the Editor's Tutorial.

Connected content

If you opted for the connected content strategy, this is the default way that translated pages will be handled in the frontend and requires no further configuration in the site configuration. In the site configuration, the fallbackType "strict" would look like this:

config/sites/my_site/config.yaml
languages:
  -
    # ... other language settings ...
    fallbackType: strict
Copied!

In this connected mode, TYPO3 will first internally fetch the records of the default language, then overlay them with the target language. If a record is not translated into the target language, then it is discarded and not shown at all.

The German version will be reduced to the actually translated elements:

English and German translation without overlays

English and German translation without overlays

Another way to view connected content in the frontend is by creating a fallback chain. For this we have to adjust the site configuration and provide a comma-separated list of fallback languages:

config/sites/my_site/config.yaml
languages:
  -
    # ... other language settings ...
    fallbackType: fallback
    # 1 = Danish, 0 = English
    fallbacks: '1,0'
Copied!

In this mode, TYPO3 will also first internally fetch the records of the default language and then overlay them with the target language. If no translation exists for the target language, it will now go from left to right through the fallback chain and first try to find a translation for the language ID 1 (Danish) and, if that is a miss as well, fall back to language ID 0 (English).

English and German translation with overlays

The English version and its German translation, with overlays

Free content

With the free mode content strategy, the site configuration will have to be adjusted accordingly:

config/sites/my_site/config.yaml
languages:
  -
    # ... other language settings ...
    fallbackType: free
Copied!

This means, TYPO3 will directly fetch the translated records and not care about the records in the default language at all.

The "All" language

When using overlays, it becomes possible to use a particular language called "All", which will be automatically visible across all translations. The uid of that particular language is "-1".

Content element in "All" language

A content element valid for all languages

It is also marked with the special language icon in the Web > Page module:

"All" language in the Page Module

An "All" language content element displayed in the Web > Page module

Note that no Translate button appears, the new content element is valid for all languages.

Localizing inline elements (IRRE)

When localizing a record with inline elements (IRRE), the inline elements will be grayed out and not automatically localized. TYPO3 will display two buttons to help you localize and keep the elements in sync.

Buttons for localizing and synchronizing inline elements

Buttons for localizing and synchronizing inline elements

Localize all records (1) creates localized versions of all inline elements from the default language in your current language so that you can translate them. Each element that has not yet been localized will also have a Localize button (3) that will allow you to localize individual elements.

Synchronize with original language (2) can be clicked when you have translated the elements and the content in the default language changed.

Localized labels

Using XLF labels in TypoScript structures

XLIFF (XLF) files are XML files containing labels that the system can retrieve in a localized version if the appropriate translations are installed. It is possible to retrieve values from XLF files using TypoScript:

EXT:site_package/Configuration/TypoScript/setup.typoscript
page.20 = TEXT
page.20.data = LLL:EXT:indexed_search/Resources/Private/Language/locallang.xlf:form.submit
Copied!

This looks for the label form.submit in the file Resources/Private/Language/locallang.xlf from the extension "indexed_search".

If the frontend is now accessed in the language "de", the German language is configured in the site configuration and the German translations are installed in getLabelsPath()/de/, the output should be "Suche" (instead "Search" which is what you will get if the label is retrieved for the default language).

Using XLF labels in frontend plugins

Making your code ready for internationalization is covered in Localization and basics about XLIFF files are found in XLIFF Format ("TYPO3 Explained"). Properly made plugins should use XLIFF files for every label that they used, so that they can be translated and thus generate a proper output when a specific language is requested.

For instance, if the page is accessed in the language "de" and the German translations are installed, you will see this page for "indexed search":

Output from the Indexed Search plugin in German

Output from the Indexed Search plugin in German

Overriding an existing translation is possible using custom XLIFF files. This process is described in Custom translations.

It is also possible to override a label using TypoScript. See the TypoScript Reference for more information.

You can also create XLIFF files for a language into which TYPO3 is not translated yet. This process is described in Custom languages.

Language selection menu

To properly navigate a multilingual web site you probably want to have a language selection menu somewhere. This menu should show an entry for each possible, with variation of aspect depending on translation availability:

  • Translation exists
  • Translation exists - currently selected
  • Missing translation
  • Missing translation - currently selected

This kind of menu can be made by either using a LanguageMenuProcessor on a FLUIDTEMPLATE or using the HMENU content object with special type language.

LanguageMenuProcessor example

The Introduction Package comes with a text-based rendering of the language menu, located in the footer:

Text-based language menu

A text-based language menu, with active and disabled states

This is the corresponding TypoScript code, that sets up a LanguageMenuProcessor on the page level:

EXT:site_package/Configuration/TypoScript/setup.typoscript
page = PAGE
page {
  10.dataProcessing {
    40 = TYPO3\CMS\Frontend\DataProcessing\LanguageMenuProcessor
    40 {
      languages = <insert language-id> or "auto"
      as = languagenavigation
    }
  }
}
Copied!

The value for languages is a list of comma-separated language IDs (for example: 0,1,2) or auto to load from site configuration.

The menu is then passed as a hierarchical array to Fluid in the variable {languagenavigation}. The Fluid template iterates through the array and creates the language links:

EXT:site_package/Resources/Private/Templates/SomeTemplate.html
<f:if condition="{languagenavigation}">
    <ul id="language_menu" class="language-menu">
        <f:for each="{languagenavigation}" as="item">
            <li class="{f:if(condition: item.active, then: 'active')} {f:if(condition: item.available, else: 'text-muted')}">
                <f:if condition="{item.available}">
                    <f:then>
                        <a href="{item.link}" hreflang="{item.hreflang}" title="{item.title}">
                            <span>{item.navigationTitle}</span>
                        </a>
                    </f:then>
                    <f:else>
                        <span>{item.navigationTitle}</span>
                    </f:else>
                </f:if>
            </li>
        </f:for>
    </ul>
</f:if>
Copied!

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 first 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::getLanguageOverlay() 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.

Permissions

You can restrict backend users and groups to edit records in specific languages only. The permissions are enforced across all tables by checking the language specified in the TCA configuration of the "languageField" of the table. Navigate to the backend module System > Backend Users. Select a backend user group. The Limit to languages setting is found in the Access Lists tab:

Setting language limitations

Restricting backend users or groups to one or more languages

Settings from groups and sub-groups are merged.

If a user has no Limit to languages checkboxes set at all they can edit all languages.

Regardless of Limit to languages all users can edit records with [All] language.

Setting Limit to languages is not only good to protect translators from editing other languages or default content - it is also helpful to limit the confusion a user might have when faced with modules displaying translations for a variety of other languages!

Sitemap