首页 文章

python gettext:在_()中指定语言环境

提问于
浏览
2

我正在寻找一种在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

3 回答

  • 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()
    

    Output:

    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

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

    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
    

    当我尝试使用线程时,我只有一个英文翻译 . 您可以在每个进程中创建单独的线程来处理连接 . 您可能不希望为每个连接创建新进程 .

  • 1

    我花了一点时间来编写一个使用系统上所有可用语言环境的脚本,并尝试在其中打印一条众所周知的消息 . 请注意,"all locales"仅包括编码更改,但无论如何都会被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()
    

相关问题