Guiding users to translated pages

Question

If my site contains alternative language versions of the same page, what can I do to help the user see the page in their preferred language?

This article is relevant for pages for which there are complete translations of the content. If your alternative pages have different content, or are regional variants rather than translations, you may need to do things differently.

Quick answer

Use server-based, language-related content negotiation to point the user to the page that matches their browser preferences, but also add links to each page so that the user can change languages easily if they prefer.

Consider how to indicate to the user where the in-page language links are, and if the page is available in a long list of languages, consider whether or not to use something like a select control (and if so, how to make it obvious what its function is).

If the user switches to a different language, offer them the opportunity to remember that choice and serve up subsequent pages in that language, overriding their browser settings.

Details

Content negotiation

It's usually helpful to automatically guide a reader to a page in a language they prefer, if such a page is available . This is called content negotiation, and is done by the server when they request a page. There are different types of content negotiation. Here we are concerned with language-related content negotiation, which we will refer to as language negotiation.

This makes it easier for the user to quickly get to content in the language they prefer. There may be times when this delivers a page in a language the reader doesn't actually want, but there are ways to help with that, as we will see below.

For the page you are reading now, the server looks at the Accept-Language header, sent via HTTP when your browser requests a page.

The Accept-language header takes information from either the browser settings or system settings. When you install a browser, default preferences are always set, but in most desktop browsers you can also change or add to the language preferences. You can also specify a prioritised list of languages, so that if a version of the page is not available in your preferred language the server can look for alternative language versions of the page, in the order you prefer.

Be wary about using IP addresses or other location services to guess the language of the user. This approach can be useful for accessing region-specific services or finding local information, etc., but it can cause problems for language negotiation since the location of the user doesn't necessarily tally with the language in which they prefer to read a web page.

Whichever language negotiation method is used, you should ensure that there is a default option, which comes into play when content is not available in the language or languages you requested. In the case of the page you are reading, the fallback is to the English version, because that is the original version, is most up-to-date, and has undergone wide review. English is also chosen because the W3C views it as the most widely accessible lingua franca.

Of course, a user may prefer to fall back to another language that they know well, rather than to English. This is a matter of personal preference. If they prefer to look for another language before being redirected to the English default version, they may be able to do so by setting their browser or system's language preferences to a prioritised list, as mentioned above. If language negotiation fails for their preferred language, the server will try the others in their list, in order.

You may also find the article When to use language negotiation useful. It discusses in more detail when it is appropriate, or not, to use language negotiation.

Overriding the language negotiation

Suppose your native language is Spanish and so your browser is set to receive content in Spanish most of the time, but suppose also that you understand English and you prefer to read technical articles such as W3C pages in the English original.

If you are redirected to a Spanish page every time you follow a link and have to manually move to the English page, this can quickly become tiresome. The reader needs some way to indicate that they want to stay in their preferred language while reading pages on the site, and a mechanism that ensures that they do.

The page you are reading enables this by allowing you to set a cookie any time you click on the language links at the top right of the page. Every time you link to one of these pages, the server checks whether a cookie is set and active, and if so, serves the page in the language in which you last viewed it.

You can do the same thing with localStorage, if you prefer that to cookies.

Additional information

Which type of content negotiation to use on Apache?

The page you are currently reading is served by an Apache server. When doing content negotiation, there are two common ways of managing page redirects with Apache: MultiViews or type maps. We use type maps because we feel it gives us greater flexibility in redirecting requests, but your mileage may vary. The downside is that it requires some additional files on the server, and those files need to be edited when a translation is added or removed. See the next section for a quick overview of type maps. We also have an article that describes how to use MultiViews, if you prefer that approach.

Using type maps for language negotiation

What follows are some brief notes about using the type map approach for language negotiation on a Apache server. See the Apache documentation for full details of how to use this approach.

To use type maps you create a file in the same directory as the main file, with the same name, but with the extension .var. This file is invoked by the server when a file of that name is requested without a language extension (ie. .en, or .de). The file tells the server where to look for a specific language version.

You also need to configure your server to use type maps content negotiation by adding an AddHandler directive. For example, we use:

AddHandler type-map .var

Here is an example of a file called my-article.var:

URI: my-article

Content-language: de
Content-type: text/html
URI: my-article.de.php

Content-language: en
Content-type: text/html
URI: my-article.en.php

Content-language: es
Content-type: text/html
URI: another-file-with-long-name-so-you-notice.php

Content-language: uk
Content-type: text/html
URI: my-article.uk.php

Content-type: text/html
URI: my-article.en.php

When a request comes in for the resource http://www.example.org/my-article, the server looks at the Accept-Language header sent by the browser. If it is de (German), the server will look down the list and send back the file my-article.de.php.

Now note how, if the incoming Accept-Language header is set to es (Spanish), the server routes the request to a file with a completely different name. It could also be in a different directory.

Finally note that the last two lines define the default case: if no Accept-Language is matched to the items above, the server will return the nominated file (in this case the English version at my-article.en.php).