You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, Click implements gettext using the classic GNU gettext API. That looks like this:
fromgettextimport_print(_("Hello, world!"))
This API depends on a global state in the gettext module. By calling gettext.textdomain(), the active translation domain is changed for all Python modules that use the classic GNU gettext API.
This side effect is usually desirable, except when your module is imported by another module as a library. So you usually don't want to call gettext.textdomain() without putting it behind some sort of function call. With argparse, this is easy: put it in your main function before you even create the ArgumentParser object. With Click, I'm not sure this is possible:
Your main group/command is decorated with all manner of Click magic and its contents may not actually be run (e.g. --help).
There is no way to pre-hook a group/command with a function call that I am aware of.
So you end up having to call gettext.textdomain() on import of your module containing your Click groups/commands.
We can fix that by switching to the class-based API. Because Click will still need to support the old API as well for backwards compatibility, my proposal looks a little as follows. Create a module click.i18n with the following contents (simplified):
Now, elsewhere in Click, you replace all from gettext import _ with from .i18n import _.
Subsequently, we can create a function install_translations(translations) in i18n.py that replaces the TRANSLATIONS global constant with an instantiated GNUTranslations object. This function would still need to be called before the consumer's main function, but it wouldn't change the gettext global state—it would only change Click's. Which, as far perfectionism goes, is probably tolerable. It would be better still if there was a pre-hook, but this is fine.
Furthermore, the consumer could use different domains for Click's TRANSLATIONS object and their own, allowing them to separate their own translations from Click's, and hypothetically reuse the Click translations in other projects.
In fact, having done this plumbing, Click could even ship its own translation strings, getting rid of duplication efforts of translating the same Click strings. Click's own translations could then be activated using e.g. install_click_translations() without any arguments.
In summary, the problems solved by this:
Importing the Click consumer's module no longer changes the global state of the gettext module.
Users can separate their own module's translations from Click's.
Click would be enabled to provide its own translations to reduce duplication of efforts.
I am not aware of other ways to achieve the above that do not require changes to Click. Adding a pre-hook to groups/commands might partially address the problem.
I am willing to make a PR if this issue is validated.
I wrote a blog post here that provides more context on how I use gettext + Click (+ some other components). It has more context than is necessary to understand this issue.
The text was updated successfully, but these errors were encountered:
Hi lovely Click maintainers,
Currently, Click implements gettext using the classic GNU gettext API. That looks like this:
This API depends on a global state in the gettext module. By calling
gettext.textdomain()
, the active translation domain is changed for all Python modules that use the classic GNU gettext API.This side effect is usually desirable, except when your module is imported by another module as a library. So you usually don't want to call
gettext.textdomain()
without putting it behind some sort of function call. With argparse, this is easy: put it in yourmain
function before you even create theArgumentParser
object. With Click, I'm not sure this is possible:--help
).So you end up having to call
gettext.textdomain()
on import of your module containing your Click groups/commands.We can fix that by switching to the class-based API. Because Click will still need to support the old API as well for backwards compatibility, my proposal looks a little as follows. Create a module
click.i18n
with the following contents (simplified):Now, elsewhere in Click, you replace all
from gettext import _
withfrom .i18n import _
.Subsequently, we can create a function
install_translations(translations)
ini18n.py
that replaces theTRANSLATIONS
global constant with an instantiatedGNUTranslations
object. This function would still need to be called before the consumer'smain
function, but it wouldn't change the gettext global state—it would only change Click's. Which, as far perfectionism goes, is probably tolerable. It would be better still if there was a pre-hook, but this is fine.Furthermore, the consumer could use different domains for Click's
TRANSLATIONS
object and their own, allowing them to separate their own translations from Click's, and hypothetically reuse the Click translations in other projects.In fact, having done this plumbing, Click could even ship its own translation strings, getting rid of duplication efforts of translating the same Click strings. Click's own translations could then be activated using e.g.
install_click_translations()
without any arguments.In summary, the problems solved by this:
I am not aware of other ways to achieve the above that do not require changes to Click. Adding a pre-hook to groups/commands might partially address the problem.
I am willing to make a PR if this issue is validated.
I wrote a blog post here that provides more context on how I use gettext + Click (+ some other components). It has more context than is necessary to understand this issue.
The text was updated successfully, but these errors were encountered: