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:

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