MultiLingual Websites in ExpressionEngine

Jan 06
MultiLingual Websites in ExpressionEngine

The following article will show you one of the many ways that you can create a multilingual or country-specific site in ExpressionEngine to deliver content specific to each language/country.

Firstly, we’re going to start with a blank ExpressionEngine Install:

Default File List

Now what we need to do is to define our content languages/countries. We do this by creating root folders for each country that we want. For the purpose of this article, I’m going to create an English (en), French (fr) and Spanish (es).

Default File List with Language Folders

I personally choose to use the two letter abbreviations of countries so that I keep my URL’s small enough ( rather than using the full name ( – but this is entirely up to you.

we now need to copy the ‘/index.php’ and the ‘/path.php’ files into each of the folders that we just created.

Folder List with Languages and Files

We don’t need to change anything in the index.php files since they are exactly the same, but we do need to make some changes on the path.php files.

Default File :

1. <?php
3. // ------------------------------------------------------
6. // ------------------------------------------------------
7. // Path to the directory containing your backend files
9. $system_path = "./system/";
11. // ------------------------------------------------------
13. // See user guide for more information
14. // ------------------------------------------------------
16. $template_group = "";
17. $template = "";
18. $site_url = "";
19. $site_index = "";
20. $site_404 = "";
21. $global_vars = array(); // This array must be associative
23. ?>

Change 1) Change line 9 to represent the new path to the system folder

9. $system_path = "../system/";

Change 2) Change line 18 to include the URL of the site (including the folder segment)

18. $site_url = "";

Change 3) Create an Array value in line 21 to include a country reference that you will use within your Custom Fields.

21. $global_vars = array(
); // This array must be associative

Do that for each of the languages/folders that you have created passing the correct folder/language into the appropriate section.

Now you have a working folder structure with languages – try visiting and you should see your EE Homepage!

Part 2 : Referencing Custom Fields

Now we need to create a custom field to associate our content/language. There are multiple ways you can do this, but I simply do it by creating a custom field called {cat}_lang – for example, my news section would be ‘news_lang’ or my about section would be ‘about_lang’. You can do this how you want though.

Next you set the custom field to be a drop down and you pre-populate the fields with the same values as you entered in the associative array (change 3). As the following example shows.

Creating a Custom Field

Now once you’ve done this, you will be able to publish an entry and choose it’s appropriate language – keeping all your news in the ‘news channel’ and so on so forth.

All you now need to do is to amend your standard exp:weblog:entries code to allow for the filter of the language/location. This is where the ‘countrycode’ global array comes into it’s own – simply add the following to your exp:weblog:entries code:


Et Voila!

One last thing you should probably also consider is that if someone went to ‘’ – it wouldn’t show them anything because you have no language settings etc. I simply use a .htaccess file in the root and add the following line to redirect the user to a’default’ location (i.e the English site):

Redirect 301 /index.php

UPDATE: EE2.1 Users

Ok – it’s in fact, even easier in EE2.1 (don’t you just love progress!)… Easiest way, I’ve found to do it is as follows:

1) Create your Language folders are per the set up above, then copy the /index.php file into the folder you’ve created.

2) Open the index.php file you’ve just copied and make the following tweaks:

$system_path = '../system';

Note the double dot (..) instead of the one.

Then uncomment line #73 :

$assign_to_config['global_vars'] = array();

This is where you set your variables as per the instructions above, for example:

$assign_to_config['global_vars'] = array(
); // This array must be associative

and finally, uncomment the following:

$assign_to_config['site_url'] = '';

and make sure you put the URL in with the language slug as well, e.g:

$assign_to_config['site_url'] = '';

And thats it!

  • Share/Bookmark

4 Tweets

