Digital Developer Conference: Cloud Security 2021 -- Build the skills to secure your cloud and data Register free

Learn Linux, 101: Localisation and internationalisation


In this tutorial, learn to:

  • Configure your locale settings and environment variables
  • Configure your time zone settings and environment variables

Time, place, and language

There are thousands of different languages used throughout the world. Numbers and dates can be formatted differently, and there are over 40 alphabets, or sets of ideographs, in existence. People use either a 12-hour clock or a 24-hour clock for time. There are also different systems of measurement for everything from building materials to the very paper on which we write or print. This tutorial shows you how to configure your Linux system to adapt it to your locale as these variations are collectively known.

With 24 hours in a day, your time zone is likely to be different than mine, so I also show you how to configure your time zone settings and the associated environment variables.

This tutorial helps you prepare for Objective 107.3 in Topic 107 of the Linux Server Professional (LPIC-1) exam 102. The objective has a weight of 3.


To get the most from the tutorials in this series, you need a basic knowledge of Linux and a working Linux system on which you can practice the commands covered in this tutorial. You should be familiar with GNU and UNIX commands. Sometimes different versions of a program format output differently, so your results might not always look exactly like the listings shown here.

Unless otherwise noted, I use Fedora 25 running Wayland and Ubuntu 16.04 LTS running X11 for the examples in this tutorial.

Localization and internationalization

Adapting a system to meet the needs of your particular locale is called localization, often abbreviated to l18n, where the number 18 simply stands for the 18 internal letters between the initial l and the final n. To adapt a system to your locale, the system must be capable of such adaptation. The process of writing software so that it can potentially be adapted to different locales is called internationalization, which is similarly abbreviated to i18n.

Locale settings and environment variables

When you install a Linux system, you set a language and an appropriate keyboard for the country where it will be used. You can use the localectl command to display the language and keyboard settings, or you can display the value of the LANG environment variable if you just want to see the system language. Listing 1 illustrates these commands on my Fedora 25 system. Note: If you use localectl alone, the default is to display status.

Listing 1. Display system settings

[ian@atticf25 ~]$ localectl status
   System Locale: LANG=en_US.UTF‑8
       VC Keymap: us
      X11 Layout: us
[ian@atticf25 ~]$ echo $LANG

You can see that I have a United States (us) keyboard and that my language is set to enUS.UTF-8. The LANG setting is typically a two-letter lowercase language code, followed by an underscore () and a two-letter uppercase country code. Then a period follows, along with a codepage or character set name — UTF-8 in this example. Where no two-letter ISO 639‑1 code is appropriate for a language, a three-letter ISO 639‑2 code is used instead. For example, fur_IT is used for Friulian in the Friuli region of Italy. Depending on context, some parts might be case-insensitive as some of my examples show.

You can use the locale command with the -a (or --all-locales) option to display a list of all the available locales on your system. On a Fedora system, this list is long, so Listing 2 shows just parts of it. On my Ubuntu 15.04 LTS system, the list is restricted to English locales because I chose English when I installed the system.

Listing 2. List available locales

[ian@atticf25 ~]$ #Display all French locales
[ian@atticf25 ~]$ locale ‑‑all‑locales | grep fr_
[ian@atticf25 ~]$ #Display all Friulian locales
[ian@atticf25 ~]$ locale ‑‑all‑locales |grep "^fur"
[ian@atticf25 ~]$ #How many locales are on this system?
[ian@atticf25 ~]$ locale ‑a | wc ‑l

Locale categories

I mentioned that there are some other things, such as numeric formatting or currency symbols, that differ by locale. These are grouped into categories. Use the locale command to view your current settings as shown in Listing 3.

Listing 3. View your locale settings

[ian@atticf25 ~]$ locale

These settings are often referred to collectively as LC_*. They are not actual environment variables, as you can see if you try to display them using the echo command. However, you can assign values to them.

The values LC_CTYPE, LC_COLLATE, LC_MESSAGES, LC_MONETARY, LC_NUMERIC, and LC_TIME are defined by POSIX. The GNU C library also supports LC_IDENTIFICATION, LC_MEASUREMENT, LC_NAME, LC_PAPER, and LC_TELEPHONE. Be aware that these extra values might not be available on all UNIX variants.

Each category has one or more keywords within it. Use the locale command with one or more category or keyword names to display information about the category or keyword, as shown in Listing 4.

Listing 4. Display information about category or keyword

[ian@atticf25 ~]$ locale LC_NUMERIC
[ian@atticf25 ~]$ locale thousands_sep decimal_point

The output display in Listing 4 is not particularly useful, except when you need the value of a single keyword. Add the -c (or --category-name) option to display the category name or the -k (or --keyword-name) to display the keyword name. You can use both keywords, and it is often useful to do so, particularly when displaying multiple categories. Listing 5 shows some examples of these options.

Listing 5. Using the -c and -k options of locale

[ian@atticf25 ~]$ locale ‑‑category‑name ‑‑keyword‑name LC_NUMERIC LC_PAPER
[ian@atticf25 ~]$ locale ‑ck thousands_sep decimal_point height

You can change your locale settings by changing the value of LANG; for example, LANG=en_GB.UTF8 changes all of my US ENGLISH locale settings to British English. This changes all the LC_* settings. Listing 6 shows an example on my Ubuntu 16.04 LTS system. Unlike assignment to ordinary environment variables, changes to LANG are inherited by child processes and do not need to be exported.

Note that Debian-based systems, including Ubuntu, have an additional LANGUAGE value that is not changed when you change the LANG value. LANGUAGE is normally the same as your system language, but you can set it to a colon-separated list of languages that can be used for messages. This gives a fallback mechanism if a message is not available in a particular language.

Listing 6. LAMG and LANGUAGE settings on Ubuntu

ian@attic‑u16:~$ LANG=en_GB.UTF8
ian@attic‑u16:~$ locale

Sometimes you want to change all the LC* categories without changing the system language or locale settings. This is most often done in scripts, but you can override the environment for a single command. Set LC_ALL to change all the LC*c categories at once, or set one or more individual categories, such as LC_MONETARY. Listing 7 shows some examples on my Ubuntu 16.04 LTS system.

Listing 7. Overriding locale settings

ian@attic‑u16:~$ locale ‑ck int_curr_symbol currency_symbol
int_curr_symbol="USD "
ian@attic‑u16:~$ LC_ALL=en_GB.UTF8 locale ‑ck int_curr_symbol currency_symbol
int_curr_symbol="GBP "
ian@attic‑u16:~$ LC_MONETARY=en_ZA.UTF8 locale ‑ck int_curr_symbol currency_symbol
int_curr_symbol="ZAR "

The C locale

The C (or POSIX) locale provides a basic locale that programs and shell scripts often use, and it is the default locale for C programs. With this locale, programs or scripts can use a well-known environment without needing to worry about localization where this is not needed. Scripts and programs can both set other locales as needed.

Additional language support

Most systems are initially installed with a single language supported. As you add packages to your system, those packages can add language-specific components based on your system language settings. If you are missing language support for something you are trying to do, you will probably see an error message and any output is likely to be in the language of your system. Listing 8 shows how to list the names of the days of the week and their abbreviations in both English and French.

Listing 8. Listing day names

ian@attic‑u16:~$ locale ‑ck day abday
ian@attic‑u16:~$ LC_ALL=fr_FR.UTF8 locale ‑ck day abday
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory

Oops! As you see, this system does not seem to have French language support installed. Now, I show you how to install additional language support on Ubuntu and Fedora systems.

Ubuntu has a check-language-support program that you can use to find out which packages are needed for a particular language. With no arguments, it identifies missing language support on your system. Listing 9 shows some examples.

Listing 9. Finding missing language packages on Ubuntu

