TYPO3 multi language & multi domain site with RealURL and language menu


typo3 multilanguage multidomain language menu typoscript realurl

This is a step-by-step tutorial which describes in detail how to setup TYPO3 for a multilingual website with "domain language switching". It will describe how to properly configure RealURL, extend it's configuration with simple PHP statements in the RealURL configuration file and last but not least, probably the most important thing: the language switch menu with cross domain support.

I started with a clean TYPO3 installation and created two page trees in order to test the setup in a multiple page tree setup. Therefore I defined that on my local Linux box typo3-ml.local listens to the default language, while typo3-ml-en.local and typo3-ml-it.local - you guessed it - listens to the english and italian language versions of the page tree.

Note: This article is deprecated. Please see the latest comments. I'll post a follow-up as soon as possible!

Deprecated article

This article is deprecated. Please see Dmitry Dulepovs comment.

I'll post a follow up to this article as soon as possible.


The setup for a multi domain/language environment in a One-Tree-Fits-All page tree splits in 3 steps:

  1. RealURL-Setup
  2. TypoScript-Setup
  3. The language menu


The following conditions must be met to ensure the correct functionality of this setup guide:

  1. within one page tree every page has a unique page title in every language
  2. every page in a page tree has an alternative page language
  3. at root level, all domains must be created as domain records which should be available for the specific page tree
  4. the main domain records (no redirects) must not have set the "Always prepend this domain in links" option
  5. the root level page must have set the "Is root of website" option in the page properties
  6. for this sample setup the default language (sys_language_uid = 0) is german
  • Root level domain records
  • Uncheck this option: Always prepend this domain...
  • This must be checked: Is root of website

RealURL Setup

The RealURL Setup is built upon a default setup which is built on the typo3.org news area RealURL setup then again.

At the end of this default setup you must add three lines which configures some parts of the default setup for every domain. These settings first of all affect the rootpage_id and the preVars setting for the multi domain language switch.

At first, the selected domain inherits the default setup:

$TYPO3_CONF_VARS['EXTCONF']['realurl']['typo3-ml.local'] = $TYPO3_CONF_VARS['EXTCONF']['realurl']['_DEFAULT'];

Then, you have to configure the rootpage_id:

$TYPO3_CONF_VARS['EXTCONF']['realurl']['typo3-ml.local']['pagePath']['rootpage_id'] = 1;

At last you have to override the preVars segment with a dummy array because the language switch isn't part of the URL but of the domain:

$TYPO3_CONF_VARS['EXTCONF']['realurl']['typo3-ml.local']['preVars'] = $byPassLVar;

The dummy preVars array might look like the following, but should be adjusted to fit the requirements of your preVars setting:

$byPassLVar = array(
    'GETvar' => 'L',
    'valueMap' => array(),
    'noMatch' => 'bypass'

With these settings, the well known paths de/, en/ or it/ aren't part of the URL anymore. But how do we tell TYPO3 to switch the language?

The first part of this definition is done in the RealURL configuration. You have to ensure that the L parameter is set. The best solution is to implement a switch() statement like this one:

switch (t3lib_div::getIndpEnv('HTTP_HOST')) {
  case 'typo3-ml-en.local':
  case 'typo3-ml2-en.local':
    $_GET['L'] = 1;
  case 'typo3-ml-it.local':
  case 'typo3-ml2-it.local':
    $_GET['L'] = 2;
    $_GET['L'] = 0;

TypoScript setup

The language switch is done with the very well known TypoScript conditions like the following:

[globalVar = GP:L = 1]
config.sys_language_uid = 1
config.language = en

This must be adjusted in such a way to enable TYPO3 to switch the language if a specific domain request exists instead of the L parameter.

[globalString = IENV:HTTP_HOST = typo3-ml-en.local]
config.sys_language_uid = 1
config.language = en

[globalString = IENV:HTTP_HOST = typo3-ml-it.local]
config.sys_language_uid = 2
config.language = it
config.htmlTag_langKey = en_US
config.locale_all = en_US.utf8
config.baseURL = http://typo3-ml-en.local/

The language menu

The final step: the language switcher. I've spent a lot of time for investigating the best approach for this issue. Finally, I found two solutions:

Upon a closer view I think the solution of Bernhard is the better one because it allows a better maintainability and extendability if it comes to adjust the typolink setup or add more domains.

temp.menu.language.de = COA
temp.menu.language.de {
  stdWrap.wrap = <li>|</li>
  10 = TEXT
  // the german domain
  10.value = typo3-ml.local
  // note to add "www." in production environment
  10.wrap =  <a href="http://|/
  20 = TEXT
  20.typolink {
    parameter.data = TSFE:id
    returnLast = url
    additionalParams = &L=0
    addQueryString = 1
    addQueryString.method = GET
    // must be set, otherwise id parameter appears 2 times
    addQueryString.exclude = id
  30 = TEXT
  30.value = DE
  30.wrap =">|</a>
temp.menu.language.en < temp.menu.language.de
temp.menu.language.en {
  10.value = typo3-ml-en.local
  20.typolink.additionalParams = &L=1
  30.value = EN

temp.menu.language.it < temp.menu.language.de
temp.menu.language.it {
  10.value = typo3-ml-it.local
  20.typolink.additionalParams = &L=2
  30.value = IT
// compile language menu
lib.menu.language = COA
lib.menu.language {
  wrap = <ul id="menu-language">|</ul>
  10 < temp.menu.language.de
  20 < temp.menu.language.en
  30 < temp.menu.language.it
// set active state according to domain/L parameter
// the active item won't be linked
[globalVar = GP:L = 0] OR [globalString = IENV:HTTP_HOST = typo3-ml.local]
lib.menu.language.10 = TEXT
lib.menu.language.10 {
  value = DE
  stdWrap.wrap = <li class="active">|</li>

[globalVar = GP:L = 1] OR [globalString = IENV:HTTP_HOST = typo3-ml-en.local]
lib.menu.language.20 = TEXT
lib.menu.language.20 {
  value = EN
  stdWrap.wrap = <li class="active">|</li>

[globalVar = GP:L = 2] OR [globalString = IENV:HTTP_HOST = typo3-ml-it.local]
lib.menu.language.30 = TEXT
lib.menu.language.30 {
  value = IT
  stdWrap.wrap = <li class="active">|</li>

Important notice: during testing I experienced that the following configuration keys must not be set:

config.typolinkCheckRootline = 1
config.typolinkEnableLinksAcrossDomains = 1

Improvement suggestions

This version of the language menu is built with other TypoScript cObjects than the well known and maybe preferred HMENU cObject, which also supports a language menu creation via the special. configuration key.

For example it's possible to define the language menu item state for non-existing page translations. I think that's the only drawback but you get a much more important feature: domain driven language switching.

Another approach is to use the _DOMAINS feature which was introduced in RealURL v 1.5. But during testing this you'll experience not properly translated URL paths because of a bug in RealURL.

Links & sources

The following ressources gave me a better understanding for writing this article:


comments powered by Disqus