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

May 9th, 2011 • typo3, multilanguage, multidomain, language menu, typoscript, realurl • comments
TYPO3 multi language & multi domain site with RealURL and language menu

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.

Overview

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

Conditions

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(
  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;
    break;
  case 'typo3-ml-it.local':
  case 'typo3-ml2-it.local':
    $_GET['L'] = 2;
    break;
  default:
    $_GET['L'] = 0;
    break;
}

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
[global]

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
[global]

[globalString = IENV:HTTP_HOST = typo3-ml-it.local]
config.sys_language_uid = 2
config.language = it
[global]

Additionally the following CONFIG TLO settings are very important for a proper internal language switch (as an example for the english language):


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>
}
[global]

[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>
}
[global]

[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>
}
[global]

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.

Downloads

typo3-ml_local.t3d

Sample page tree export with TypoScript setup

14.3 K

realurl_conf.tar.gz

RealURL configuration, place in typo3conf/ directory

1.4 K

Links & sources

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

Comments

  1. Gravatar: ian
    ian August 31st, 2010 02:44pm
    Very well written article.
    Loved the downloads (t3d & realurl_conf.php), not everybody think about this.
    Thank you for sharing this! =)
  2. Gravatar: Thomas Juhnke
    Thomas Juhnke August 31st, 2010 03:14pm
    Hi Ian,

    thank you for your comment. Glad, you like the article. Yes that's what I thought while writing: never seen downloads of page trees of archived configuration (just code fields in the text which are sometimes difficult to handle and copy & paste) ;)

    Best wishes,

    tommy
  3. Gravatar: Thomas
    Thomas October 14th, 2010 01:07pm
    Awesome article, really awesome.

    With the combined t3d download and your information, I got finally the right understanding for multi language implementations. No other documentation did do that :-)

    Thanks a lot.
  4. Gravatar: Maarten
    Maarten January 21st, 2011 04:38pm
    Nice article, thanks!
  5. Gravatar: Dmitry Dulepov
    Dmitry Dulepov May 8th, 2011 06:38pm
    I usually do not comment on blogs but here it is necessary.

    You should use this approach. It is plain wrong. There are too many conditions, that would cause a lot of entries in cache_hash, slower web site and problems if you want to add a new domain. Instead, you should use _DOMAINS feature of RealURL. It handles languages depending on domain name. Just read the manual (or see links at the bottom of my post).

    In addition, you should not use _DEFAULT when you have multiple domains because that causes other hard to solve issues.

    More reading:
    http://dmitry-dulepov.com/article/realurl-made-easy-part-2.html
    http://dmitry-dulepov.com/article/realurl-separate-language-domains-in-an-easy-way.html
  6. Gravatar: Thomas Juhnke
    Thomas Juhnke May 9th, 2011 09:05am
    Hi Dmitry,

    thank you for your kind and valuable comment. I'll post a follow up to this article as soon as possible and mark this one as deprecated.

    Greetings,

    tommy
  7. Gravatar: Kevin
    Kevin September 9th, 2011 10:32am
    hi there,
    very nice step by step! what would also be interesting maybe is how to set up automatic language detection.

    Greetings
  8. Gravatar: Thomas Juhnke
    Thomas Juhnke September 15th, 2011 11:29am
    Hey Kevin,

    thanks for your comment. I wouldn't recommend a server side language detection because of the search engine spiders which will be misled with this functionality.

    E.g. let's assume the Google bot starts a crawl of your website for the german search index, but the bot resides on a computer in England - guess what? The automatic language detection (e.g. it's IP/geolocation based) will switch to english.

    I think a better approach will be a client side redirect. With JavaScript. That won't hurt any search engine spiders. If you're interested, I'll write an article how to achieve this goal.

    Best regards,

    tommy
Leave a comment

Make sure you enter the * required information where indicated. Comments are moderated – and rel="nofollow" is in use. Please no link dropping, no keywords or domains as names; do not spam, and do not advertise!