Saturday, March 27, 2010

PHP, foreign language dates and problems with setlocale()

I recently came across an issue when trying to display the date in a foreign language using PHP.

I was working on a web development project for a Spanish client which required that the date, or more specifically the day and month names, be displayed in Spanish.

A quick Google search or look through the php manual will point you to the setlocale() function.

Great, you change the locale to match your desired language and the date will be outputted accordingly, here's an example:

setlocale(LC_TIME,"es_ES.utf8");
echo strftime("%A, %B %d", time());

And the output should be:

sábado, marzo 27

For the majority of people this will work perfectly and is the ideal solution, but for some however, including myself, it would always display the date in English regardless.

Something to make sure of is that the locale is available on your system, to see what locales are available/installed on your system, use the following command:

-bash-3.2$ locale -a

If it's still not working and the locale you need is available/installed then the logical route at this point would be to determine if it's a problem with the PHP setup, or the system itself:

-bash-3.2$ export LC_TIME="es_ES.utf8"
-bash-3.2$ date
sáb mar 27 14:16:39 GMT 2010

The system's date function works without a problem but for some reason the PHP solution doesn't.

This seems to be a bug in certain versions of PHP and/or on certain flavours of Linux, at least that's what I can gather from searching on Google and finding many people in the same situation. If anybody finds out exactly what the cause is, please post a comment at the bottom of the page as I'm sure there are many people who would greatly appreciate it, including myself.

If your uploading your script to a web hosting server then you probably won't have the control you need to fix any configuration problem so we're in need of a workaround solution.

A quick fix that a lot of people have posted solutions for is to simply create an array of the days or months in that language:

$day_names=array("Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado");
echo $day_names[date("w")];

Which works fine but I wanted a solution I could reuse easily in the future without having to create an array of the days and months for every language I needed.

So I wrote a quick function to interface with the linux date command.

It sets the locale variable using putenv(), cleans up the date format so there's no need for backslashes, calls date with the arguments specified and returns the output.

This solution is not compatible with Windows however functions similarly to strftime(), here's the code:

function _date($format,$locale="en_GB.utf8",$date='') {
putenv("LC_TIME=$locale");
$cmd="date +" . preg_replace("/([^a-z0-9_%])/i","\\\\$1",$format);
if ($date!="") { $cmd.=" --date='$date'"; }
$p=popen($cmd,"r");
$date=fread($p,1024);
pclose($p);
return $date;
}

Here's an example of it working for English, Spanish, French, German and Russian:

echo _date("%A, %B %d","en_GB.utf8","2010-01-01 12:00");
echo _date("%A, %B %d","es_ES.utf8","2010-01-01 12:00");
echo _date("%A, %B %d","fr_FR.utf8","2010-01-01 12:00");
echo _date("%A, %B %d","de_DE.utf8","2010-01-01 12:00");
echo _date("%A, %B %d","ru_RU.utf8","2010-01-01 12:00");

And here's the output:

Friday, January 01
viernes, enero 01
vendredi, janvier 01
Freitag, Januar 01
Пятница, Январь 01

For information on the parameters to enter for the date format/first argument see the strftime() manual page here: http://php.net/manual/en/function.strftime.php or type "man date" in your terminal.

Please feel free to use the function however you wish and any links back to this page and comments are greatly appreciated.