使用 gettext 和 jinja2 以及金字塔翻译 %%

2024-03-18

使用 Jinja2 和 Pyramid 与 Python 进行 i18n 工作。似乎不知道如何翻译%%。 我开始怀疑这个 bug 存在于 Jinja2 中。


所以我做了更多的调查,看来问题更多的是 gettext 而不是 jinja2,如 repl 所示

>>>gettext.gettext("98%% off %s sale") % ('holiday')
'98% off holiday sale'
>>>gettext.gettext("98%% off sale")
'98%% off sale'

>>>gettext.gettext("98% off %s sale") % ('holiday')
Traceback (most recent call last):
  Python Shell, prompt 13, line 1
TypeError: %o format: a number is required, not str

这似乎是一个先有鸡还是先有蛋的问题。

  • 如果 gettext 翻译 %% -> % 则格式化程序会在参数替换期间对其进行处理。
  • 如果 gettext 不翻译 %% -> % 那么当未调用格式化程序(没有要插入的参数)时 %% 会泄漏。

所有这些意味着翻译人员(其中大多数不是计算机程序员)必须非常小心地进行翻译,每个人都需要非常小心包含 % 的翻译。

似乎我们(不知何故)做错了,应该有一个更简单和统一的格式来做到这一点。现在我们通过简单地注入 % 作为格式参数来应对。

有没有更好的方法来做到这一点,或者这已经是最好的方法了吗?


底部有一个.po文件

单元测试几乎说明了一切,为什么最后一个断言失败了?这是 Jinja2 的错误吗,还是我需要以不同的方式处理这个问题。

class Jinja2Tests(TestCase):

    def test_percent_percent(self):
        """ i18n(gettext) expresses  98% as 98%% only in some versions of jinja2 that has not
            worked as expected.  This is to make sure that it is working. """
        env = Environment(extensions=['jinja2.ext.i18n'])
        lang = gettext.translation('messages', path.abspath(path.join(path.dirname(__file__), 'data')))
        env.install_gettext_translations(lang)

        template = env.from_string(source="{{ _('98%% off %(name)s sale') | format(name='holiday') }}")
        result = template.render()
        self.assertEqual('98% off holiday sale(translated)', result)

        template = env.from_string(source="{{ _('98%% off sale') }}")
        result = template.render()

        # THE LINE BELOW FAILS WITH:
        # AssertionError: '98% off sale(translated)' != u'98%% off sale(translated)'
        self.assertEqual('98% off sale(translated)', result)

并且您必须将 MO 文件编译为 PO 文件才能运行上述代码。

# This file is distributed under the same license as the Uniregistrar project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: Uniregistrar 1.0\n"
"Report-Msgid-Bugs-To: [email protected] /cdn-cgi/l/email-protection\n"
"POT-Creation-Date: 2016-12-22 15:22-0500\n"
"PO-Revision-Date: 2016-11-14 16:42-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
"Language-Team: en <[email protected] /cdn-cgi/l/email-protection>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.3\n"

#: uniregistrar/constants.py:90
msgid "98%% off sale"
msgstr "98%% off sale(translated)"

#: uniregistrar/constants.py:90
msgid "98%% off %(name)s sale"
msgstr "98%% off %(name)s sale(translated)"

据我了解这个问题,这是您主要关心的问题:

所有这些意味着翻译人员(其中大多数不是计算机程序员)必须非常小心地进行翻译,每个人都需要非常小心包含 % 的翻译。

tl;dr:这就是原因msgfmt有一个选项--check。该选项导致msgfmt检查翻译是否可以安全地通过目标语言的字符串插值工具运行。所有这些问题的根源都是Cprintf()当使用错误的参数调用时,很容易崩溃:

printf("Bonjour, %s!");

The printf()函数是一个可变函数。这%s导致它从堆栈中弹出另一个参数。在上面的示例中,除了字符串文字之外没有其他参数。这意味着插入的字符串%s可以被认为来自任意地址,例如 0。在大多数现代语言中这将是空指针异常。在 C 中,它是一个空指针取消引用,经常被利用来运行任意代码,这很糟糕。

我们假设代码如下所示:

printf(gettext("Hello, world!"));

只要翻译是安全的gettext()出现的内容不包含任何“%”字符。但如果法语翻译翻译“你好,世界!”与“你好,%s!”程序会崩溃。

好吧,如果软件的维护者使用标准的翻译工作流程,它就不会崩溃。在这种情况下,xgettext(在 Python 中,它可能类似于“pybabel extract”)会在.po file:

#: filename.c:1
#, c-format
msgid "Hello, world!"
msgstr ""

将“#, c-format”行读取为“这是一个 printf 格式字符串”!

比如说,法语翻译将其翻译成以下条目:

#: filename.c:1
#, c-format
msgid "Hello, world!"
msgstr "Bonjour, %s!"

如果你运行这个msgfmt whatever.po它将被接受。但这不是推荐的工作流程。你应该运行它msgfmt --check whatever.po。现在你得到一个错误:

messages.po:23: number of format specifications in 'msgid' and 'msgstr' does not match
msgfmt: found 1 fatal error

这是因为对于 GNU gettext 支持的每种语言,都会实现一个格式检查器来准确检查该问题。它确保翻译不会导致运行时问题。

您现在可能会争辩说,恶意翻译器会简单地从文件中删除“c-format”限定符。.po文件。但是您的构建系统应该确保从外部源返回的翻译始终与当前的消息集合并,通常称为YOURPROJECT.pot,然后这样的修改.po文件将被简单地丢弃。

所以,理论上你说的没有道理。在实践中,您可能会拥有一个,因为有很多项目和软件都使用.po文件直接用于运行时翻译。这是一个坏主意,请参阅我的答案 https://stackoverflow.com/questions/53743208/benefits-of-compiling-po-files-to-mo/53819204#53819204类似的问题。

我不知道这在多大程度上适用于你的问题,因为你没有提到如何将字符串提取到你的.pot文件以及如何将其编译为二进制文件.mo文件。上面的解释应该清楚地表明这一点至关重要:提取步骤应该向.po关于所使用的字符串插值方法的文件,以及.po文件到一个.mo文件应启用格式字符串检查。如果你做不到这一点,那么你的构建系统就有缺陷。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 gettext 和 jinja2 以及金字塔翻译 %% 的相关文章

随机推荐