locale – POSIX cultural localization API

Purpose:Format and parse values that depend on location or language.
Available In:1.5 and later

The locale module is part of Python’s internationalization and localization support library. It provides a standard way to handle operations that may depend on the language or location of a user. For example, it handles formatting numbers as currency, comparing strings for sorting, and working with dates. It does not cover translation (see the gettext module) or Unicode encoding.

Note

Changing the locale can have application-wide ramifications, so the recommended practice is to avoid changing the value in a library and to let the application set it one time. In the examples below, the locale is changed several times within a short program to highlight the differences in the settings of various locales. It is far more likely that your application will set the locale once at startup and not change it.

Probing the Current Locale

The most common way to let the user change the locale settings for an application is through an environment variable (LC_ALL, LC_CTYPE, LANG, or LANGUAGE, depending on the platform). The application then calls setlocale() without a hard-coded value, and the environment value is used.

import locale
import os
import pprint
import codecs
import sys

sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)

# Default settings based on the user's environment.
locale.setlocale(locale.LC_ALL, '')

print 'Environment settings:'
for env_name in [ 'LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE' ]:
    print '\t%s = %s' % (env_name, os.environ.get(env_name, ''))

# What is the locale?
print
print 'Locale from environment:', locale.getlocale()

template = """
Numeric formatting:

  Decimal point      : "%(decimal_point)s"
  Grouping positions : %(grouping)s
  Thousands separator: "%(thousands_sep)s"

Monetary formatting:

  International currency symbol             : "%(int_curr_symbol)r"
  Local currency symbol                     : %(currency_symbol)r (%(currency_symbol_u)s)
  Symbol precedes positive value            : %(p_cs_precedes)s
  Symbol precedes negative value            : %(n_cs_precedes)s
  Decimal point                             : "%(mon_decimal_point)s"
  Digits in fractional values               : %(frac_digits)s
  Digits in fractional values, international: %(int_frac_digits)s
  Grouping positions                        : %(mon_grouping)s
  Thousands separator                       : "%(mon_thousands_sep)s"
  Positive sign                             : "%(positive_sign)s"
  Positive sign position                    : %(p_sign_posn)s
  Negative sign                             : "%(negative_sign)s"
  Negative sign position                    : %(n_sign_posn)s

"""

sign_positions = {
    0 : 'Surrounded by parentheses',
    1 : 'Before value and symbol',
    2 : 'After value and symbol',
    3 : 'Before value',
    4 : 'After value',
    locale.CHAR_MAX : 'Unspecified',
    }

info = {}
info.update(locale.localeconv())
info['p_sign_posn'] = sign_positions[info['p_sign_posn']]
info['n_sign_posn'] = sign_positions[info['n_sign_posn']]
# convert the currency symbol to unicode
info['currency_symbol_u'] = info['currency_symbol'].decode('utf-8')

print (template % info)

The localeconv() method returns a dictionary containing the locale’s conventions. The full list of value names and definitions is covered in the standard library documentation.

A Mac running OS X 10.6 with all of the variables unset produces this output:

$ export LANG=; export LC_CTYPE=; python locale_env_example.py

Environment settings:
        LC_ALL =
        LC_CTYPE =
        LANG =
        LANGUAGE =

Locale from environment: (None, None)

Numeric formatting:

  Decimal point      : "."
  Grouping positions : []
  Thousands separator: ""

Monetary formatting:

  International currency symbol             : "''"
  Local currency symbol                     : '' ()
  Symbol precedes positive value            : 127
  Symbol precedes negative value            : 127
  Decimal point                             : ""
  Digits in fractional values               : 127
  Digits in fractional values, international: 127
  Grouping positions                        : []
  Thousands separator                       : ""
  Positive sign                             : ""
  Positive sign position                    : Unspecified
  Negative sign                             : ""
  Negative sign position                    : Unspecified

Running the same script with the LANG variable set shows how the locale and default encoding change:

France (fr_FR):

$ LANG=fr_FR LC_CTYPE=fr_FR LC_ALL=fr_FR python locale_env_example.py

