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 (www.example.com/fr/, www.example.com/en/, 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:
- either the cookie set in a previous visit,
- or the browsers preferred language,
- 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 www.example.com/index.cgi. 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).
#!/usr/bin/perl
# Redirect to correct language version of the site.
# Use cookie if present. Otherwise, parse the HTTP_ACCEPT_LANGUAGE header
# Author: Milivoj Ivkovic "mi\x40alma.ch"
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;
}
else { # try HTTP_ACCEPT_LANGUAGE
# 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;
last;
}
}
}
# 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 "http://example.com//en/" 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",
-value=>$wanted_language,
-path=>"/",
-expires=>"+1y",
),
);
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">
<?php
$langs = array(
"fr" => "Français",
"de" => "Deutsch",
"en" => "English",
);
$self=$_SERVER['PHP_SELF'];
$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";
?>
</div>
<!-- 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;
}
a.lang-current,
a.lang-current:visited,
a.lang-current:hover
{
color: #CCCCCC;
text-decoration: underline;
}
a.lang-other,
a.lang-other:visited
{
color: #333333;
text-decoration: none;
}
a.lang-other:hover
{
text-decoration: underline;
background: #F7EBDB;
}
@media print {
#lang-selector {
display: none;
}
}
By the way, the site for which this was first set up is http://lesouffledudesert.com/.
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: code, computers, en, javascript, perl, web-design
13 Comments:
Hi Again,
I was wondering if this could be made to work for a structure with multiple levels of subdirectory.
Figured it out, here's how:
File the line:
$pattern = "{^.*/}i";
Now delete the asterix, so it looks like:
$pattern = "{^./}i";
Voila, now it uses the entire source URL and replaces the the string you've set up.
Cheers,
John
Indeed, my original regex didn't work with additional subdirectories. Thanks "jnesb2121" for pointing it out.
In the PHP lang-selector.php script, I replaced the line
$pattern = "{^.*/}i";
with
$pattern = "{^/[^\/]+/}i";
and the line
$url = preg_replace($pattern, "../$l/", $self);
with
$url = preg_replace($pattern, "/$l/", $self);
The links are now absolute instead of relative.
Do you have a zip file with a default working little sample?
I am not good with PHP and a very small sample to see your structure will help greatly.
Thank you.
Hi
Very nice implementation.
What would you change in php lang selector if all php pages were in one directory and only the name changes. (example).
index.php
indexFR.php
indexDE.php
It would help me very much if you would answer, as I'm not so good in php.
Best Regards
George
BUMP...
Would like to use this, but with indexEN, indexFR and so on were in the root folder.
Thanks!
index.cgi doesn't work on my ISP - cgi scrpts only work in cgi-bin. Is there a redirect page in php or html that will refer to the script.cgi in the bin?
Worked it out!
1) Save the redirector script as redirect.cgi and save in the cgi-bin.
2) Change:
my %available_languages = (
fr => 'fr/',
de => 'de/',
en => 'en/',
default => 'fr/',
TO:
my %available_languages = (
fr => '../fr/',
de => '../de/',
en => '../en/',
default => '../en/',
3)Make a new file in notepad called index.php. The only content of this file should be:
Removed by program - it is a straightforward redirect one line script. Email me for it.
Upload and Voila! It works!
Very many thankks for the original brilliant script.
very nice script indeed!, but where does the setlang function go? on each page? What triggers the setlang javascript function?
Nice work, simply brilliant script.
how do you display flag gifs instead of text. Example:
$langs = array(
"fr" => "fr.gif",
Thanks in advance
I tried"openbracketimg src="images/flag_france.jpg" alt="France Flag" border="0" /closebracket" instead of "French" but it didn't work. It stopped the page executing!
I tried"openbracketimg src="images/flag_france.jpg" alt="France Flag" border="0" /closebracket" instead of "French" but it didn't work. It stopped the page executing!
Cracked it! You have to change the double quotes " for single quotes '
e.g:
"nl" => "openbracketimg src='../images/flag_netherlands_sm.jpg' alt='France Flag' /closebracket",
The word 'bracket' stands for a < or >
Please can anyone tell me how to lay out the $langs = array( as a table? That is with say three rows or three countries across. Tis would be great if using flags. Thanks!
Post a Comment
<< Home