我正在寻找一种在gettext中请求翻译字符串时动态设置语言的方法。我将解释原因:
我有一个多线程机器人,可以通过多个服务器上的文本响应用户,因此需要使用不同的语言进行回复。 gettext的documentation表示,要在运行时更改语言环境,您应该执行以下操作:
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()
但是,这不适用于我的情况,因为机器人是多线程的:
想象一下,以下两个片段同时运行:
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 :'(
和
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
您可以看到邮件有时会以错误的语言环境进行翻译。
但是,如果我能做_("string", lang="FR")
之类的事情,问题就会消失!
我错过了什么,或者我使用了错误的模块来完成任务...... 我正在使用python3
答案 0 :(得分:2)
以下示例直接使用translation
,如o11c's answer所示,允许使用线程:
import gettext
import threading
import time
def translation_function(quit_flag, language):
lang = gettext.translation('simple', localedir='locale', languages=[language])
while not quit_flag.is_set():
print(lang.gettext("Running translator"), ": %s" % language)
time.sleep(1.0)
if __name__ == '__main__':
thread_list = list()
quit_flag = threading.Event()
try:
for lang in ['en', 'fr', 'de']:
t = threading.Thread(target=translation_function, args=(quit_flag, lang,))
t.daemon = True
t.start()
thread_list.append(t)
while True:
time.sleep(1.0)
except KeyboardInterrupt:
quit_flag.set()
for t in thread_list:
t.join()
<强>输出:强>
Running translator : en Traducteur en cours d’exécution : fr Laufenden Übersetzer : de Running translator : en Traducteur en cours d’exécution : fr Laufenden Übersetzer : de
如果我对gettext
有更多了解,我会发布这个答案。对于那些真正想继续使用_()
的人,我要留下我以前的答案。
答案 1 :(得分:1)
以下简单示例显示了如何为每个翻译者使用单独的流程:
import gettext
import multiprocessing
import time
def translation_function(language):
try:
lang = gettext.translation('simple', localedir='locale', languages=[language])
lang.install()
while True:
print(_("Running translator"), ": %s" % language)
time.sleep(1.0)
except KeyboardInterrupt:
pass
if __name__ == '__main__':
thread_list = list()
try:
for lang in ['en', 'fr', 'de']:
t = multiprocessing.Process(target=translation_function, args=(lang,))
t.daemon = True
t.start()
thread_list.append(t)
while True:
time.sleep(1.0)
except KeyboardInterrupt:
for t in thread_list:
t.join()
输出如下:
Running translator : en Traducteur en cours d’exécution : fr Laufenden Übersetzer : de Running translator : en Traducteur en cours d’exécution : fr Laufenden Übersetzer : de
当我尝试使用线程时,我只有一个英文翻译。您可以在每个进程中创建单独的线程来处理连接。您可能不希望为每个连接创建新进程。
答案 2 :(得分:1)
我花了一些时间来编写一个使用所有系统上可用的语言环境的脚本,并尝试在其中打印一条众所周知的消息。请注意&#34;所有区域设置&#34;仅包括编码更改,无论如何都被Python否定,并且大量翻译不完整,所以请使用后备。
显然,您还必须对xgettext
(或等效物)的使用进行适当的更改,以便您真实地识别翻译功能。
#!/usr/bin/env python3
import gettext
import os
def all_languages():
rv = []
for lang in os.listdir(gettext._default_localedir):
base = lang.split('_')[0].split('.')[0].split('@')[0]
if 2 <= len(base) <= 3 and all(c.islower() for c in base):
if base != 'all':
rv.append(lang)
rv.sort()
rv.append('C.UTF-8')
rv.append('C')
return rv
class Domain:
def __init__(self, domain):
self._domain = domain
self._translations = {}
def _get_translation(self, lang):
try:
return self._translations[lang]
except KeyError:
# The fact that `fallback=True` is not the default is a serious design flaw.
rv = self._translations[lang] = gettext.translation(self._domain, languages=[lang], fallback=True)
return rv
def get(self, lang, msg):
return self._get_translation(lang).gettext(msg)
def print_messages(domain, msg):
domain = Domain(domain)
for lang in all_languages():
print(lang, ':', domain.get(lang, msg))
def main():
print_messages('libc', 'No such file or directory')
if __name__ == '__main__':
main()
答案 3 :(得分:0)
虽然上述解决方案似乎可行,但它们在别名为gettext()的常规_()
函数中不能很好地发挥作用。但是我想保留该功能,因为它用于从源中提取翻译字符串(请参见docs或例如this blog)。
由于我的模块在多进程和多线程环境中运行,因此using the application’s built-in namespace或a module’s global namespace无法正常工作,因为_()
是共享资源,并且如果存在竞争条件,多个线程会安装不同的翻译。
因此,首先,我编写了一个简短的辅助函数,该函数返回翻译闭包:
import gettext
def get_translator(lang: str = "en"):
trans = gettext.translation("foo", localedir="/path/to/locale", languages=(lang,))
return trans.gettext
然后,在使用翻译后的字符串的函数中,我将该翻译闭包分配给了_
,从而使其在我的函数的本地范围内成为所需的函数_()
,而不会污染全局共享名称空间:
def some_function(...):
_ = get_translator() # Pass whatever language is needed.
log.info(_("A translated log message!"))
(用于将get_translator()
函数包装到memoizing cache中的额外布朗尼点,以避免多次创建相同的闭包。)