ian@attic‑u16:~$ check‑language‑support
gimp‑help‑en hunspell‑en‑au hunspell‑en‑ca hyphen‑en‑gb libreoffice‑help‑en‑gb 
libreoffice‑l10n‑en‑gb libreoffice‑l10n‑en‑za mythes‑en‑au thunderbird‑locale‑en‑gb
ian@attic‑u16:~$ check‑language‑support ‑l en_US
ian@attic‑u16:~$ check‑language‑support ‑l fr
firefox‑locale‑fr gimp‑help‑fr hunspell‑fr hyphen‑fr language‑pack‑fr language‑pack‑gnome‑fr 
libreoffice‑help‑fr libreoffice‑l10n‑fr mythes‑fr thunderbird‑locale‑fr wfrench

You can use the output of check-language-support as input to apt-get to install the required packages. Listing 10 shows how to install the packages needed from French.

Listing 10. Installing French language support using apt-get

ian@attic‑u16:~$ sudo apt‑get install $(check‑language‑support ‑l fr)
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  gimp‑help‑common hunspell‑fr‑classical language‑pack‑fr‑base
Suggested packages:
  hunspell libreoffice‑grammarcheck‑fr
The following NEW packages will be installed:
  firefox‑locale‑fr gimp‑help‑common gimp‑help‑fr hunspell‑fr
  hunspell‑fr‑classical hyphen‑fr language‑pack‑fr language‑pack‑fr‑base
  language‑pack‑gnome‑fr language‑pack‑gnome‑fr‑base libreoffice‑help‑fr
  libreoffice‑l10n‑fr mythes‑fr thunderbird‑locale‑fr wfrench
0 upgraded, 15 newly installed, 0 to remove and 6 not upgraded.
Need to get 41.6 MB of archives.
After this operation, 115 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Setting up language‑pack‑fr‑base (1:16.04+20160627) ...
Generating locales (this might take a while)...
  fr_BE.UTF‑8... done
  fr_CA.UTF‑8... done
  fr_CH.UTF‑8... done
  fr_FR.UTF‑8... done
  fr_LU.UTF‑8... done
Generation complete.
Setting up language‑pack‑gnome‑fr‑base (1:16.04+20160627) ...
Processing triggers for dictionaries‑common (1.26.3) ...
Processing triggers for bamfdaemon (0.5.3~bzr0+16.04.20160824‑0ubuntu1) ...
Rebuilding /usr/share/applications/bamf‑2.index...

Now that you have French language support installed, you can display the day names and abbreviations as shown in Listing 11.

Listing 11. Displaying day names in French

ian@attic‑u16:~$ LC_ALL=fr_FR.UTF8 locale ‑ck day abday

Fedora uses a language meta pack to install language support for a particular language. For example, langpacks-es is the meta package for Spanish language support. Listing 12 shows an example of installing the Spanish language pack on my Fedora 25 system using dnf. On older systems, you might need to use yum instead.

Listing 12. Installing Spanish language support using dnf

[root@atticf25 ~]#dnf install langpacks‑es
Last metadata expiration check: 0:06:08 ago on Wed May 31 22:16:08 2017.
Dependencies resolved.
 Package                        Arch    Version                  Repository
 autocorr‑es                    noarch  1:‑3.fc25         updates  181 k
 glibc‑langpack‑es              x86_64  2.24‑4.fc25              updates  411 k
 gnome‑getting‑started‑docs‑es  noarch  3.22.0‑1.fc25            fedora    10 M
 hunspell‑es                    noarch  1:0.7‑6.fc24             fedora   251 k
 hyphen‑es                      noarch  0.20110222svn‑8.fc24     fedora    16 k
 langpacks‑es                   noarch  1.0‑8.fc25               fedora   8.5 k
 libreoffice‑langpack‑es        x86_64  1:‑3.fc25         updates  7.3 M
 man‑pages‑es                   noarch  1.55‑26.fc25             fedora   1.6 M
 mythes‑es                      noarch  0.20150304‑4.fc24        fedora   710 k

Transaction Summary
Install  9 Packages

Total download size: 21 M
Installed size: 49 M
Is this ok [y/N]: y
Downloading Packages:
(1/9): langpacks‑es‑1.0‑8.fc25.noarch.rpm        27 kB/s | 8.5 kB     00:00    


  autocorr‑es.noarch 1:‑3.fc25                                           
  glibc‑langpack‑es.x86_64 2.24‑4.fc25                                          
  gnome‑getting‑started‑docs‑es.noarch 3.22.0‑1.fc25                            
  hunspell‑es.noarch 1:0.7‑6.fc24                                               
  hyphen‑es.noarch 0.20110222svn‑8.fc24                                         
  langpacks‑es.noarch 1.0‑8.fc25                                                
  libreoffice‑langpack‑es.x86_64 1:‑3.fc25                               
  man‑pages‑es.noarch 1.55‑26.fc25                                              
  mythes‑es.noarch 0.20150304‑4.fc24                                            


You can now use Spanish locales. For example, Listing 13 shows the day names and their abbreviations in Spanish.

Listing 13. Spanish day names and their abbreviations

[ian@atticf25 ~]$ LANG=es_ES.UTF8 locale ‑ck day abday

Persistent locale changes

If you want to use a specific language or locale setting whenever you log in, you can set the LANG, LANGUAGE, LCALL, or the individual LC* settings in your .bashrc profile. Of course, you need the appropriate language support packages installed before you do this.

To change the language used as the system default, you need to install the desired language packs and then use the localectl command to change the default system locale settings. You can change the language, LC_* settings, or the keymap used for console or X11 terminals. These settings are used before login and for any user who does not override them.

Code page conversions

Early computers did not communicate with other computers, and each manufacturer used a custom way of representing characters internally. In 1963, American Standard Code for Information Interchange (ASCII) was released with the goal of being a common interchange language. With only 127 characters being represented, it had limited use outside English. A variety of code pages were adopted by standards organizations, such as ISO, and by computer manufactures. A document created on one system using a particular code page wasn’t necessarily usable on another system unless that system also supported the same code page or something compatible.

Another widely used standard in the Western world is ISO-8859-1 which is similar but not identical to Windows® code page 1252. It is an 8-bit encoding with the first 127 characters encoded the same as ASCII. ISO-8859-1 supports many Western languages reasonably well, but it is missing some characters and some symbols such as punctuation.

Unicode is a modern encoding standard developed by The Unicode Consortium. Its goal is to help people around the world use computers in any language. Unicode originally started as a 16-bit encoding, but since July 1996, it has encoded characters in the range U+0000..U+10FFFF, which requires at least a 21-bit code space. There are three common encodings in use:

  • UTF-8 uses one to four 8-bit bytes to represent a character, with characters in the range 0-127 being represented in one byte using the same encoding as ASCII.
  • UTF-16 uses one or two 16-bit units to represent a character.
  • UTF-32 uses a single 32-bit unit to represent a character.

All of the examples you have seen so far in this tutorial use UTF-8 encoding, although you saw some other possibilities, such as fr_BE.iso88591, in the output of locale -alocale-a.

You use the iconv program to convert between character encodings. Obviously, if you go from a large character set to a smaller one, the conversion does not happen properly. You have the option of ignoring the problem, or transliterating the character to some approximation. Transliteration might drop accents from characters, or replace an unsupported currency symbol with its short name, such as EUR for Euro.

Listing 14 shows the difference between transliterating output and ignoring invalid characters as I convert from UTF-8 to ASCII. In the first example, some characters lose accents or cedillas, and two are transliterated to common alternative representations. In the second example, much of the text is lost as it cannot be represented in the limited ASCII character set.

Listing 14. Converting from UTF-8 to ASCII

ian@attic‑u16:~$ echo abc ß α € àḃç | iconv ‑f UTF‑8 ‑t ASCII//TRANSLIT
abc ss ? EUR abc
ian@attic‑u16:~$ echo abc ß α € àḃç | iconv ‑f UTF‑8 ‑t ASCII//IGNORE
iconv: illegal input sequence at position 22

If you try the same examples using ISO-8859-1 instead of ASCII, your output has some strange looking characters. In Listing 15, I show an example using a French error message that I capture to a file. Then, I convert the file to ISO-8859-1 and back to UTF-8 so that you can see the differences and that this example does not lose data.

Listing 15. Lossless conversion example

ian@attic‑u16:~$ LANGUAGE=fr_FR ls no‑such‑file 2>ic‑utf8
ian@attic‑u16:~$ cat ic‑utf8
ls: impossible d'accéder à 'no‑such‑file': Aucun fichier ou dossier de ce type
ian@attic‑u16:~$ iconv ‑f ISO‑8859‑1 ‑t UTF‑8 ‑o ic‑utf8 ic‑8859 
ian@attic‑u16:~$ iconv ‑f ISO‑8859‑1 ‑t UTF‑8 ‑o ic‑utf8‑out ic‑8859 
ian@attic‑u16:~$ ls ‑l ic‑*
‑rw‑rw‑r‑‑ 1 ian ian 79 May 31 17:22 ic‑8859
‑rw‑rw‑r‑‑ 1 ian ian 81 May 31 17:23 ic‑utf8
‑rw‑rw‑r‑‑ 1 ian ian 81 May 31 17:24 ic‑utf8‑out
ian@attic‑u16:~$ diff ic‑utf8 ic‑utf8‑out
ian@attic‑u16:~$ diff ‑q ic‑8859 ic‑utf8
Files ic‑8859 and ic‑utf8 differ

Time zone settings and environment variables

As with language and language related settings, you might need to configure time zone settings. Your system has a hardware clock and, once booted, probably synchronizes its time setting with a Network Time Protocol (NTP) server. Your company can maintain its own NTP server or server pool, or you might use a public server, such as, which is a large cluster of time servers.

To be able to synchronize time across millions of computers and appliances, you need a time standard to work with. This standard is called Coordinated Universal Time (UTC). You also need some measure of the difference between UTC and your local time. Because the Earth’s rotation time is 24 hours, time zones usually span a single hour. For example, Eastern Standard time in the United States is five hours behind UTC (written as UTC-5). Some places, such as South Australia, use a half hour granularity in their offset rather than a whole hour.

Greenwich Mean Time (GMT) is the same time as UTC, but it is not the standard. Rather, it is a time zone that is used in the British Isles and elsewhere.

Use the date command to display your current time and date information. You can choose several different output formats, and you can further customize the output format for specific purposes, such as when you are scripting. I show some examples in Listing 16.

Listing 16. Using the date command

ian@attic‑u16:~$ #Date and time in my system format
ian@attic‑u16:~$ date
Wed May 31 18:46:24 EDT 2017
ian@attic‑u16:~$ #Date and time in ISO 8601 format
ian@attic‑u16:~$ date ‑‑iso‑8601
ian@attic‑u16:~$ #Date and time in RFC 2822 format
ian@attic‑u16:~$ date ‑‑rfc‑2822
Wed, 31 May 2017 18:48:27 ‑0400
ian@attic‑u16:~$ #UTC Date and time
ian@attic‑u16:~$ date ‑u
Wed May 31 22:49:21 UTC 2017
ian@attic‑u16:~$ #Date and time at 09:00 next Friday
ian@attic‑u16:~$ date ‑‑date='09:00 next Fri'
Fri Jun  2 09:00:00 EDT 2017
ian@attic‑u16:~$ #What day is today?
ian@attic‑u16:~$ date "+%A"

The timedatectl command is somewhat similar to the localectl command that I used earlier. Used with no arguments, it displays the current status, as shown in Listing 17.

Listing 17. Displaying date and time information

ian@attic‑u16:~$ timedatectl
      Local time: Wed 2017‑05‑31 19:04:09 EDT
  Universal time: Wed 2017‑05‑31 23:04:09 UTC
        RTC time: Wed 2017‑05‑31 23:04:09
       Time zone: America/New_York (EDT, ‑0400)
 Network time on: yes
NTP synchronized: yes
 RTC in local TZ: no

Note the time zone specification, which contains three parts. The time zone name is America/New_York. The common abbreviation is EDT (Eastern Daylight Time), and it is 4 hours behind UTC. Use the tzselect command to check the right format for your own time zone or for a time zone somewhere else. This is an interactive command that steps you through a few choices. Listing 18 shows how to check the time zone for South Australia.

