Dispersion Design

< Back

Listing the Names of the Month in Any Language

2012-04-10

In summary:

In Perl, the name of each month of the year in a particular language can be obtained using the POSIX setlocale() and strftime() functions:

use POSIX qw(strftime locale_h);
use Encode;

setlocale(LC_ALL, "ru_RU.utf8");

my @months;
for (0..11)
{
	my $utf8_name = strftime("%B", 0, 0, 0, 1, $_, 100);
	$months[$_] = decode_utf8($utf8_name);
}

Introduction

There have been many times that I have written code like this:

my $months = ['January', 'February', 'March', 'April',
	'May', 'June', 'July', 'August', 'September',
	'October', 'November', 'December'];

print "<select>\n";
for (0..11)
{
	print qq|<option value="$_">$months[$_]</option>\n|;
}
print "</select>\n";

The problem is that this code is language specific (specifically English) and would have to be re-written if the month names were needed in a different language. Internationalization and localization (i18n and L10n) are becoming a necessity when developing applications, and hard coding month names is no longer an acceptable solution.

Thankfully, Perl is able to access language specific locale information, so hard-coding month names is not necessary.

Enter POSIX

A standard module called POSIX is available in Perl for accessing the standard POSIX programming functions, including those for setting the current locale and formatting dates and times. We will need the strftime() and setlocale() functions. We also need the decode_utf8() function from the Encode module to be able to convert UTF-8 strings to Perl Unicode strings.

use POSIX qw(strftime locale_h);
use Encode;

Now the current locale can be set:

setlocale(LC_ALL, "ru_RU.utf8");

The locale name is of the format "{language code}_{country code}.utf8". The 'utf8' suffix is important to make sure that you receive values encoded with the UTF-8 character set and not some other character set. On a Linux command line, typing 'locale -a | grep utf8' will list all the UTF-8 locales available on your system. In this example we are choosing the Russian language as spoken in Russia ("ru_RU.utf8").

strftime

Now that the locale has been set, the strftime() function will return values formatted according to that locale. The strftime() function requires the following parameters: strftime(format, sec, min, hour, mday, month, year). We just want the name of the month, so we pass the formatting string "%B" into strftime(). The date can be any date in the current month. We just choose sec=min=hour=0, mday=1 and year=100.

my @months;
for my $i (0..11)
{
	my $utf8_name = strftime("%B", 0, 0, 0, 1, $i, 100);
	$months[$i] = decode_utf8($utf8_name);
}

Note that strftime() returns a UTF-8 encoded string. We use the decode_utf8() function to convert it to a Perl Unicode string.

Displaying the Results

The month names can now be printed. Remember to use binmode() to tell Perl that STDOUT is a UTF-8 output stream, or you'll get "Wide character in print..." warnings.

binmode(STDOUT, ":utf8");
for my $i (0..11)
{
	print "$i: $months[$i]\n";
}

The Complete Solution

A complete Perl script for displaying the name of each month in a particular language could then be written:

#!/usr/bin/perl

use strict;
use warnings;
use POSIX qw(strftime locale_h);
use Encode;

setlocale(LC_ALL, "ru_RU.utf8");

my @months;
for (0..11)
{
	my $utf8_name = strftime("%B", 0, 0, 0, 1, $_, 100);
	$months[$_] = decode_utf8($utf8_name);
}

binmode(STDOUT, ":utf8");
for my $i (0..11)
{
	print "$i: $months[$i]\n";
}

The results of this script should look like the following:

0: Январь
1: Февраль
2: Март
3: Апрель
4: Май
5: Июнь
6: Июль
7: Август
8: Сентябрь
9: Октябрь
10: Ноябрь
11: Декабрь