Ch26.6: Locale
1. What the Locale Layer Is
The locale layer answers questions that are not part of a value: which character is the
thousands separator, how digits are grouped, what a date format string expands to, what
the currency symbol is, and which weekday names the locale uses. fast_io keeps that data
in a separate module, fast_io.i18n, because most programs do not need it and
because locale data is large.
The layer is not a global stream state. A locale is a pointer to an immutable image; you
pass it explicitly to imbue or lc_concat. Two threads can print
through two different locales at the same time.
2. The Locale Image
l10n::lc_locale is a flat, position-independent image. It contains offsets,
not absolute pointers:
lc_rva<T>— a relative offset that resolves to a pointer when combined with the image base.lc_scatter<T>— a relative offset plus a length, resolving to abasic_io_scatter_t.lc_get_rva,lc_get_scatter,lc_get_all— the accessors that resolve those offsets.
This is why the same bytes work from a file mapping, an embedded array, a WebAssembly
module or a resource. There is no relocation step and no pointer fixup. The image has a
magic value and a version; lc_verify checks the whole image once before it is
published.
lc_locale carries one section per character width:
all (the declared codeset, usually UTF-8 or GB18030), u8all,
u16all and u32all. lc_get_all<char_type>
selects the section that matches the output character type.
3. Loading a Locale
l10n::load_l10n(name, flags) maps a locale blob, verifies it and returns an
lc_locale const *. The name is a UTF-8 string. An empty name resolves the
operating system's current locale.
auto const *loc{::fast_io::l10n::load_l10n(u8"de_DE.UTF-8")};
The loader keeps a process-wide cache: a thread-local map first, then a global map under a mutex. Mappings live forever, so a reload is a pointer lookup rather than a remap. The loader opens files relative to a single process-lifetime directory handle and does not concatenate paths by hand.
l10n_load_flags::ignore_system_settings tells the loader not to consult the
OS user-locale data when resolving an empty name. On Windows this avoids reading the
HKCU\Control Panel\International registry value; the normal OS lookup still
resolves the empty name. On POSIX, FAST_IO_L10N_LANG, LC_ALL,
LANG and FAST_IO_L10N_PATH remain the normal mechanism.
Sandboxed callers do not have to use the loader. They can pass a pointer to an
lc_locale image they already mapped or embedded. That is the freestanding
path, and it is why the locale pointer type — not a loader object — is the API.
4. lc_ctx and imbue
lc_ctx<char_type> is the runtime view used by the formatting hooks. It
holds the locale pointer, the selected section, and the decoded numeric hot fields
(separator and grouping). lc_load_ctx<char_type>(loc) builds it.
imbue(loc, out) wraps an output stream. The context is built lazily, only
when an argument actually needs locale formatting:
import fast_io.i18n;
int main()
{
using namespace ::fast_io::io;
auto const *loc{::fast_io::l10n::load_l10n(u8"de_DE.UTF-8")};
println(::fast_io::imbue(loc, ::fast_io::out()), 1234567);
}
This prints the number with the locale's grouping and separator. An argument that has no locale-sensitive representation takes the ordinary print path; the wrapper adds no formatting cost to it. A locale with no grouping never builds the context at all.
5. Locale Categories
| Section | What it carries |
|---|---|
| identification | Locale name, encoding, title and related metadata. |
| numeric | Decimal point, thousands separator, grouping, positive/negative signs. |
| monetary | Currency symbol, international symbol, decimal and grouping rules. |
| time | d_t_fmt, d_fmt, t_fmt,
t_fmt_ampm, date_fmt, era formats, weekday and month
names. |
| messages | Yes/no expressions and message catalogs. |
| paper, telephone, name, address, measurement, keyboard | The remaining POSIX locale sections, carried in the same image. |
lc_time_flag selects a time format inside the time section:
d_t_fmt, d_fmt, t_fmt,
t_fmt_ampm, date_fmt, era_d_t_fmt,
era_d_fmt and era_t_fmt. The
mnp::d_t_fmt, mnp::d_fmt, mnp::t_fmt and related
time manipulators are header-side helpers today; the module exports
lc_dump but not the time manipulators yet.
6. Locale-Aware Concatenation
lc_concat formats arguments into a new string using the locale, without
wrapping a stream:
auto s{::fast_io::lc_concat(loc, "total: ", 1234567, " items")};
lc_concatln appends a newline. basic_lc_concat lets you choose
the destination string type; basic_lc_general_concat is the general form.
Arguments with no locale-sensitive representation go through the plain print path, so the
function does not impose locale formatting on strings or characters.
7. Dumping a Locale
mnp::lc_dump(loc) prints the whole locale in a textual, sectioned form. It
is a debugging and inspection tool, not a serialization format for reloading:
println(::fast_io::imbue(loc, ::fast_io::out()), ::fast_io::mnp::lc_dump(loc));
The dump is produced from the wrapped locale's own image, so the locale being dumped does
not have to be the locale currently imbued on the stream. The raw
lc_locale const * is deliberately not printable; wrapping it in
lc_dump is the explicit request.
8. Module and Header Boundary
fast_io.i18n exports fast_io and the locale core:
l10n::lc_locale, the wire types, load_l10n,
lc_ctx, lc_load_ctx, lc_imbuer,
imbue, lc_concat, lc_concatln and
mnp::lc_dump. The loader is compiled into the module, so importing it does
not require a separate fast_io_i18n library.
The header interface is #include <fast_io_i18n.h>. It exposes the same
core plus the header-side time manipulators and any helpers not yet in
i18n.inc. If a locale helper is missing from the module, use the header until
the export list grows.
Key Takeaways
- A locale is an immutable, position-independent image, addressed by
lc_locale const *. load_l10nmaps and verifies a blob, then caches the pointer; sandboxes can pass an image directly.imbue(loc, out)is the explicit, per-stream binding; there is no global locale state.lc_ctxis built lazily and carries the decoded numeric hot fields.lc_concatproduces locale-formatted strings;mnp::lc_dumpinspects a locale.- The module exports the locale core and
lc_dump; time manipulators and some helpers remain header-side. - An empty locale name means the OS locale;
ignore_system_settingssuppresses user-locale lookup on Windows.