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.
的自定义错误
我发现模块 speaklater 有 make_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 对象 _
。
我在 python 3.5 上有一个使用 tornado 4.5.1 的应用程序,我遇到了 wForms 中给出的自定义错误的麻烦,我做了一个研究,wForms 说必须使用 lazy_gettex为了能够翻译传递给 wForm Class.
的自定义错误我发现模块 speaklater 有 make_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 对象 _
。