make_lazy_gettext wForms 获得输出 .lazy_gettext at 0x07808D68>

make_lazy_gettext with wForms getting output .lazy_gettext at 0x07808D68>

我在 python 3.5 上有一个使用 tornado 4.5.1 的应用程序,我遇到了 wForms 中给出的自定义错误的麻烦,我做了一个研究,wForms 说必须使用 lazy_gettex为了能够翻译传递给 wForm Class.

的自定义错误

我发现模块 speaklatermake_lazy_gettext ,但是使用它我遇到了另一个麻烦,相反在前端的错误消息中,我得到这样的 .lazy_gettext at 0x07808D68>

我的代码处理程序:hanlders.py 在处理程序中,我有可用的 self._ gettext 翻译,现在可以使用了

class ChangePassword(ProfileHandler):
    def initialize(self, *args, **kwargs):
        super(ChangePassword, self).initialize(*args, **kwargs)
        self.form = profile_f.ChangePasswordForm(meta=self.meta_locale)

    @authenticated(domain='c')
    @coroutine
    def get(self):
        """::access::
        <div class="___sort">Profile</div>
        <div>
            <font class="__get__">GET</font>
            <i class="icon-arrow-right14"></i>
            <font class="desc">Change password</font>
        </div>
        ::
        ::auto_access::1::
        """
        response = yield self.model.item(_id=self.current_user)
        profile = response.get('user', {})
        response = yield self.quiz_m.get_all_quizzes(self.current_user)
        quizzes = response.get('quizzes', {})
        render_vars = {
            'form': self.form,
            'profile': profile,
            'quizzes': quizzes,
        }
        self.render('pages/profile/profile_edit_password.html', **render_vars)

    @authenticated(domain='c')
    @coroutine
    def post(self):
        """::access::
        <div class="___sort">Profile</div>
        <div>
            <font class="__post__">POST</font>
            <i class="icon-arrow-right14"></i>
            <font class="desc">Change password</font>
        </div>
        ::
        ::auto_access::1::
        """
        self.form.process(self.request.arguments)
        if self.form.validate():
            form_data = self.form.prepared_data
            update_response = yield self.model.update_password(
                self.current_user, new_password=form_data['new_password'], old_password=form_data['old_password']) # noqa
            if update_response['success']:
                # start send email
                user = yield self.model.mongo_find_one('users', ObjectId(self.current_user))
                render_vars = {
                    'user': user,
                    'new_password': form_data['new_password'],
                }
                yield emails_template.send_template(self, 'profile_password', render_vars, user['email'])
                # end send email
                self.redirect('/profile/')
                return
            else:
                self.form.set_foreign_errors({'old_password': update_response['errors']})
        response = yield self.model.item(_id=self.current_user)
        profile = response.get('user', {})
        response = yield self.quiz_m.get_all_quizzes(self.current_user)
        quizzes = response.get('quizzes', {})
        render_vars = {
            'form': self.form,
            'profile': profile,
            'quizzes': quizzes,
        }
        self.render('pages/profile/profile_edit_password.html', **render_vars)

我的代码如下:forms.py

from copy import copy
from forms import BaseForm, vl, fl, CustomStringField
from forms import EmailValidator

from speaklater import make_lazy_gettext as _


class ChangePasswordForm(BaseForm):
    old_password = fl.PasswordField(validators=[vl.DataRequired(), vl.length(max=32, min=6)])
    new_password = fl.PasswordField(validators=[vl.DataRequired(), vl.length(max=32, min=6)])
    new_repassword = fl.PasswordField(validators=[vl.DataRequired(), vl.length(max=32, min=6)])

    def validate_new_repassword(self, field):
        if self.data.get('new_password') != field.data:
            raise vl.ValidationError(_('Incorrect retyped password'))

根据上面的代码,我的自定义错误消息是 _('Incorrect retyped password') 并且前端输出我得到这个 .lazy_gettext at 0x07808D68>

为什么会发生这种情况有什么建议吗?

应用程序中使用的简单 gettext 函数

def load_gettext_translations(dir_dom):
    # Todo: think about workaround
    """
    Like standard tornado.locale.load_gettext_translations but loads more locales from different locations
    :param dir_dom: {'directory': '', 'domain': '', 'prefix': ''}
    """
    import gettext
    # global _translations
    # global _supported_locales
    # global _use_gettext
    # _translations = {}
    for trans in dir_dom:
        for lang in os.listdir(trans['directory']):
            if lang.startswith('.'):
                continue  # skip .svn, etc
            if os.path.isfile(os.path.join(trans['directory'], lang)):
                continue
            try:
                os.stat(os.path.join(trans['directory'], lang, "LC_MESSAGES", trans['domain'] + ".mo"))
                tornado.locale._translations['%s%s' % (trans.get('prefix', ''), lang)] = gettext.translation(
                    trans['domain'], trans['directory'],
                    languages=[lang])
            except Exception as e:
                gen_log.error("Cannot load translation for '%s': %s", lang, str(e))
                continue
    tornado.locale._supported_locales = frozenset(list(tornado.locale._translations.keys()) + [tornado.locale._default_locale])
    tornado.locale._use_gettext = True
    gen_log.debug("Supported locales: %s", sorted(tornado.locale._supported_locales))

更新: 我创建了一个 github 存储库作为此答案的演示。可以在这里找到:https://github.com/bhch/tornado-localization.


你用错了。从 speaklater docs make_lazy_gettext,顾名思义,创建一个惰性 gettext 对象。它不是 return 字符串对象,而是 return 函数。所以,当你在做的时候:

msg = _('Message')

msg is in fact a function, not a lazy string

正确的用法是这样的:

from speaklater import make_lazy_gettext


def translate(message):
    """Function responsible for translating message"""

    return message # return message for the sake of example


_ = make_lazy_gettext(lambda: translate)

msg = _('Hello')

print(msg)
# outputs Hello

translate函数负责翻译。我使用了一个简单的函数来演示 speaklater 的使用。但在实际应用程序中,它必须能够根据客户端的语言翻译给定的字符串。


翻译的工作原理:

您似乎认为使用 _gettext 会自动将某些消息翻译成任何语言。

没有。这不是翻译的工作方式。

您必须手动为所有要翻译的消息提供翻译。

在上面示例中的 translate() 函数中,您可以通过创建字典来为您的消息实现翻译:

# spanish
{
  'es_ES': {'Incorrect retyped password': 'Contraseña reescrita incorrecta'}
  # do this for other languages you want to support
}

并在 translate() 函数中编写适当的代码,该函数将根据语言(区域设置)和 return 翻译后的消息在该词典中查找翻译。


如果你想为多种语言提供支持,编写字典可能会很累而且不切实际。为此,您可以查看 tornado-babel library to make things easier for you. Tornado also has support for locale。但这比使用 tornado-babel.

要多一些工作

示例 Tornado 应用程序

我应该指出的一件事是您不能使用全局 _ 对象,因为 Tornado 是单线程的。您需要在处理程序中创建 _ 对象,以便每个用户的语言环境保持不同。

from tornado import web, locale
from speaklater import make_lazy_gettext

class MyHandler(web.RequestHandler):
    def get(self):
        # tornado handlers have a `self.locale` attribute
        # which returns the current user's locale
        _ = make_lazy_gettext(lambda: self.locale.translate)

        hello = _('hello')
        # do something else


if __name__ == '__main__':
    # load the translation files
    locale.load_gettext_translations('/path/to/locale/', 'domain')
    # ...

您还需要修改 ChangePasswordForm__init__ 函数以接受 gettext 对象 _