The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

This is an (almost) drop-in replacement for the system setlocale(3), taking the same parameters, and returning the same information, except that it returns the correct underlying LC_NUMERIC locale. Regular setlocale will instead return C if the underlying locale has a non-dot decimal point character, or a non-empty thousands separator for displaying floating point numbers. This is because perl keeps that locale category such that it has a dot and empty separator, changing the locale briefly during the operations where the underlying one is required. Perl_setlocale knows about this, and compensates; regular setlocale doesn't.

Another reason it isn't completely a drop-in replacement is that it is declared to return const char *, whereas the system setlocale omits the const (presumably because its API was specified long ago, and can't be updated; it is illegal to change the information setlocale returns; doing so leads to segfaults.)

Finally, Perl_setlocale works under all circumstances, whereas plain setlocale can be completely ineffective on some platforms under some configurations.

Perl_setlocale should not be used to change the locale except on systems where the predefined variable ${^SAFE_LOCALES} is 1. On some such systems, the system setlocale() is ineffective, returning the wrong information, and failing to actually change the locale. Perl_setlocale, however works properly in all circumstances.

The return points to a per-thread static buffer, which is overwritten the next time Perl_setlocale is called from the same thread.

This is an (almost) drop-in replacement for the system nl_langinfo(3), taking the same item parameter values, and returning the same information. But it is more thread-safe than regular nl_langinfo(), and hides the quirks of Perl's locale handling from your code, and can be used on systems that lack a native nl_langinfo.

Expanding on these:

  • The reason it isn't quite a drop-in replacement is actually an advantage. The only difference is that it returns const char *, whereas plain nl_langinfo() returns char *, but you are (only by documentation) forbidden to write into the buffer. By declaring this const, the compiler enforces this restriction, so if it is violated, you know at compilation time, rather than getting segfaults at runtime.

  • It delivers the correct results for the RADIXCHAR and THOUSEP items, without you having to write extra code. The reason for the extra code would be because these are from the LC_NUMERIC locale category, which is normally kept set by Perl so that the radix is a dot, and the separator is the empty string, no matter what the underlying locale is supposed to be, and so to get the expected results, you have to temporarily toggle into the underlying locale, and later toggle back. (You could use plain nl_langinfo and "STORE_LC_NUMERIC_FORCE_TO_UNDERLYING" for this but then you wouldn't get the other advantages of Perl_langinfo(); not keeping LC_NUMERIC in the C (or equivalent) locale would break a lot of CPAN, which is expecting the radix (decimal point) character to be a dot.)

  • The system function it replaces can have its static return buffer trashed, not only by a subsequent call to that function, but by a freelocale, setlocale, or other locale change. The returned buffer of this function is not changed until the next call to it, so the buffer is never in a trashed state.

  • Its return buffer is per-thread, so it also is never overwritten by a call to this function from another thread; unlike the function it replaces.

  • But most importantly, it works on systems that don't have nl_langinfo, such as Windows, hence makes your code more portable. Of the fifty-some possible items specified by the POSIX 2008 standard, http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/langinfo.h.html, only one is completely unimplemented, though on non-Windows platforms, another significant one is also not implemented). It uses various techniques to recover the other items, including calling localeconv(3), and strftime(3), both of which are specified in C89, so should be always be available. Later strftime() versions have additional capabilities; "" is returned for those not available on your system.

    It is important to note that when called with an item that is recovered by using localeconv, the buffer from any previous explicit call to localeconv will be overwritten. This means you must save that buffer's contents if you need to access them after a call to this function. (But note that you might not want to be using localeconv() directly anyway, because of issues like the ones listed in the second item of this list (above) for RADIXCHAR and THOUSEP. You can use the methods given in perlcall to call "localeconv" in POSIX and avoid all the issues, but then you have a hash to unpack).

    The details for those items which may deviate from what this emulation returns and what a native nl_langinfo() would return are specified in I18N::Langinfo.

When using Perl_langinfo on systems that don't have a native nl_langinfo(), you must

 #include "perl_langinfo.h"

before the perl.h #include. You can replace your langinfo.h #include with this one. (Doing it this way keeps out the symbols that plain langinfo.h would try to import into the namespace for code that doesn't need it.)

The original impetus for Perl_langinfo() was so that code that needs to find out the current currency symbol, floating point radix character, or digit grouping separator can use, on all systems, the simpler and more thread-friendly nl_langinfo API instead of localeconv(3) which is a pain to make thread-friendly. For other fields returned by localeconv, it is better to use the methods given in perlcall to call POSIX::localeconv(), which is thread-friendly.

On systems without locale support, or on typical single-threaded builds, or on platforms that do not support per-thread locale operations, this function does nothing. On such systems that do have locale support, only a locale global to the whole program is available.

On multi-threaded builds on systems that do have per-thread locale operations, this function converts the thread it is running in to use the global locale. This is for code that has not yet or cannot be updated to handle multi-threaded locale operation. As long as only a single thread is so-converted, everything works fine, as all the other threads continue to ignore the global one, so only this thread looks at it.

However, on Windows systems this isn't quite true prior to Visual Studio 15, at which point Microsoft fixed a bug. A race can occur if you use the following operations on earlier Windows platforms:

POSIX::localeconv
I18N::Langinfo, items CRNCYSTR and THOUSEP
"Perl_langinfo" in perlapi, items CRNCYSTR and THOUSEP

The first item is not fixable (except by upgrading to a later Visual Studio release), but it would be possible to work around the latter two items by using the Windows API functions GetNumberFormat and GetCurrencyFormat; patches welcome.

Without this function call, threads that use the setlocale(3) system function will not work properly, as all the locale-sensitive functions will look at the per-thread locale, and setlocale will have no effect on this thread.

Perl code should convert to either call Perl_setlocale (which is a drop-in for the system setlocale) or use the methods given in perlcall to call POSIX::setlocale. Either one will transparently properly handle all cases of single- vs multi-thread, POSIX 2008-supported or not.

Non-Perl libraries, such as gtk, that call the system setlocale can continue to work if this function is called before transferring control to the library.

Upon return from the code that needs to use the global locale, sync_locale() should be called to restore the safe multi-thread operation.

Perl_setlocale can be used at any time to query or change the locale (though changing the locale is antisocial and dangerous on multi-threaded systems that don't have multi-thread safe locale operations. (See "Multi-threaded operation" in perllocale). Using the system setlocale(3) should be avoided. Nevertheless, certain non-Perl libraries called from XS, such as Gtk do so, and this can't be changed. When the locale is changed by XS code that didn't use Perl_setlocale, Perl needs to be told that the locale has changed. Use this function to do so, before returning to Perl.

The return value is a boolean: TRUE if the global locale at the time of call was in effect; and FALSE if a per-thread locale was in effect. This can be used by the caller that needs to restore things as-they-were to decide whether or not to call Perl_switch_to_global_locale.