21 Responses to “MultiLingual Websites in ExpressionEngine”

  1. cwcrawley says:

    First Blog Post of 2010 : Multilingual Sites in #ExpressionEngine #EE RT as you wish! kthxbye!

    This comment was originally posted on Twitter

  2. cwcrawley says:

    A quick RT from last nights post – New Blog Entry : Multilingual Sites in ExpressionEngine #EE #ExpressionEngine

    This comment was originally posted on Twitter

  3. Erwin Heiser says:

    Carl, this approach has been on the EE wiki for a few years now:

  4. Carl Crawley says:

    Hi Erwin,

    Yes – thats how I used to do it, but I adapted the way they suggest so that I don’t need to have lots of en_body, fr_body, de_body, en_summary fr_summary, de_summary etc etc field. This gets rather unmanageable when we build sites with 8 languages and there is title/summary/body/image_captions/ that all need 8 versions.

    Quite a few people have asked me to clarify how I deal with mulit-language in EE since thats mainly what I do – most sites are at least 2 languages, sometimes up to 8 as I mentioned.



  5. Angel Scotfield says:

    BTW if you prefer to use en_body, fr_body, de_body etc. – try to use it with MX Custom Tabs extension. It’s help to create a really nice UI .

  6. Carl Crawley says:

    Interesting. I’m not a fan of the en_body, fr_body solution to be honest because as I said, it’s a bit unmanageable when you have anything up to 8 languages to deal with…

    But I’ll certainly take a look at that extension and see what it does.


  7. vanni says:

    Thanks for this tip! You have provided much more details that what was on the EE wiki. I never did get that example to work.
    PS it helps to have real examples rather than talk about things in generalities.

  8. MarmaladeToday says:

    It’s good to see this laid out for people Carl.

    Another approach that will make things more manageable and avoid the en_body etc you don’t like is to replicate the weblog and tag it’s shortname instead using that same convention, e.g. weblog_shortname_en, weblog_shortname_fr etc. You’ll need another global path variable, e.g. shortname_suffix, where that in the ‘en’ folder is ‘_en’, but then you can do {exp:weblog:entries weblog=’weblog_shortname{shortname_suffix}’ }. This also has the benefit that you can assign editor member groups per language weblog and also access language pages in search.

    Of course all this is EE 1.6.x. Not sure how things are affected in EE 2 yet.

  9. Carl Crawley says:

    Thanks Vanni – good to see my posts are useful at least.

    Yes Aidann, I’ve seen that solution used/suggested somewhere before and thought it was quite a good way of doing it. and like you say, has the added benefit of creating language specific members.

    As usual with anything like this – once you’ve learned how to do something in a certain way, it just seems to stick and its easier sometimes to not change something which isn’t broken if you get my meaning.

    I’m sure EE2.0 will give us lots of new challenges :)


  10. MarmaladeToday says:

    Thanks Carl. That technique was also developed out of ideas in the wiki article.

    I agree, use what works for you. Also use what makes most sense given your situation. Horses for courses.

    Oh yes, EE2 is certainly going to :-)


  11. cwcrawley says:

    @dionnu @jghull take a look at – principle should be the same. just redirect local sites to

    This comment was originally posted on Twitter

  12. jghull says:

    Great post from @cwcrawley here –

    This comment was originally posted on Twitter

  13. Marco says:

    Well-written tutorial,

    it worked for me, and helped a lot!

    I have only some trouble with the “redirection part” : if I use a simple .htaccess file with your code inside “Redirect 301 /” the browser freezes and safari warns me: “Too many redirects occurred trying to open “”. This might occur if you open a page that is redirected to open another page which then is redirected to open the original page.”

    What am I doing wrong?



  14. Marco says:


    Redirect 301 /index.php



  15. Amweg says:

    Well-written tutorial,

    it worked for me, and helped a lot!

    I have only some trouboe with the "redirection part" : if I use a simple .htaccess file with your code inside "Redirect 301 /; 6he browser freezes and safari warns me: "Too many redirects occurred trying to open “”. This might occur if you open a page that is redirected to open another page which then is redirected to open the original page."

    What am I doing wrong?



  16. Carl Crawley says:

    I believe marco pointed out that one – you need to specify the /index.php as per his comment further up (I have updated the article as well).

    Thanks for the encouraging comments though!



  17. kaalofkammen says:

    This is an example for EE1. How does this work on EE2?
    Thanx and many regards,


  18. ClientSide Username Checking, EE and jQuery « Digital Meanderings says:

    [...] If you have a multi-lingual set up like my previous post on Multilingual sites in EE, then you will need to change {segment_3} for [...]

  19. Carl Crawley says:

    Now updated to include instructions for EE2.1 setup as well…


  20. moonbeetle says:

    ah the duplicate fields vs a language field question.

    It would seem logical to have a language field for each channel that deals with language dependent entries. It reminds me of the time I wrote SQL statements like: SELECT * FROM news WHERE lang = ‘en’. Sure from a programmer perspective this seems so obvious.

    But with duplicated fields (body_en, body_fr, body_es) you will have the benefit that all data for an entry (the main language and the translated versions) are grouped together in one interface while if you use a language field and not duplicate fields you will end up with many entries of the same “thing”. Site owners often find it easier to have all translations in one place, thus duplicated fields.

    A question you can ask to decide wether or not to use a language field or duplicate fields is: Does this channel data always need to be translated? If the answer is yes I would go with duplicating fields. If the answer is no you can run with having a language field.

    A third option is using statuses. I try to avoid this but sometimes it’s the only way.

  21. Carl Crawley says:

    Yes – you’ve hit the nail there.

    Most of the time (I’d say 99%) of the time, I am building websites which have two lots of independent content – in different languages and news items don’t always apply to a French audience as they would to an English Audience for example, so utilising the duplicated field methodology doesn’t work…

    but yes, in case where you’re selling tickets for example to an event – you don’t want to have two ‘event’ entries for each language, so duplicating the body fields is the best solution.

    I wouldn’t advise using statuses at all because this then causes complications that are just un-necessary when there are so many other solutions about!

    Thanks for the post!


Leave a Reply

Additional comments powered by BackType

Links of Interest!

Here are some links of sites/people that I find interesting...


This page and most pages on this site should comply with w3c validation where appropriate. If you find it doesn't, please notify me