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:

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).
I personally choose to use the two letter abbreviations of countries so that I keep my URL’s small enough (www.sitedomain.com/en/) rather than using the full name (www.sitedomain.com/english/) – 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.
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
2.
3. // ------------------------------------------------------
4. // DO NOT ALTER THIS FILE UNLESS YOU HAVE A REASON TO
5.
6. // ------------------------------------------------------
7. // Path to the directory containing your backend files
8.
9. $system_path = "./system/";
10.
11. // ------------------------------------------------------
12. // MANUALLY CONFIGURABLE VARIABLES
13. // See user guide for more information
14. // ------------------------------------------------------
15.
16. $template_group = "";
17. $template = "";
18. $site_url = "";
19. $site_index = "";
20. $site_404 = "";
21. $global_vars = array(); // This array must be associative
22.
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 = "http://www.sitedomain.com/en/";
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(
"countrycode"=>"English"
); // 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 http://www.sitedomain.com/en/ 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.
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:
search:news_lang="{countrycode}"
Et Voila!
One last thing you should probably also consider is that if someone went to ‘www.sitedomain.com’ – 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 http://www.sitedomain.com/en/
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(
"countrycode"=>"English"
); // 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'] = 'http://www.site.com/fr';
And thats it!



January 6th, 2010 at 8:49 pm
First Blog Post of 2010 : Multilingual Sites in #ExpressionEngine #EE http://is.gd/5P8Dj RT as you wish! kthxbye!
This comment was originally posted on Twitter
January 7th, 2010 at 11:18 am
A quick RT from last nights post – New Blog Entry : Multilingual Sites in ExpressionEngine http://tinyurl.com/y8ljqpm #EE #ExpressionEngine
This comment was originally posted on Twitter
January 7th, 2010 at 12:27 pm
Carl, this approach has been on the EE wiki for a few years now:
http://expressionengine.com/wiki/Multi_language_site_alternative/
January 7th, 2010 at 3:17 pm
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.
Cheers,
C.
January 7th, 2010 at 6:30 pm
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 .
January 7th, 2010 at 6:55 pm
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.
C.
January 7th, 2010 at 7:28 pm
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.
January 7th, 2010 at 7:47 pm
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.
January 7th, 2010 at 10:12 pm
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
C.
January 8th, 2010 at 12:52 pm
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
A.
January 15th, 2010 at 1:40 pm
@dionnu @jghull take a look at http://tinyurl.com/y8ljqpm – principle should be the same. just redirect local sites to masterdomain.com/lang
This comment was originally posted on Twitter
January 15th, 2010 at 2:17 pm
Great post from @cwcrawley here – http://bit.ly/7UDr5U
This comment was originally posted on Twitter
February 4th, 2010 at 11:54 am
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 / http://www.mysite.it/it/” the browser freezes and safari warns me: “Too many redirects occurred trying to open “http://www.mysite.it/it/it/it/it/it/it/it/it/it/it/it/it/it/it/it/it/”. 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?
Thanks
Marco
February 4th, 2010 at 4:17 pm
solution:
Redirect 301 /index.php http://www.mysite.it/it/
Bye
Marco
March 12th, 2010 at 8:26 am
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 / http://www.mysite.it/it/" 6he browser freezes and safari warns me: "Too many redirects occurred trying to open “http://www.mysite.it/it/it/it/it/it/it/it/it/it/it/it/it/it/it/it/it/”. 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?
Thanks
Marco;
March 18th, 2010 at 4:41 pm
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!
Regards,
Carl
March 30th, 2010 at 1:42 pm
This is an example for EE1. How does this work on EE2?
Thanx and many regards,
@kaalofkammen
July 27th, 2010 at 10:56 am
[...] 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 [...]
August 11th, 2010 at 2:39 pm
Now updated to include instructions for EE2.1 setup as well…
Enjoy!
August 11th, 2010 at 3:00 pm
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.
August 11th, 2010 at 3:09 pm
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!
C.