A Beginner’s Guide to Python’s locale Module

Software localization

Explore the fundamental concept behind Python's locale module and learn how to use it for string, number, and date formatting.
Software localization blog category featured image | Phrase

Python's locale module is part of the standard library for internationalization (i18n) and localization (l10n) in Python. The locale module allows developers to deal with certain cultural issues in their applications. The good news is that they need not know all the specifics of the location or language where the software is used. Let's explore in great detail the basic concept of Python's locale module.

The basics of Python's locale module

Let's have a look at the nuts and bolts of Python's locale module.

Import and initialization

Based on the official documentation, the locale module is implemented on top of the _locale module. It uses an ANSI C locale implementation if available. A normal application typically starts with the following code to set the locale:

import locale

# use user's default settings

locale.setlocale(locale.LC_ALL, '')

# use current setting

locale.setlocale(locale.LC_ALL, None)

The first parameter represents the locale category while the second parameter modifies the locale setting for the category. It will use the current setting for the category if the second parameter is not present or None. On the other hand, an empty string will use the user's default setting.

In the following example, the locale is set to German (de_DE).

# name might vary with operating system, refer to the notes below

locale.setlocale(locale.LC_ALL, 'de_DE')

Most of the time, it uses the Language Code Identifier (LCID) Reference, as stated in the following link (replace the hyphen in the language tags with an underscore). For example, de-DE should be stated as de_DE. However, older OS versions might accept the locale in a different format, as shown below:

# using standard Language Code Identifier (LCID) Reference

locale.setlocale(locale.LC_ALL , 'de_DE')

# using ISO country code, alpha-3

locale.setlocale(locale.LC_ALL , 'deu_deu')

# using language name

locale.setlocale(locale.LC_ALL , 'German')

List of available locales

The recommended method is to test out the standard Language Code Identifier Reference first. If you encountered an error, the next step is to print out the full list of the available locales in your machine.

# for Windows users

for lang in locale.windows_locale.values():

    print(lang)

# for other operating systems

for lang in locale.locale_alias.values():

    print(lang)

Getting an existing locale

You can get the string for the existing locale with the following code:

# get current locale

loc = locale.getlocale()

Information about an existing locale

Calling the built-in localeconv() function will provide you with detailed information about the current locale. Run the following code to visualize it:

for key, value in locale.localeconv().items():

    print("%s: %s" % (key, value))

Here is an example output for en_SG (English/Singapore):

Python localeconv() function output | Phrase

Check out the official documentation for more information on the keys and their respective meaning.

Formatting and parsing numbers

The locale module provides two specialized functions to parse a string into a number, based on the current locale:

  • atof (convert a string to a floating point number), and
  • atoi (convert a string to integer).

This is extremely useful for parsing a string that contains a comma or dot, as different locales interpret it differently. For example:

locale.atof('32,824')
  • 32,824 is 32824.00 for en_SG (English/Singapore),
  • 32,824 is 32.824 for de_DE (German).

On top of that, you can use the format_string() function to format a number according to your preferences. The following code snippet is based on the de_DE locale:

locale.format_string('%10.2f', 738213.78)

# 738213,78

locale.format_string('%10.2f', 738213.78, grouping=True) # grouping is False by default

# 738.213,78

You can set a grouping parameter for the format_string() function, which accepts a boolean.

  • grouping (if set to True, it takes grouping into account; otherwise, it is False by default).

Formatting currency

Furthermore, you can utilize the built-in currency() function to format any input number. The end result comes with the currency symbol or text. Let us have a look at the following example for en_SG:

locale.currency(924531.78)

# $924531.78

locale.currency(924531.78, grouping=True)

# $924,531.78

locale.currency(924531.78, grouping=True, international=True)

# SGD924,531.78

Apart from the number input, the currency() function also accepts three additional parameters:

  • symbol (if set to True, the returned string includes the currency symbol; the default value is True),
  • grouping (if set to True, the grouping is done with the value; the default value is False),
  • international (if set to True, the international currency symbol is used; the default value is False).

Formatting date and time

