What is a Locale?
A locale is a collection of rules and data that tell a program how to format and display information for a specific language and region. The same date — 15 May 2025 — is written as 15/05/2025 in India, 05/15/2025 in the USA, and 15.05.2025 in Germany. The same decimal number 1234.56 becomes 1.234,56 in Germany (period as thousands separator, comma as decimal separator). Locales handle all of this automatically.
Keywords in This Post
A locale is not one monolithic setting. It is split into categories, each controlling a different aspect of cultural formatting. You can mix them independently.
| Category | What it controls | Example difference (de_DE vs en_US) |
|---|---|---|
| LC_CTYPE | Character classification (is ‘ä’ a letter? uppercase of ‘ß’?) | German ‘ä’, ‘ö’, ‘ü’ are letters; in C locale they are not |
| LC_COLLATE | Sort order of strings (strcoll, strxfrm) | German ‘ü’ sorts as ‘ue’; Spanish ‘ñ’ after ‘n’ |
| LC_MONETARY | Currency symbol, decimal/thousands separators for money | €1.234,56 vs $1,234.56 |
| LC_NUMERIC | Decimal point and thousands separator for non-monetary numbers | 1.234,56 (Germany) vs 1,234.56 (US) |
| LC_TIME | Date/time format strings used by strftime() | “Dienstag, 15 Mai” vs “Tuesday, May 15” |
| LC_MESSAGES | Language for yes/no responses and program messages | “Ja/Nein” vs “Yes/No” |
The setlocale() function controls which locale a program uses. It can set one category, all categories, or just query the current setting.
#include <locale.h>
char *setlocale(int category, const char *locale);
/* Returns the locale name for that category on success, or NULL on error */
| Call | What it does |
|---|---|
| setlocale(LC_ALL, “”) | Read all LC_* / LANG environment variables and apply them |
| setlocale(LC_TIME, “de_DE”) | Force German date/time formatting regardless of system locale |
| setlocale(LC_ALL, NULL) | Query the current locale without changing it |
| setlocale(LC_ALL, “C”) | Reset to the default POSIX/C locale (ASCII, US conventions) |
The most important call: setlocale(LC_ALL, "")
Passing an empty string "" means “read the locale from environment variables”. Without this call, your program ignores LANG, LC_TIME etc. completely and behaves as if locale is “C”. Always call this at the start of any user-facing program that should be locale-aware.
Locale data files are stored under /usr/share/locale/. Each subdirectory is named using the convention language[_territory[.codeset]]:
| Locale name | Language | Territory | Encoding |
|---|---|---|---|
| en_IN.UTF-8 | English | India | UTF-8 |
| hi_IN.UTF-8 | Hindi | India | UTF-8 |
| de_DE.utf-8 | German | Germany | UTF-8 |
| fr_CH | French | Switzerland | (default) |
| C (or POSIX) | ASCII English | Default (always present) | ASCII |
# List all locales installed on your system
locale -a
# Check what locale your current shell session is using
locale
When you call setlocale(LC_ALL, ""), the C library reads these environment variables in order of precedence:
| Priority | Variable | Scope |
|---|---|---|
| 1 (highest) | LC_ALL | Overrides everything — all 6 categories |
| 2 | LC_TIME, LC_NUMERIC, etc. | Individual category overrides |
| 3 (lowest) | LANG | Default for any category not set above |
# Set German as default, but use Italian for date/time
LANG=de_DE LC_TIME=it_IT ./my_program
# Force French for everything, even if LC_TIME says something else
LC_ALL=fr_FR LC_TIME=en_US ./my_program # LC_ALL wins
When strftime() is called after setting a locale, it uses localised names for weekdays and months. The same format string produces different output in different locales.
#include <locale.h>
#include <time.h>
#include <stdio.h>
void show_date(const char *locale_name)
{
time_t t = 1747267215; /* A fixed time for consistent demo */
struct tm *tm_ptr;
char buf[128];
setlocale(LC_TIME, locale_name);
tm_ptr = localtime(&t);
strftime(buf, sizeof(buf), "%A, %d %B %Y", tm_ptr);
printf("[%-12s] %s\n", locale_name, buf);
}
int main(void)
{
show_date("en_US"); /* Thursday, 15 May 2025 */
show_date("de_DE"); /* Donnerstag, 15 Mai 2025 */
show_date("fr_FR"); /* jeudi, 15 mai 2025 */
show_date("hi_IN"); /* (depends on available locales) */
return 0;
}
The I18N abbreviation: “Internationalisation” is often written as I18N (I + 18 letters + N). The process of adapting a program for a specific locale is called L10N (Localisation = L + 10 letters + N). A well-written program does I18N once (uses locale-aware functions), and L10N is then “free” — just install the right locale data.
Up Next in This Series
Part 8 covers updating the system clock with settimeofday() and adjtime(), and why gradual adjustment is preferred.
