Use Perl Locales Safely with use locale and setlocale
You will configure a Perl program to read the process locale, enable locale-sensitive operations only where they are needed, and restore a previous setting after a temporary change. The examples match Perl 5.38.2 from the installed perl-doc package. Allow about 15 minutes. You need a shell, Perl, and at least one installed locale.
The route
Jump straight to the step you need, or tick off Done means at the end.
A locale affects categories such as character types, collation, numeric formatting, dates, messages and monetary data. Perl does not make most of its own operations locale-sensitive merely because the environment contains LC_ALL or LANG. The use locale pragma opts the relevant operations in, while POSIX::setlocale changes the underlying current locale.
1. Check Perl and the available locales
Start by checking that this Perl was built with locale support and that the locale name you plan to use actually exists:
$ perl -V:d_setlocale
d_setlocale='define';
$ locale -a
C
C.utf8
POSIX
en_US.utf8
Your list will differ. Locale names are supplied by the operating system, so do not copy en_US.utf8 into a deployment script unless locale -a shows it on the target host. There is no need for elevated privileges for these read-only checks.
Checkpoint: if d_setlocale is not define, or the required locale is absent, stop here and fix the host's locale installation or use a locale that is present. Setting PERL_BADLANG can hide a startup warning, but it does not repair locale support.
2. Inspect the startup locale
Perl initialises its underlying locale from the standard environment. LC_ALL overrides category-specific variables, and category variables override LANG. Query the result through the POSIX interface:
$ LC_ALL=C perl -MPOSIX=locale_h,setlocale -e 'print setlocale(LC_ALL), "\n"'
C
$ LC_ALL=en_US.utf8 perl -MPOSIX=locale_h,setlocale -e 'print setlocale(LC_ALL), "\n"'
en_US.utf8
This is the process's starting point, not a permanent system change. The environment assignment applies only to the command on that line. In a service, set locale variables in the service's environment deliberately and record the required locale as part of the deployment.
3. Opt into locale-sensitive Perl operations
Put use locale around the code that should follow the current locale. Its effect is lexical: it ends at the enclosing block, file or other scope. The broad form enables the documented locale-sensitive areas, including collation for lt, cmp and default sort, character classification and case conversion, and locale-aware numeric stringification.
use strict;
use warnings;
use locale;
my @names = sort qw(Zed alpha beta);
print join(", ", @names), "\n";
Use eq and ne when you want a character-by-character equality test. They are not changed by the locale. The ordering operators and default sorting are different: they use LC_COLLATE when the relevant locale scope is active.
For tighter control, enable only the categories you need:
use locale qw(:ctype :numeric);
my $display = sprintf "%.2f", 12.5;
my $upper = uc "example";
Available category selectors include :collate, :ctype, :messages, :monetary, :numeric, :time and the :characters pseudo-category. Perl currently does not use LC_MONETARY directly, so enabling :monetary alone does not add currency formatting.
Checkpoint: keep locale scope close to the operation it governs. A regular expression compiled as qr// inside a locale scope can retain locale-dependent matching behaviour when it is used later. That is easy to miss during a refactor.
4. Change and restore one category explicitly
Use POSIX::setlocale when a program must switch the current locale at run time. Save the old value, check every change, and restore it:
use strict;
use warnings;
use POSIX qw(locale_h setlocale);
use locale;
my $old = setlocale(LC_ALL);
die "cannot read current locale\n" unless defined $old;
my $selected = setlocale(LC_ALL, "en_US.utf8");
die "cannot select en_US.utf8\n" unless defined $selected;
print "using $selected\n";
my $reset = setlocale(LC_ALL, $old);
die "cannot restore $old\n" unless defined $reset;
On this machine, the same operation shows the expected values:
$ LC_ALL=C perl /tmp/locale-check.pl
using en_US.utf8
The first argument selects a category. Use LC_CTYPE for character classes, LC_COLLATE for ordering, or another specific category when changing everything would be too broad. With no second argument, setlocale queries the category. With an empty second argument, it asks the C library to use the environment-defined default. A failed selection returns undef and leaves the current locale unchanged.
5. Test a missing locale as an error
Do not assume a locale name is portable. This small check demonstrates the failure path without changing the process permanently:
$ LC_ALL=C perl -MPOSIX=locale_h,setlocale -e 'use locale; my $r = setlocale(LC_ALL, "does_NOT_EXIST"); print defined($r) ? "selected=$r\n" : "selection failed\n"; print "current=", setlocale(LC_ALL), "\n"'
selection failed
current=C
In production, report the missing locale and exit or use an explicitly documented fallback. Do not turn off the warning with PERL_BADLANG=0 just to make logs quiet. The manual describes that variable as a way to suppress the message, not a fix for a mistyped name or broken system support.
6. Respect thread and Unicode boundaries
Perl 5.28 and later can use thread-safe locale operations when the build and platform provide them. Check the read-only ${^SAFE_LOCALES} variable in a threaded program before relying on per-thread locale changes:
use strict;
use warnings;
printf "safe locales: %s\n", ${^SAFE_LOCALES} ? "yes" : "no";
Perl 5.38.2 supports the feature, but the result still depends on how it was built and on the operating system. On a build where safe locale operations are unavailable, do not call setlocale or use locale-sensitive code concurrently from multiple threads. On supported threaded builds, newly created threads start with LC_ALL set to C.
Locales are not a replacement for Unicode handling or for the richer data in CLDR. Perl supports UTF-8 locales, but the installed locale definitions and the platform's C library still matter. Keep external encoding decisions separate from locale-sensitive sorting, classification and presentation.
Done means
perl -V:d_setlocalereportsdefine, and the target locale appears inlocale -a.- The program's startup locale is deliberate and can be queried with
POSIX::setlocale. use localeis scoped around the operations that need it, with category selectors where useful.- Every
setlocalechange checks forundefand restores the previous value when appropriate. - Missing locales, thread safety and Unicode boundaries are treated as deployment concerns, not hidden by environment variables.