Multilingual websites and webapplications using PHP and Smarty, part 3: locales

Joor Loohuis, August 30, 2009, 8749 views.

Multilingual sites not only need support for different languages, but also for locales, the set of conventions that are used in countries and language regions for representing quantities such as time, numbers, and other information. This article describes how websites and webapplications that use Smarty can set a locale in addition to a language for the best user experience.

Tags: , , , , , , ,

In the previous two articles of this series we showed how to detect the language a visitor prefers, and how to set up dictionaries to display interfaces in the correct language. However, Native Language Support (NLS) doesn't end there. In order to display information in a way the visitor prefers, the locale needs to be taken into account. The locale is the set of definitions and conventions for notation of amounts, dates, and other structured data. For most users, seeing information displayed according to what is customary to them is a very important part of making an interface usable.

Picking the right locales

Locales are identified by a name that typically contains a language, a country or region, and possibly an encoding. For example, a locale for the U.S.A. might be en_US.utf8. The lowercase en is the language (English), the uppercase US is the country (U.S.A.), and utf8 the encoding (UTF-8). Note that the name of a locale might differ from the way the language and country are reported in the Accept-Language header in an HTTP request. There also are locale names with differently formatted names, and locale names will vary between operating systems.

To decide which locales you intend to support, you first need to find out which locales are installed on the webserver. If the server doesn't support a locale, you can't use it and the webserver will resort to the default system locale if you try to select it. If you don't have shell access to the webserver, get in touch with the systems administrator to get a list of available locales. If you have shell access, and the webserver is a Linux or Unix system, you can use the locale command to retrieve the list of available locales. On a Linux server you might try

  1. locale -a
C en_AU.utf8 en_BW.utf8 ...

For a Windows webserver, consult the lists of defined languages and countries and regions on MSDN. Normally speaking, the website will be using the UTF-8 encoding, since that provides us with all special characters that we might need for any language, but make sure the encoding of the HTML and the locale match up.

Setting the locale

Now we need to select the locales we need. Previously we already determined and used the language. We could do something similar for the country or region, but for the sake of simplicity, we'll just pick a suitable locale for each language we support. In the dictionary, we can add a symbol for the locale string for each language:

  1. dictionary.cfg
  2. default locale is English
locale = en_US.utf8 ...
  1. Dutch translation
nl locale = nl_NL.utf8 ...

Actually setting the locale in PHP is done using the setlocale function, but in the line of this series of articles, we want to set it in our templates. For this we need a little Smarty plugin to interface with the PHP function, which you can get from the Smarty Wiki. Put the plugin in a location where PHP can find it (i.e. in the include path). Now you can select the locale directly after determining the language:

{assign var=$lang value=$smarty.server.SCRIPT_NAME|regex_replace:'#(^/|\W.*)#':''}
{config_load file='dictionary.cfg' section=$lang}
{setlocale locale=#locale#}

Now if you display information such as a number or a date, it will be displayed in accordance with the conventions of the locale. For example, if we set the locales as such:

{setlocale locale='en_US.utf8'}
{$smarty.now|date_format:'%c'}
{123456.789|string_format:'%.2f'}
{'%i'|money_format:123456.789}
 
{setlocale locale='fr_FR.utf8'}
{$smarty.now|date_format:'%c'}
{123456.789|string_format:'%.2f'}
{'%i'|money_format:123456.789}

the resulting output is:

Sun 30 Aug 2009 11:07:14 AM CEST
123456.79
USD 123,456.79
 
dim 30 aoû 2009 11:07:14 CEST
123456,79
123 456,79 EUR

As you can see, there are significant differences in the way dates, numbers, and currency is displayed depending on the locale. For more control, you can also set the locale for a specific category of information only. There are a number of categories, but most relevant to us are those for time and dates (LC_TIME), monetary values (LC_MONETARY), numeric values other than monetary (LC_NUMERIC), character classification and case conversion (LC_CTYPE), and collation (LC_COLLATE). You can control these in the setlocale plugin by specifying the type parameter with a value 'time', 'monetary', 'numeric', 'ctype' or 'collate', respectively:

{setlocale locale='fr_FR.utf8' type='time'}

If the type parameter is not specified, the locale is set for all categories. Most of the time, this is what you actually want. Note that for the locales to work, you need to use PHP functions that support locales. For example, the sprintf function that is used by the string_format modifier supports locales, but the PHP function number_format doesn't.

Strictly speaking, the locale should be selected from the Accept-Language header of the HTTP request. After all, conventions may vary between regions and countries in the same language group. For example, in U.S. English it is customary to write dates in MDY (month-day-year) format, while in much of the rest of the world, including British English, people use DMY format (I'm ignoring the aluminum-aluminium, color-colour, tomayto-tomahto matter here). Supporting locales in this level of detail is quite involved, since the list of acceptable locales from the Accept-Language header needs to be matched against the list of available locales on the webserver. Of course, there are ways of dealing with this, but it is beyond the scope of this article.

Social networking: Tweet this article on Twitter Pass on this article on LinkedIn Bookmark this article on Google Bookmark this article on Yahoo! Bookmark this article on Technorati Bookmark this article on Delicious Share this article on Facebook Digg this article on Digg Submit this article to Reddit Thumb this article up at StumbleUpon Submit this article to Furl

Talkback

respond to this article