Listing 18. Using tzselect

ian@attic‑u16:~$ tzselect
Please identify a location so that time zone rules can be set correctly.
Please select a continent, ocean, "coord", or "TZ".
 1) Africa
 2) Americas
 3) Antarctica
 4) Asia
 5) Atlantic Ocean
 6) Australia
 7) Europe
 8) Indian Ocean
 9) Pacific Ocean
10) coord ‑ I want to use geographical coordinates.
11) TZ ‑ I want to specify the time zone using the Posix TZ format.
#? 6
Please select one of the following time zone regions.
1) Lord Howe Island              8) Queensland (most areas)
2) Macquarie Island              9) Queensland (Whitsunday Islands)
3) Tasmania (most areas)         10) South Australia
4) Tasmania (King Island)         11) Northern Territory
5) Victoria                 12) Western Australia (most areas)
6) New South Wales (most areas)         13) Western Australia (Eucla)
7) New South Wales (Yancowinna)
#? 10

The following information has been given:

    South Australia

Therefore TZ='Australia/Adelaide' will be used.
Local time is now:    Thu Jun  1 08:41:27 ACST 2017.
Universal Time is now:    Wed May 31 23:11:27 UTC 2017.
Is the above information OK?
1) Yes
2) No
#? 1

You can make this change permanent for yourself by appending the line
    TZ='Australia/Adelaide'; export TZ
to the file '.profile' in your home directory; then log out and log in again.

Here is that TZ value again, this time on standard output so that you
can use the /usr/bin/tzselect command in shell scripts:

Note the reference to the TZ environment variable. You might find that if you attempt to display the TZ environment variable, it is not set. As noted in the output, you can set and export it in your .profile file if you want to use a time zone that is different from your system time zone. As with some of the LC_* variables, you can also use it to affect a single command, such as the date command as I show in Listing 19.

Listing 19. Using the TZ environment variable

ian@attic‑u16:~$ date
Wed May 31 19:25:06 EDT 2017
ian@attic‑u16:~$ TZ='Australia/Adelaide' date
Thu Jun  1 08:55:08 ACST 2017

Your system maintains time zone information in a few important locations. Some systems (such as Ubuntu) maintain the system time zone name in /etc/timezone. The file /etc/localtime is a link to a timezone file in the /usr/share/zoneinfo directory. And finally, the /usr/share/zoneinfo directory contains subdirectories for major zones, such as Americas, and then either links or files for the individual time zones. Listing 20 shows some examples on Ubuntu and Fedora systems.

Listing 20. System timezone files

ian@attic‑u16:~$ #Ubuntu 16.04 LTS
ian@attic‑u16:~$ cat /etc/timezone 
ian@attic‑u16:~$ ls ‑l /etc/localtime 
lrwxrwxrwx 1 root root 36 Dec 23 14:54 /etc/localtime ‑> /usr/share/zoneinfo/America/New_York
ian@attic‑u16:~$ ls ‑l /usr/share/zoneinfo/America/New_York
lrwxrwxrwx 1 root root 13 Dec  7 05:59 /usr/share/zoneinfo/America/New_York ‑> ../posixrules

ian@atticf25 ~]$ #Fedora 25
[ian@atticf25 ~]$ ls /etc/timezone
ls: cannot access '/etc/timezone': No such file or directory
[ian@atticf25 ~]$ ls ‑l /etc/localtime
lrwxrwxrwx. 1 root root 38 Apr 26 18:09 /etc/localtime ‑> ../usr/share/zoneinfo/America/New_York
[ian@atticf25 ~]$ ls ‑l /usr/share/zoneinfo/America/New_York
‑rw‑r‑‑r‑‑. 3 root root 3545 Mar 27 22:18 /usr/share/zoneinfo/America/New_York

If you need to change the system date or time zone, use the tmedatectl command. See the man pages for more information on the options and commands available. You can do things like turn NTP synchronization on or foo, as well as control how the system real time clock is updated or used.

This concludes your introduction to localization and time zone settings.