Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Ruby

How To Detect User's Locale Right

4.96/5 (8 votes)
17 Nov 2016MIT2 min read 17.7K  
Use HTTP Accept-Language Header Instead Of Geo-IP

Introduction

Nowadays, I see many sites, which are supporting internationalization, including huge ones like NBA.com, google.com, skype.com which are using geo-ip for detecting user locale.

This method is both inaccurate and expensive in terms of computational resources and keeping geo-ip database up-to-date or paying for such services. Even if we ignore VPN users, there would still be a huge number of people, who are enjoying their vacations or going to business trips to other countries. They open favorite web sites and see that content is provided in an unknown language.

Image 1

I am Russian and my system default language is English, but popular sites don't care.

Background

Probably, this issue is not that critical as usually it is quite simple to find and switch locales. The thing is that we as web developers can do much better.

Each http request made by the browser has a special header "Accept-Language", where user locale preferences are provided with weighted values. These preferences are based on user system locale and browser settings. It is highly unlikely that user will set some language, he or she doesn't understand, right? Instead of guessing user's language by geo location, web server can use this header and serve content properly, which is also much cheaper than the geo-ip method. If web server is not able to match any locale, then it would be perfectly fine to fall back to English or some other default locale. If, for some reason, "Accept-Language" header is missing, then it might be also worth falling back to geo-ip locale method, but keep in mind that it is a very rare case.

Using the Code

Below are examples for popular web technology stacks, which perform HTTP "Accept-Language" header parsing and locale detection.

PHP

For PHP applications, I would recommend using pear/HTTP2 extension.

PHP
<?php
  require_once "HTTP2.php";

  $supported = array("de" => true, "en-US" => true);

  $http = new HTTP2();
  echo $http->negotiateLanguage($supported);
?>

This example negotiates with the user agent if any of the languages, which are specified in supported, are supported on the user's system. If the negotiation has a positive result, the language code of the most preferred language is printed. Otherwise, the default language code (en-US) is printed.

Ruby-on-Rails

For Rails applications, you can use Rack middleware called http_accept_language.
Just add it to your gem list and provide basic configuration in your application.rb, i.e., which locales you are supporting.

Ruby
class ApplicationController < ActionController::Base
  before_filter :set_locale

  private

  def set_locale
    I18n.locale = http_accept_language.compatible_language_from(I18n.available_locales)
  end  

end

Node.js

For Node.js applications, I use accept-language package.

JavaScript
import acceptLanguage from 'accept-language';

acceptLanguage.languages(['en-US', 'zh-CN']);

function detectLocale(request) {
  return acceptLanguage.get(request.headers['accept-language']) || 'en';  
}

Hope this helps to make the Internet slightly better for all of us!

License

This article, along with any associated source code and files, is licensed under The MIT License