Other than that, the datetime module helps in formatting date and time, based on the locale that you have set. The easiest way is to rely on the strftime() function. It converts an object to a string according to a given format. It can be a date, time, or datetime object. Let's have a look at the following list of common format codes

  • %A – weekday as locale’s full name,
  • %b – month as locale’s abbreviated name,
  • %m – month as a zero-padded decimal number,
  • %Y – year with century as a decimal number,
  • %H – hour (24-hour clock) as a zero-padded decimal number,
  • %I – hour (12-hour clock) as a zero-padded decimal number (capital "i"),
  • %p – locale’s equivalent of either AM or PM,
  • %M – minute as a zero-padded decimal number,
  • %S – second as a zero-padded decimal number,
  • %Z – time zone name (empty string if the object is naive).

You can find the full list of format codes in the following link.

Simply define the format in a string, and use it as the input parameter when calling the strftime() function. Check out the following example displaying the current date and time.

now = datetime.datetime.now()

format = "%A, %d %b %Y %H:%M:%S %Z"

now.strftime(format)

# Thursday, 23 July 2020 13:11:31

In addition, you can display just the date component for a specific RSVP event as follows:

registration_date = datetime.date(2020, 8, 15)

format = "RSVP by %A, %d-%b-%Y"

registration_date.strftime(format)

# RSVP by Saturday, 15-Aug-2020

If a value is stored as a timestamp in your database, you can easily convert it to a datetime object:

submission_date = datetime.datetime.fromtimestamp(1595485143)

format = "Submit before %A, %#I%p %d/%m/%Y"

# check notes below

submission_date.strftime(format)

# Submit before Thursday, 2PM 23/07/2020

%I (capital i) represents a 12-hour clock as a zero-padded decimal number. You can remove the leading zero by adding a hyphen or pound sign based on the operating system of your machine. For example,

  • Linux, OS X: %-I
  • Windows, Cygwin: %#I

Besides, you can highlight the timezone as long as the datetime object contains the optimal timezone information. We call this an aware datetime object. In contrast, a naive datetime object does not contain enough information to unambiguously locate itself in relation to other datetime objects. You can easily identify if a datetime object is aware or naive by printing it out.

# naive datetime object, there is no timezone information at the end

2020-08-13 15:14:00.990397

# aware datetime object, using UTC as timezone

2020-08-13 07:14:00.990397+00:00

# aware datetime object, using UTC+6 as timezone

2020-08-13 13:14:00.990397+06:00

For your information, you can easily construct and define a timezone in a datetime object by using datetime.timezone. Let us have a look at the following example of creating instances of current datetime based on different timezones.

format = "Timezone: %H:%M:%S %Z"

# naive datetime object

now = datetime.datetime.now()

now.strftime(format)

# Timezone 15:24:29

# aware datetime object, using UTC as timezone

now = datetime.datetime.now(tz=datetime.timezone.utc)

now.strftime(format)

# Timezone 07:24:29 UTC

# aware datetime object, using UTC as timezone 

now = datetime.datetime.now(tz=datetime.timezone(datetime.timedelta(hours=6)))

now.strftime(format)

# Timezone 13:24:29 UTC+06:0

%Z will return an empty string, called by a naive datetime object.

Best practices

Based on the official documentation, the locale module is a program-wide property according to the C standard definition. As a result, it is extremely expensive to change a locale in between.

Frequent locale changes may cause core dumps. That is why it is highly recommended to set it just once while initializing your application.

An extension module or library routine should never call the setlocale() function, since it will affect the entire program.

Apart from the format_string() function, the only way to perform numeric operations according to the locale is to use the special functions defined by this module:

  • atof (convert a string to a floating point number),
  • atoi (convert a string to integer),
  • str (formats a floating point number using the same format as the built-in function str(float) but takes the decimal point into account).

Concluding our guide to Python's locale module

In conclusion, the locale module is extremely useful for internationalizing and localizing Python applications. If you are looking for ways to streamline your i18n process, let Phrase do the heavy lifting. The most comprehensive localization solution on the market, the Phrase Localization Suite will equip you with everything you need to:

  • Build production-ready integrations with your development workflow,
  • Invite as many users as you wish to collaborate on your projects,
  • Edit and convert localization files online with more context for higher translation quality.

Finally, check out the following articles if you want to know even more about internationalization and localization in Python: