Hi Bruno, Thank you very much for the detailed explanation.
Bruno Haible <br...@clisp.org> writes: > Why is this being reported for the LANGUAGE environment variable but not > for the LANG and LC_ALL environment variables? Because for LANG and LC_* > we have an architecture composed of three functionalities: > > (A) environment variables: getenv(), setenv() > > (B) locales: setlocale(), newlocale(), uselocale(). > > (C) gettext() and friends. > > (A) is the bottom-most layer. But it has the limitation that multi-threaded > programs must not call setenv(). > > (B) is a layer that fetches the initial values from (A), and that allows > mutators (setlocale(), uselocale()) in multi-threaded programs. > So that multi-threaded applications can modify the program's locale after > startup, there is the setlocale() function. > So that multi-threaded programs can have a locale per thread, there is a > uselocale() function. > > (C) is an application layer that happens to be in Glibc for convenience > reasons. It is based on the layer (B). > > > Back to the LANGUAGE environment variable. The problem is that here we > have the layers (A) and (C), but (B) is missing. The solution ought to > be to introduce a layer (B) for LANGUAGE. LANGUAGE is not specified by > POSIX and does not perfectly fit into the locale system, therefore I > believe it is best treated separately. > > So, what I imagine is a layer (B) with an API like this: > > /* Returns the language precedence list for the program. */ > const char *get_i18n_language (void); > > /* Sets the language precedence list for the program. > NULL means to use the one inferred from the environment variable. */ > void set_i18n_language (const char *); > > or - if you want to have a language per thread -: > > /* Returns the language precedence list for the current thread. */ > const char *get_i18n_language (void); > > /* Sets the language precedence list for the program. > NULL means to use the one inferred from the environment variable. */ > void set_i18n_language (const char *); > > /* Sets the language precedence list for the current thread. > NULL means to use the one for the program or, if not set, > the one inferred from the environment variable. */ > void set_thread_i18n_language (const char *); > > You can protect the implementation of these functions with locks > (functions/macros gl_rwlock_*). > > > With this approach, > - Multithread program can change the i18n language in a thread-safe > way, without using setenv(). > - The setlocale() code is left alone. The approach makes a lot of sense. I will prepare a patch along those lines. > * It modifies the code of setlocale() for a purpose that is unrelated to > the locale system. > > The fact that glibc's locale/setlocale.c has to increment _nl_msg_cat_cntr > (notification from layer (B) to layer (C)) is already bad enough; it > exists because there is no standardized API for being notified of > locale changes. It forces us to override setlocale on non-glibc systems, > using gnulibology patterns. > > But adding yet another call from layer (B) to layer (C) is even more of a > hack. Yes. The intention of this hack was to save existing programs without modification (for the new API). However, for the majority of programs, it could be mitigated in a general-purpose library like GLib, by calling set_i18n_language() during initialization of the library. Regards, -- Daiki Ueno