Environment settings:
        LC_ALL = fr_FR
        LC_CTYPE = fr_FR
        LANG = fr_FR
        LANGUAGE =

Locale from environment: ('fr_FR', 'ISO8859-1')

Numeric formatting:

  Decimal point      : ","
  Grouping positions : [127]
  Thousands separator: ""

Monetary formatting:

  International currency symbol             : "'EUR '"
  Local currency symbol                     : 'Eu' (Eu)
  Symbol precedes positive value            : 0
  Symbol precedes negative value            : 0
  Decimal point                             : ","
  Digits in fractional values               : 2
  Digits in fractional values, international: 2
  Grouping positions                        : [3, 3, 0]
  Thousands separator                       : " "
  Positive sign                             : ""
  Positive sign position                    : Before value and symbol
  Negative sign                             : "-"
  Negative sign position                    : After value and symbol

Spain (es_ES):

$ LANG=es_ES LC_CTYPE=es_ES LC_ALL=es_ES python locale_env_example.py

Environment settings:
        LC_ALL = es_ES
        LC_CTYPE = es_ES
        LANG = es_ES
        LANGUAGE =

Locale from environment: ('es_ES', 'ISO8859-1')

Numeric formatting:

  Decimal point      : ","
  Grouping positions : [127]
  Thousands separator: ""

Monetary formatting:

  International currency symbol             : "'EUR '"
  Local currency symbol                     : 'Eu' (Eu)
  Symbol precedes positive value            : 1
  Symbol precedes negative value            : 1
  Decimal point                             : ","
  Digits in fractional values               : 2
  Digits in fractional values, international: 2
  Grouping positions                        : [3, 3, 0]
  Thousands separator                       : "."
  Positive sign                             : ""
  Positive sign position                    : Before value and symbol
  Negative sign                             : "-"
  Negative sign position                    : Before value and symbol

Portgual (pt_PT):

$ LANG=pt_PT LC_CTYPE=pt_PT LC_ALL=pt_PT python locale_env_example.py

Environment settings:
        LC_ALL = pt_PT
        LC_CTYPE = pt_PT
        LANG = pt_PT
        LANGUAGE =

Locale from environment: ('pt_PT', 'ISO8859-1')

Numeric formatting:

  Decimal point      : ","
  Grouping positions : []
  Thousands separator: " "

Monetary formatting:

  International currency symbol             : "'EUR '"
  Local currency symbol                     : 'Eu' (Eu)
  Symbol precedes positive value            : 0
  Symbol precedes negative value            : 0
  Decimal point                             : "."
  Digits in fractional values               : 2
  Digits in fractional values, international: 2
  Grouping positions                        : [3, 3, 0]
  Thousands separator                       : "."
  Positive sign                             : ""
  Positive sign position                    : Before value and symbol
  Negative sign                             : "-"
  Negative sign position                    : Before value and symbol

Poland (pl_PL):

$ LANG=pl_PL LC_CTYPE=pl_PL LC_ALL=pl_PL python locale_env_example.py

Environment settings:
        LC_ALL = pl_PL
        LC_CTYPE = pl_PL
        LANG = pl_PL
        LANGUAGE =

Locale from environment: ('pl_PL', 'ISO8859-2')

Numeric formatting:

  Decimal point      : ","
  Grouping positions : [3, 3, 0]
  Thousands separator: " "

Monetary formatting:

  International currency symbol             : "'PLN '"
  Local currency symbol                     : 'z\xc5\x82' (zł)
  Symbol precedes positive value            : 1
  Symbol precedes negative value            : 1
  Decimal point                             : ","
  Digits in fractional values               : 2
  Digits in fractional values, international: 2
  Grouping positions                        : [3, 3, 0]
  Thousands separator                       : " "
  Positive sign                             : ""
  Positive sign position                    : After value
  Negative sign                             : "-"
  Negative sign position                    : After value

Currency

The example output above shows that changing the locale updates the currency symbol setting and the character to separate whole numbers from decimal fractions. This example loops through several different locales to print a positive and negative currency value formatted for each locale:

import locale

sample_locales = [ ('USA',      'en_US'),
                   ('France',   'fr_FR'),
                   ('Spain',    'es_ES'),
                   ('Portugal', 'pt_PT'),
                   ('Poland',   'pl_PL'),
                   ]

for name, loc in sample_locales:
    locale.setlocale(locale.LC_ALL, loc)
    print '%20s: %10s  %10s' % (name, locale.currency(1234.56), locale.currency(-1234.56))

The output is this small table:

$ python locale_currency_example.py

                 USA:   $1234.56   -$1234.56
              France: 1234,56 Eu  1234,56 Eu-
               Spain: Eu 1234,56  -Eu 1234,56
            Portugal: 1234.56 Eu  -1234.56 Eu
              Poland: zł 1234,56  zł 1234,56-

Formatting Numbers

Numbers not related to currency are also formatted differently depending on the locale. In particular, the grouping character used to separate large numbers into readable chunks is changed:

import locale

sample_locales = [ ('USA',      'en_US'),
                   ('France',   'fr_FR'),
                   ('Spain',    'es_ES'),
                   ('Portugal', 'pt_PT'),
                   ('Poland',   'pl_PL'),
                   ]

print '%20s %15s %20s' % ('Locale', 'Integer', 'Float')
for name, loc in sample_locales:
    locale.setlocale(locale.LC_ALL, loc)

    print '%20s' % name,
    print locale.format('%15d', 123456, grouping=True),
    print locale.format('%20.2f', 123456.78, grouping=True)

To format numbers without the currency symbol, use format() instead of currency().

$ python locale_grouping.py

              Locale         Integer                Float
                 USA         123,456           123,456.78
              France          123456            123456,78
               Spain          123456            123456,78
            Portugal          123456            123456,78
              Poland         123 456           123 456,78

Parsing Numbers

Besides generating output in different formats, the locale module helps with parsing input. It includes atoi() and atof() functions for converting the strings to integer and floating point values based on the locale’s numerical formatting conventions.

import locale

sample_data = [ ('USA',      'en_US', '1,234.56'),
                ('France',   'fr_FR', '1234,56'),
                ('Spain',    'es_ES', '1234,56'),
                ('Portugal', 'pt_PT', '1234.56'),
                ('Poland',   'pl_PL', '1 234,56'),
                ]

for name, loc, a in sample_data:
    locale.setlocale(locale.LC_ALL, loc)
    f = locale.atof(a)
    print '%20s: %9s => %f' % (name, a, f)
    

The grouping and decimal separator values

$ python locale_atof_example.py

                 USA:  1,234.56 => 1234.560000
              France:   1234,56 => 1234.560000
               Spain:   1234,56 => 1234.560000
            Portugal:   1234.56 => 1234.560000
              Poland:  1 234,56 => 1234.560000

Dates and Times

Another important aspect of localization is date and time formatting:

import locale
import time

sample_locales = [ ('USA',      'en_US'),
                   ('France',   'fr_FR'),
                   ('Spain',    'es_ES'),
                   ('Portugal', 'pt_PT'),
                   ('Poland',   'pl_PL'),
                   ]

for name, loc in sample_locales:
    locale.setlocale(locale.LC_ALL, loc)
    print '%20s: %s' % (name, time.strftime(locale.nl_langinfo(locale.D_T_FMT)))
$ python locale_date_example.py

                 USA: Thu Feb 21 06:35:54 2013
              France: Jeu 21 fév 06:35:54 2013
               Spain: jue 21 feb 06:35:54 2013
            Portugal: Qui 21 Fev 06:35:54 2013
              Poland: czw 21 lut 06:35:54 2013

This discussion only covers some of the high-level functions in the locale module. There are others which are lower level (format_string()) or which relate to managing the locale for your application (resetlocale()).

See also

locale
The standard library documentation for this module.
gettext
Message catalogs for translations.