Detecting Locale
In order to localize your website the very first thing that needs to be done is to detect what locale your user prefers. A locale is an identifier that specifies how written language should be handled. The naming convention for locales is language[-region]
. So while en
represents the English language generally, en-US
would represent English for the United States and en-GB
would represent English for Great Britain.
Locale can be detected on either the client or the server. While the server is the preferred method, which we'll see why in a moment, we'll look at how it can be done in either environment.
For the sake of example, let's assume that our browser's language settings look like this:
Server side
On the server a user's locale can be detected using the Accept-Language
HTTP header. Using Express you would do the following:
var express = require('express'),
app = express();
app.get('/', function (req, res) {
console.log(req.header('Accept-Language'));
// en-US,en;q=0.8,fr;q=0.6,de;q=0.4
});
app.listen(3000);
The Accept-Language
header provides a list of preferred locales. This is beneficial because if for some reason you don't support the en-US
locale, you can fall back to one of the other locales until you hopefully find a match.
You probably noticed the q=0.8
portion. This is the quality value of the locale. It is optional and defaults to q=1
. If specified it indicates the user's preference for that locale. The higher the value the greater the preference.
Client side
On the client you will have to deal with the idiosyncrasies between different vendors.
// IE
if (navigator.browserLanguage) {
console.log(navigator.browserLanguage);
// en-US
}
// All other vendors
else if (navigator.language) {
console.log(navigator.language);
// en-US
}
Not the worst hoop to jump through, but not as clean as the server. What about the actual value that is returned by these properties?
First off none of the vendors provide a list as the server does. All vendors will return a single string, in this case en-US
.
The biggest challenge though with detecting locale on the client is that the value returned is inconsistent. Firefox is the only vendor that honors the locale that was specified in the browser's language settings. So if the browser settings specify German as the user's greatest language preference but the language of the user's OS is English, Firefox will return de
as you might expect. All other vendors will return en
, the language of the OS.
For these reasons it is best to detect locale on the server.
Hybrid approach
For the times when you need to accurately reference the user's locale on the client you can provide the server value to the client. Let's assume the language for the user's OS is en-US
, but the user's browser settings specify de
.
// app.js
var acceptLang = require('accept-language'),
express = require('express'),
app = express();
app.set('view engine', 'ejs');
app.use(function (req, res, next) {
var parsed = acceptLang.parse(req.header('Accept-Language')),
locale = parsed[0];
res.locals.locale = locale.language + (locale.region ? '-' + locale.region : '');
next();
});
app.get('/', function (req, res) { res.render('index.ejs'); });
app.listen(3000);
<!-- views/index.ejs -->
<!doctype html>
<html lang="<%= locale %>">
<head>
<meta charset="utf-8" />
<title>Example App</title>
</head>
<body>
Hello World!
</body>
</html>
Now the client has the correct locale supplied by the server. Anytime you need to reference the user's locale on the client you can simply query the lang
attribute on the html
tag:
console.log(document.documentElement.getAttribute('lang'));
// de
In this case the server would have set the lang
attribute to de
correctly and the client has the user's proper locale preference.
Up next
In the next article we'll delve into how to do i18n with Angular.
Open source hacker. Community organizer. Co-organizer @ReactRally. Software Sommelier.