[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [bug-gettext] RFC: move LANGUAGE check out of gettext()
From: |
Daiki Ueno |
Subject: |
Re: [bug-gettext] RFC: move LANGUAGE check out of gettext() |
Date: |
Wed, 11 May 2016 17:43:21 +0900 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/25.1.50 (gnu/linux) |
Hi Bruno,
Thank you very much for the detailed explanation.
Bruno Haible <address@hidden> 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