python gettext:在 _() 中指定区域设置

Way*_*oor 7 python multithreading gettext internationalization python-3.x

我正在寻找一种在请求 gettext 中的字符串翻译时动态设置语言的方法。我会解释为什么:

我有一个多线程机器人,它在多个服务器上通过文本响应用户,因此需要用不同的语言回复。文档,要在运行时更改区域设置,您应该执行以下操作:

import gettext # first, import gettext

lang1 = gettext.translation('myapplication', languages=['en']) # Load every translations
lang2 = gettext.translation('myapplication', languages=['fr'])
lang3 = gettext.translation('myapplication', languages=['de'])

# start by using language1
lang1.install()

# ... time goes by, user selects language 2
lang2.install()

# ... more time goes by, user selects language 3
lang3.install()
Run Code Online (Sandbox Code Playgroud)

但是,这不适用于我的情况,因为机器人是多线程的:

想象一下以下 2 个片段同时运行:

import time
import gettext 
lang1 = gettext.translation('myapplication', languages=['fr'])
lang1.install()
message(_("Loading a dummy task")) # This should be in french, and it will
time.sleep(10)
message(_("Finished loading")) # This should be in french too, but it wont :'(
Run Code Online (Sandbox Code Playgroud)

import time
import gettext 
lang = gettext.translation('myapplication', languages=['en'])
time.sleep(3) # Not requested on the same time
lang.install()
message(_("Loading a dummy task")) # This should be in english, and it will
time.sleep(10)
message(_("Finished loading")) # This should be in english too, and it will
Run Code Online (Sandbox Code Playgroud)

您可以看到消息有时会在错误的区域设置中翻译。但是,如果我能做类似的事情_("string", lang="FR"),问题就会消失!

我错过了什么,或者我使用了错误的模块来完成任务......我正在使用 python3

Jen*_*ens 7

虽然上述解决方案似乎有效,但它们不能很好地与_()别名gettext()的传统函数配合使用。但我想保留该功能,因为它\xe2\x80\x99s 用于从源中提取翻译字符串(请参阅文档或例如此博客)。

\n

因为我的模块在多进程和多线程环境中运行,所以使用 application\xe2\x80\x99s 内置命名空间module\xe2\x80\x99s 全局命名空间不会 \xe2\x80\x99s 工作,因为_()会共享资源,并且如果多个线程安装不同的翻译,则会受到竞争条件的影响。

\n

因此,首先我编写了一个简短的辅助函数,它返回一个翻译闭包

\n
import gettext\n\ndef get_translator(lang: str = "en"):\n    trans = gettext.translation("foo", localedir="/path/to/locale", languages=(lang,))\n    return trans.gettext\n
Run Code Online (Sandbox Code Playgroud)\n

然后,在使用翻译字符串的函数中,我将该翻译闭包分配给_,从而使其成为_()函数本地范围内所需的函数,而不会污染全局共享命名空间:

\n
def some_function(...):\n    _ = get_translator()  # Pass whatever language is needed.\n\n    log.info(_("A translated log message!"))\n
Run Code Online (Sandbox Code Playgroud)\n

(额外的奖励点是将get_translator()函数包装到记忆缓存中,以避免多次创建相同的闭包。)

\n


小智 1

以下简单示例展示了如何为每个转换器使用单独的进程:

\n\n
import gettext\nimport multiprocessing\nimport time\n\ndef translation_function(language):\n    try:\n        lang = gettext.translation(\'simple\', localedir=\'locale\', languages=[language])\n        lang.install()\n        while True:\n            print(_("Running translator"), ": %s" % language)\n            time.sleep(1.0)\n    except KeyboardInterrupt:\n        pass\n\nif __name__ == \'__main__\':\n    thread_list = list()\n    try:\n        for lang in [\'en\', \'fr\', \'de\']:\n            t = multiprocessing.Process(target=translation_function, args=(lang,))\n            t.daemon = True\n            t.start()\n            thread_list.append(t)\n        while True:\n            time.sleep(1.0)\n    except KeyboardInterrupt:\n        for t in thread_list:\n            t.join()\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出如下所示:

\n\n
\n运行翻译器 : en\nTraducteur 课程 d\xe2\x80\x99ex\xc3\xa9cution : fr\nLaufenden \xc3\x9cbersetzer : de\n运行翻译器 : en\nTraducteur 课程 d\xe2\x80\x99ex\xc3\xa9cution : fr\n劳芬登 \xc3\x9cbersetzer : de\n
\n\n

当我使用线程尝试此操作时,我只得到了英文翻译。您可以在每个进程中创建单独的线程来处理连接。您可能不想为每个连接创建一个新进程。

\n