Bilingual pages

The CSS does not work in Explorer 5.0 Windows. The script itself does, but it's nearly worthless without the CSS.

One of my clients needed a bilingual site (Dutch/English). I was fed up with the silly flags that most sites use for selecting a language and decided on a different approach. Pretty soon I found that this script is very useful for my own site, too.

See the script in action in my CV or Publications pages, or Petr Stanícek's modification at WellStyled.com, where he uses the script for a completely bilingual English/Czech site.

HTML and CSS

The bilingual page contains two content div's, which are initially shown side by side:

<div id="nederlands">
Nederlandse tekst
</div>

<div id="english">
English text
</div>


div#nederlands,div#english {
	float: left;
	width: 40%;
	font-size: 80%;
}

I wanted a vertical separator and rather haphazardly defined it on the Dutch (left) div:

div#nederlands {
	border-right: 1px solid #999999;
	padding-right: 5px;
	margin-right: 5px;
}

div#english {
	padding-left: 5px;
}

Then two classes large and small for the language the user selects and the other language, respectively. The large one uses the full font size, the small one uses an unreadably small font size and makes all content grey.

I also give the small class overflow: hidden for reasons explained later.

div.large {
	font-size: 100%;
}

div.small {
	font-size: 60%;
	overflow: hidden;
	color: #999999;
}

div.small * {
	color: #999999;
}

Finally I decided to hide all images in the passive (small) language:

div.small img {
	display: none;
}

Note that I do not define width and height in the CSS. I tried to set a percentual value for the widths of the divs but every experiment ended in cross browser disaster. I finally decided to set the widths in JavaScript.
As to the heights, they cannot be set by CSS, as we'll see below.

(I skipped some CSS, see bilingual.css for all styles I use on this site.)

The script

This is the bilingual script. Make sure you have the correct div's and CSS, run init() onload and it works.

var selectedLanguage;
var totalWidth;
var nlTitle = 'Klik om Nederlandse versie te vergroten';
var enTitle = 'Click to enlarge English version';

function init()
{
	var eng = document.getElementById('english');
	eng.onclick = sizeText;
	eng.title = enTitle;
	var nl = document.getElementById('nederlands');
	nl.onclick = sizeText;
	nl.title = nlTitle;
	setWidths();
}

function exit()
{
	if (selectedLanguage)
		createCookie('bilingual',selectedLanguage,180);
}

window.onresize = setWidths;

function setWidths()
{
	totalWidth = parseInt(document.body.offsetWidth*.8);
	var lang = (selectedLanguage) ? selectedLanguage : readCookie('bilingual');
	if (lang)
		document.getElementById(lang).onclick();
}

function sizeText()
{
	this.className = 'large';
	this.style.width = parseInt(totalWidth*.80)+'px';
	this.title = '';
	this.style.height = 'auto';
	var other = (this.id == 'nederlands') ? 'english' : 'nederlands';
	document.getElementById(other).className = 'small';
	document.getElementById(other).style.width = parseInt(totalWidth*.20)+'px';
	document.getElementById(other).title = (other == 'nederlands') ? nlTitle : enTitle;
	document.getElementById(other).style.height = this.offsetHeight;
	selectedLanguage = this.id;
}

You also need my cookie scripts.

Explanation

We start by defining some general stuff. A selectedLanguage variable to hold the language the user has selected and a totalWidth variable to hold the combined width the two language divs should use. A Dutch and an English title to explain the interface.

var selectedLanguage;
var totalWidth;
var nlTitle = 'Klik om Nederlandse versie te vergroten';
var enTitle = 'Click to enlarge English version';

init() is called onload. We take the English text div, make sure that when the user clicks on it the function sizeText() is executed and give it the English title. Then we do the same for the Dutch div.

Finally we call the vital function setWidths() that calculates the width the two language divs should user.

function init()
{
	var eng = document.getElementById('english');
	eng.onclick = sizeText;
	eng.title = enTitle;
	var nl = document.getElementById('nederlands');
	nl.onclick = sizeText;
	nl.title = nlTitle;
	setWidths();
}

exit() is called onunload. We set a cookie that remembers the user's preferred language for future use.

function exit()
{
	if (selectedLanguage)
		createCookie('bilingual',selectedLanguage,180);
}

onresize we call setWidths() again: if the width of the browser window has changed we must adjust our parameters.

window.onresize = setWidths;

setWidths()

setWidths() sets the combined width of the language divs. By trial and error I arrived at a value of 80% of the body width (document.body.offsetWidth). Test this well, especially in Opera which is by far the most annoying browser.

Note that this function does not set the widths of the two language divs. That still remains to be done.

Then determine the selected language and store it in lang. If the user has already selected a language, read out selectedLanguage. If not, try to read out the cookie.

function setWidths()
{
	totalWidth = parseInt(document.body.offsetWidth*.8);
	var lang = (selectedLanguage) ? selectedLanguage : readCookie('bilingual');

If we found a selected language, execute the onclick event handler of the corresponding div to switch it to active (large) language.

	if (lang)
		document.getElementById(lang).onclick();
}

sizeText()

The function sizeText() is called whenever the user clicks on one of the two language divs. As we saw, the setWidths() function calls it, too.

function sizeText()
{

Because we use traditional event registration the this keyword refers to the div the user has clicked on. That's why setWidths() executes the onclick event handler of a div instead of directly calling the function: the function needs to have a context.

We set the class of the clicked div to 'large' to make it active. Then we set its width to 80% of totalWidth. Feel free to change this ratio, but I found that an 80%/20% division works best in my design.

We erase the div's title and set its height to 'auto' (which means: as much as you need) for reasons that I explain later.

	this.className = 'large';
	this.style.width = parseInt(totalWidth*.80)+'px';
	this.title = '';
	this.style.height = 'auto';

Then we find the id of the other div.

	var other = (this.id == 'nederlands') ? 'english' : 'nederlands';

We make this div passive. We set its class to 'small', its width to 20% of the total width and its title to the correct string.

	document.getElementById(other).className = 'small';
	document.getElementById(other).style.width = parseInt(totalWidth*.20)+'px';
	document.getElementById(other).title = (other == 'nederlands') ? nlTitle : enTitle;

Since I have written far more English than Dutch articles, the English column of my Publications page is much longer than the Dutch one. When Dutch is the selected language, even the passive, tiny, greyed out English column sticks out well beyond the active Dutch column.

Therefore I give the passive block the height of the active block. Because of the overflow: hidden in my CSS the passive block is now cut off where necessary.

	document.getElementById(other).style.height = this.offsetHeight;

If the user switches languages, the old passive block becomes active and all height restrictions should be removed. That's the reason of the this.style.height = 'auto' we saw before.

Finally store the selected language in selectedLanguage.

	selectedLanguage = this.id;
}