Thursday, March 30, 2006

Simple multi-language web site

There are many ways to direct users of a multi lingual web site to the pages
in their language. But the quick search I made didn't return solutions which
I liked, so I did my own. I'm sure the same method has been used many times,
but it's sometimes faster to re-invent a little wheel than to go through zillions
of Google search results.

I had to work on a small site with about 20 pages, in which every language
is in it's own subdirectory (,, etc.)
and the file names remain the same.

This simple structure made it pretty easy, and the solution makes it trivial
to add another language.

(For a lot of background about languages and a different solution involving the standard "content language negotiation", see Dan's Web Tips: Languages).

The way I choose works like this:

  • One line in every page calls a PHP script to display the language menu.
  • A cookie holds the visitor's preferred language.
  • Javascript updates the cookie when the visitor switches languages.
  • The main index page redirects visitors to the correct language, using:
    1. either the cookie set in a previous visit,
    2. or the browsers preferred language,
    3. or a pre-configured default language.
  • Finally, a few CSS rules take care of the menu's look

The redirector script

I used a little Perl script on It first looks for
a cookie with the user's preferred language. If there is no cookie (usually
because this is a first-time visit), it uses the HTTP_ACCEPT_LANGUAGE
header sent by the browser to find the best guess. If that doesn't work either,
it uses a default language. Finally, it redirects the user to the selected language,
while also setting the cookie for the next visit.

I probably could have done the index page in PHP, but I didn't know PHP at
all when I started so it was easier for me in Perl. (If someone feels like sharing
a PHP translation, you are welcome to post it here as a comment).


# Redirect to correct language version of the site.
# Use cookie if present. Otherwise, parse the HTTP_ACCEPT_LANGUAGE header

# Author: Milivoj Ivkovic "mi\"

use CGI qw(:cgi);
my $VERSION = 0.3;

my %available_languages = (
fr => 'fr/',
de => 'de/',
en => 'en/',
default => 'fr/',
my $wanted_language = 'default';

my $q = new CGI;

# try cookie
my $cookie_lang = $q->cookie("lang");
if ( exists $available_languages{$cookie_lang} ) {
$wanted_language = $cookie_lang;
# Get languages from HTTP_ACCEPT_LANGUAGE, (ignore local variants like en_US, en_GB, etc.).
# Don't bother removing duplicates
my @langs = map { substr($_, 0, 2) } split(/,/, $ENV{HTTP_ACCEPT_LANGUAGE});
foreach my $l (@langs) {
if (exists $available_languages{$l}) {
$wanted_language = $l;

# if nothing worked, we still have the default in $wanted_language which was set at the beginning

# fix URL
my $url = $q->url(-path_info=>1);

# (some servers give us a url ending with 2 "/", so we need "/+" in the
# regex below instead of just "/".
# Otherwise we may end up with "" which doesn't look nice.)
$url =~ s|/+[^/]*$|/$available_languages{$wanted_language}|;

# redirect, and also set the cookie for next time
print $q->redirect ( -uri => $url,
-cookie => cookie( -name=>"lang",

The javascript setLang function

Anytime the user switches the language, this small javascript function is called
to update a cookie, so the preference is saved between sessions.

function setLang(l) {
// Called from links which switch the language.
// Sets cookie with "lang=fr" or other language code, valid for 1 year, on all the site
var expire = new Date();
expire.setTime(expire.getTime() + 3600000*24*365);
document.cookie = 'lang='+l+";expires="+expire.toGMTString()+";path=/";

The language menu

Since all the pages happened to be in PHP, I did the language menu in PHP,
and added an include() to every page.

<?php include("../common/lang-selector.php") ?>

The PHP lang-selector.php script

This is the PHP script which displays the language menu.

<!-- php lang selector -->
<div id="lang-selector">
$langs = array(
"fr" => "Français",
"de" => "Deutsch",
"en" => "English",
$pattern = "{^/[^\/]+/}i";
$links = array();
foreach ($langs as $l => $lang) {
$is_current = ! ( strpos($self, "/$l/") === false );
$url = preg_replace($pattern, "/$l/", $self);
$link = "<a href=\"$url\" class=\"lang-"
. ($is_current ? "current\">" : "other\" onclick=\"setLang('$l')\">")
. " $lang </a> ";
array_push($links, $link);
echo " " . implode(" | ", $links) . "\n";
<!-- end php lang selector -->

The finishing touch: CSS

The look of the language menu is done with CSS. This is what I used for that
particular site:

#lang-selector {
font-size: x-small;
text-align: left;
white-space: nowrap;
padding-bottom: 1em;
color: #CCCCCC;

color: #CCCCCC;
text-decoration: underline;

color: #333333;
text-decoration: none;

text-decoration: underline;
background: #F7EBDB;

@media print {
#lang-selector {
display: none;

By the way, the site for which this was first set up is

When we added English to the site, the steps were:

  • Copy the /fr directory to a new /en directory
  • Add "en" => "English", to lang-selector.php
  • Add en => 'en/', to index.cgi
  • And of course, translate all pages, which was the real work and where
    this little multi-language system doesn't help...

Labels: , , , , ,