<locale.h> locale handling| Function | Description |
|---|---|
setlocale() |
Set the locale |
localeconv() |
Get information about the current locale |
The “locale” is the details of how the program should run given its physical location on the planet.
For example, in one locale, a unit of money might be printed as $123, and in another €123.
Or one locale might use ASCII encoding and another UTF-8 encoding.
By default, the program runs in the “C” locale. It has a basic set of characters with a single-byte encoding. If you try to print UTF-8 characters in the C locale, nothing will print. You have to switch to a proper locale.
setlocale()Set the locale
#include <locale.h>
char *setlocale(int category, const char *locale);Sets the locale for the given category.
Category is one of the following:
| Category | Description |
|---|---|
LC_ALL |
All of the following categories |
LC_COLLATE |
Affects the strcoll() and strxfrm() functions |
LC_CTYPE |
Affects the functions in <ctype.h> |
LC_MONETARY |
Affects the monetary information returned from localeconv() |
LC_NUMERIC |
Affects the decimal point for formatted I/O and formatted string functions, and the monetary information returned from localeconv() |
LC_TIME |
Affects the strftime() and wcsftime() functions |
And there are three portable things you can pass in for locale; any other string passed in is implementation-defined and non-portable.
| Locale | Description |
|---|---|
"C" |
Set the program to the C locale |
"" |
(Empty string) Set the program to the native locale of this system |
NULL |
Change nothing; just return the current locale |
| Other | Set the program to an implementation-defined locale |
The most common call, I’d wager, is this:
// Set all locale settings to the local, native locale
setlocale(LC_ALL, "");Handily, setlocale() returns the locale that was just set, so you could see what the actual locale is on your system.
On success, returns a pointer to the string representing the current locale. You may not modify this string, and it might be changed by subsequent calls to setlocale().
On failure, returns NULL.
Here we get the current locale. Then we set it to the native locale, and print out what that is.
#include <stdio.h>
#include <locale.h>
int main(void)
{
char *loc;
// Get the current locale
loc = setlocale(LC_ALL, NULL);
printf("Starting locale: %s\n", loc);
// Set (and get) the locale to native locale
loc = setlocale(LC_ALL, "");
printf("Native locale: %s\n", loc);
}Output on my system:
Starting locale: C
Native locale: en_US.UTF-8Note that my native locale (on a Linux box) might be different from what you see.
Nevertheless, I can explicitly set it on my system without a problem, or to any other locale I have installed:
But again, your system might have different locales defined.
localeconv(), strcoll(), strxfrm(), strftime(), wcsftime(), printf(), scanf(), <ctype.h>
localeconv()Get information about the current locale
#include <locale.h>
struct lconv *localeconv(void);This function just returns a pointer to a struct lconv, but is still a bit of a powerhouse.
The returned structure contains tons of information about the locale. Here are the fields of struct lconv and their meanings.
First, some conventions. In the field names, below, a _p_ means “positive”, and _n_ means “negative”, and int_ means “international”. Though a lot of these are type char or char*, most (or the strings they point to) are actually treated as integers21.
Before we go further, know that CHAR_MAX (from <limits.h>) is the maximum value that can be held in a char. And that many of the following char values use that to indicate the value isn’t available in the given locale.
| Field | Description |
|---|---|
char *mon_decimal_point |
Decimal pointer character for money, e.g. ".". |
char *mon_thousands_sep |
Thousands separator character for money, e.g. ",". |
char *mon_grouping |
Grouping description for money (see below). |
char *positive_sign |
Positive sign for money, e.g. "+" or "". |
char *negative_sign |
Negative sign for money, e.g. "-". |
char *currency_symbol |
Currency symbol, e.g. "$". |
char frac_digits |
When printing monetary amounts, how many digits to print past the decimal point, e.g. 2. |
char p_cs_precedes |
1 if the currency_symbol comes before the value for a non-negative monetary amount, 0 if after. |
char n_cs_precedes |
1 if the currency_symbol comes before the value for a negative monetary amount, 0 if after. |
char p_sep_by_space |
Determines the separation of the currency symbol from the value for non-negative amounts (see below). |
char n_sep_by_space |
Determines the separation of the currency symbol from the value for negative amounts (see below). |
char p_sign_posn |
Determines the positive_sign position for non-negative values. |
char p_sign_posn |
Determines the positive_sign position for negative values. |
char *int_curr_symbol |
International currency symbol, e.g. "USD ". |
char int_frac_digits |
International value for frac_digits. |
char int_p_cs_precedes |
International value for p_cs_precedes. |
char int_n_cs_precedes |
International value for n_cs_precedes. |
char int_p_sep_by_space |
International value for p_sep_by_space. |
char int_n_sep_by_space |
International value for n_sep_by_space. |
char int_p_sign_posn |
International value for p_sign_posn. |
char int_n_sign_posn |
International value for n_sign_posn. |
Even though many of these have char type, the value stored within is meant to be accessed as an integer.
All the sep_by_space variants deal with spacing around the currency sign. Valid values are:
| Value | Description |
|---|---|
0 |
No space between currency symbol and value. |
1 |
Separate the currency symbol (and sign, if any) from the value with a space. |
2 |
Separate the sign symbol from the currency symbol (if adjacent) with a space, otherwise separate the sign symbol from the value with a space. |
The sign_posn variants are determined by the following values:
| Value | Description |
|---|---|
0 |
Put parens around the value and the currency symbol. |
1 |
Put the sign string in front of the currency symbol and value. |
2 |
Put the sign string after the currency symbol and value. |
3 |
Put the sign string directly in front of the currency symbol. |
4 |
Put the sign string directly behind the currency symbol. |
Returns a pointer to the structure containing the locale information.
The program may not modify this structure.
Subsequent calls to localeconv() may overwrite this structure, as might calls to setlocale() with LC_ALL, LC_MONETARY, or LC_NUMERIC.
Here’s a program to print the locale information for the native locale.
#include <stdio.h>
#include <locale.h>
#include <limits.h> // for CHAR_MAX
void print_grouping(char *mg)
{
int done = 0;
while (!done) {
if (*mg == CHAR_MAX)
printf("CHAR_MAX ");
else
printf("%c ", *mg + '0');
done = *mg == CHAR_MAX || *mg == 0;
mg++;
}
}
int main(void)
{
setlocale(LC_ALL, "");
struct lconv *lc = localeconv();
printf("mon_decimal_point : %s\n", lc->mon_decimal_point);
printf("mon_thousands_sep : %s\n", lc->mon_thousands_sep);
printf("mon_grouping : ");
print_grouping(lc->mon_grouping);
printf("\n");
printf("positive_sign : %s\n", lc->positive_sign);
printf("negative_sign : %s\n", lc->negative_sign);
printf("currency_symbol : %s\n", lc->currency_symbol);
printf("frac_digits : %c\n", lc->frac_digits);
printf("p_cs_precedes : %c\n", lc->p_cs_precedes);
printf("n_cs_precedes : %c\n", lc->n_cs_precedes);
printf("p_sep_by_space : %c\n", lc->p_sep_by_space);
printf("n_sep_by_space : %c\n", lc->n_sep_by_space);
printf("p_sign_posn : %c\n", lc->p_sign_posn);
printf("p_sign_posn : %c\n", lc->p_sign_posn);
printf("int_curr_symbol : %s\n", lc->int_curr_symbol);
printf("int_frac_digits : %c\n", lc->int_frac_digits);
printf("int_p_cs_precedes : %c\n", lc->int_p_cs_precedes);
printf("int_n_cs_precedes : %c\n", lc->int_n_cs_precedes);
printf("int_p_sep_by_space: %c\n", lc->int_p_sep_by_space);
printf("int_n_sep_by_space: %c\n", lc->int_n_sep_by_space);
printf("int_p_sign_posn : %c\n", lc->int_p_sign_posn);
printf("int_n_sign_posn : %c\n", lc->int_n_sign_posn);
}Output on my system:
mon_decimal_point : .
mon_thousands_sep : ,
mon_grouping : 3 3 0
positive_sign :
negative_sign : -
currency_symbol : $
frac_digits : 2
p_cs_precedes : 1
n_cs_precedes : 1
p_sep_by_space : 0
n_sep_by_space : 0
p_sign_posn : 1
p_sign_posn : 1
int_curr_symbol : USD
int_frac_digits : 2
int_p_cs_precedes : 1
int_n_cs_precedes : 1
int_p_sep_by_space: 1
int_n_sep_by_space: 1
int_p_sign_posn : 1
int_n_sign_posn : 1