Dr. Roger Ianjamasimanana

The locale.h library and macros in C

By Dr. Roger Ianjamasimanana

1. What is the locale.h header in C

The C standard library’s locale.h header provides functions and macros for managing and customizing locale settings, which dictate language-specific or region-specific conventions, such as date/time formats, numeric formatting, monetary formatting, and more. This mechanism helps your programs adapt to different cultural conventions, facilitating internationalization and localization.

2. Overview of locale.h

The primary usage of locale.h revolves around:

  • setlocale() – Setting or retrieving the current locale.
  • localeconv() – Retrieving locale-specific numeric and monetary information.
  • Macros (e.g., LC_ALL, LC_TIME) for specifying which locale category to set.
  • Data structures like struct lconv for holding locale-specific formatting details.

By adjusting locale categories (such as LC_CTYPE for character classification or LC_MONETARY for currency formatting), you can tailor how your program processes and displays data to match user expectations in various regions.

3. Macros in locale.h

The following table lists the macros defined in locale.h and explains what each one does. These macros are used with setlocale() to specify the locale category you want to affect or retrieve.

The locale.h macros Summary / Usage
LC_CTYPE Controls character classification and case conversion (e.g., isalpha, toupper).
LC_NUMERIC Controls numeric formatting rules (e.g., decimal point, grouping of digits).
LC_TIME Controls formatting of dates and times.
LC_COLLATE Determines how string collation (i.e., sorting) is done for locale-specific ordering.
LC_MONETARY Controls currency formatting, such as currency symbols and decimal separators for monetary values.
LC_MESSAGES Used for message translations (often used in internationalized applications).
LC_ALL Applies the locale change to all categories simultaneously.
LC_PAPER Specifies formatting for paper sizes and dimensions (non-standard in some C libraries).
LC_NAME Manages conventions for personal name formats (non-standard usage).
LC_ADDRESS Defines address formats for specific locales (non-standard usage).
LC_TELEPHONE Defines telephone number formats (non-standard usage).
LC_MEASUREMENT Controls measurement systems (metric vs. imperial, etc.).
LC_IDENTIFICATION Specifies metadata about the locale itself (non-standard usage).

4. Main functions

The locale.h header primarily exposes two key functions:


// 1. setlocale
char *setlocale(int category, const char *locale);

// 2. localeconv
struct lconv *localeconv(void);
  • setlocale(int category, const char *locale)
    Sets or retrieves the current locale for the specified category. Passing NULL as the locale argument returns the current setting, while providing a locale string (e.g., "en_US.UTF-8" or "fr_FR") changes the locale for that category.
  • localeconv()
    Returns a pointer to a static struct lconv, which contains locale-specific formatting rules for numeric and monetary data.

5. Example C codes using locale.h

// locale_example.c
#include <stdio.h>
#include <locale.h>

int main(void) {
    // Set the locale for all categories to the user's default environment
    setlocale(LC_ALL, "");

    // Retrieve numeric and monetary formatting rules
    struct lconv *lc = localeconv();

    printf("Decimal point: %s\n", lc->decimal_point);
    printf("Thousands separator: %s\n", lc->thousands_sep);
    printf("Currency symbol: %s\n", lc->currency_symbol);
    printf("Positive sign: %s\n", lc->positive_sign);
    printf("Negative sign: %s\n", lc->negative_sign);

    return 0;
}

In this example, setlocale(LC_ALL, "") sets the locale to the user’s default environment variables (e.g., LANG, LC_* in Linux). If the default evironment variables are not retrieved, you may set the locale explicitely as, e.g., setlocale(LC_ALL, "en_US.UTF-8"). To list all the available locales, you can type the following command in your terminale:

locale -a

The localeconv() call retrieves the formatting information, and we print some of the fields from the returned lconv structure.

5.1. Formatting monetary values

When building an application that handles currencies from different regions (e.g., USD, EUR, JPY), you can use setlocale() and localeconv() to automatically apply local currency symbols, decimal separators, and spacing conventions for output. Below is a minimal example:

// format_money.c

#include <stdio.h>
#include <locale.h>
#include <stdlib.h>

int main(void) {
    // Set the locale to US English, for demonstration.
    // Replace "en_US.UTF-8" with another locale identifier if needed.
    if (setlocale(LC_ALL, "en_US.UTF-8") == NULL) {
        fprintf(stderr, "Error: Could not set locale to en_US.UTF-8.\n");
        return EXIT_FAILURE;
    }

    // Obtain the locale-specific formatting rules
    struct lconv *loc = localeconv();

    // Print out a money-like value using the discovered currency symbol
    double price = 12345.67;
    printf("Currency symbol: %s\n", loc->currency_symbol);
    printf("Decimal point: %s\n", loc->mon_decimal_point);
    printf("Thousands separator: %s\n", loc->mon_thousands_sep);

    // Simple example of formatting a monetary value
    // (In real applications, you'd use more sophisticated logic or sprintf-like functions.)
    printf("Price: %s%.2f\n", loc->currency_symbol, price);

    return EXIT_SUCCESS;
}
✍️
Monetary formatting

The actual monetary formatting often relies on additional logic or functions to insert grouping characters (e.g., commas) at correct positions. The mon_grouping and other fields in struct lconv provide guidance on how to properly format numeric and monetary values.

5.2. Localizing dates and times with locale.h

A scheduler or calendar application might need to display dates and times in formats familiar to users around the world. By setting LC_TIME to "en_US", "fr_FR", etc., you can ensure day/month names, weekday abbreviations, and date order reflect each locale’s conventions.


// localize_time.c

#include <stdio.h>
#include <time.h>
#include <locale.h>
#include <stdlib.h>

int main(void) {
    // Set the time locale to French for demonstration
    if (setlocale(LC_TIME, "fr_FR.UTF-8") == NULL) {
        fprintf(stderr, "Error: Could not set locale to fr_FR.UTF-8.\n");
        return EXIT_FAILURE;
    }

    // Get the current time
    time_t now = time(NULL);
    struct tm *local_time = localtime(&now);

    // Create a buffer to hold the formatted time string
    char time_str[128];

    // Format the date/time according to the current LC_TIME locale
    // For example, %c is the "date and time representation for the locale"
    strftime(time_str, sizeof(time_str), "%c", local_time);

    printf("Localized date/time: %s\n", time_str);

    return EXIT_SUCCESS;
}
✍️
Tips

Experiment with different format specifiers (e.g., %B for full month name) to see how they adapt to various locales (French, German, etc.).

5.3. Sorting and string collation using locale.h

When comparing or sorting strings, LC_COLLATE determines the alphabetical order for the current locale. This is crucial for multi-language data where accented characters must be correctly interpreted. Below is an example demonstrating how strcoll() can be used to compare strings according to locale-specific rules.

// sort_collate.c

#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <stdlib.h>

int main(void) {
    // Set collation locale to Spanish
    if (setlocale(LC_COLLATE, "es_ES.UTF-8") == NULL) {
        fprintf(stderr, "Error: could not set locale to es_ES.UTF-8.\n");
        return EXIT_FAILURE;
    }

    // Sample strings with accented characters
    const char *str1 = "árbol";   // Spanish for "tree"
    const char *str2 = "ármol";   // Fake word, but for demonstration

    int result = strcoll(str1, str2);

    printf("Comparing '%s' and '%s'\n", str1, str2);

    if (result < 0) {
        printf("'%s' sorts before '%s'\n", str1, str2);
    } else if (result > 0) {
        printf("'%s' sorts after '%s'\n", str1, str2);
    } else {
        printf("Strings are considered equal.\n");
    }

    return EXIT_SUCCESS;
}

In this example, strcoll() uses the locale-based collation rules for Spanish (es_ES.UTF-8), which may handle accented characters differently than English or other languages. This ensures sorting results make sense for Spanish speakers.

✍️
Note

If a given locale is not installed on your system, setlocale() will return NULL. You may need to install or generate the locale data (e.g., via locale-gen in Linux).

6. Conclusion

The C library’s locale.h header is vital for writing applications that adapt to various language and regional conventions. By leveraging functions like setlocale() and localeconv(), along with category macros (e.g., LC_TIME, LC_MONETARY), you can seamlessly localize numeric, monetary, time, and string collation operations. This flexibility ensures that your C programs are not only correct but also user-friendly across diverse locales and cultural norms.

feature-top
Readers’ comment
feature-top
Log in to add a comment
🔐 Access