有没有办法避免输入 QCoreApplication.translate("Context",?

Is there a way to avoid typing QCoreApplication.translate("Context",?

要在 PySide2 应用程序中翻译字符串,我需要调用 QCoreApplication.translate() 并指定一个上下文,这是翻译一个短字符串的大量字符。 即:QCoreApplication.translate("MyClassName", "Hello") 我试着做这样的事情:

from PySide2.QtCore import QCoreApplication
from functools import partial


class Translate:
    def __init__(self, context):
        self.context = context
        self.translate = partial(QCoreApplication.translate, self.context)

    def __call__(self, text):
        return self.translate(text)

这样 QCoreApplication.translate() 是在正确的上下文中调用的,在我的源代码中我可以使用更短的名称,但字符串没有被提取并存储在 *.ts 文件中。 有没有办法至少减少翻译用 python 编写的 Qt 软件所需的样板字符数量?也许通过配置 pyside2-lupdate?

如果您使用 类,继承自 QObject 可为您提供一个 self.tr("your text") 方法,该方法将具有适当的上下文。

QCoreApplication.translate() 替换为 QObject().tr("your string") 确实有效,但假设您每次翻译字符串时都创建了一个新的 QObject ...

似乎做一些全局的 tr = QObject().tr 也确实有效(我怀疑这会很快变得棘手)。

我想到的是这个非常简单的函数:

defaultContext = "MyClassName"

def translate(str, context=defaultContext):
    return QCoreApplication.translate(context, str)

您在定义函数时指定默认上下文,也可以随时通过将 defaultContext 设置为不同的值来全局更改它。您可以选择在每次调用时传入不同的上下文,但使用默认值的调用非常简单干净:

translated = translate("Hello")

还是我遗漏了什么?

更新
issue 已在 Qt 5.15.4+ 和 Qt 6+

中修复

首先感谢 benjamin-forestCryptoFool,他们的回答很有用。

我发现这里实际上存在三个不同的问题:

  • 只有 PyQt5 的 pylupdate5 支持自定义函数,
    pyside2-lupdate 没有配置选项,不像 lupdate
  • pylupdate5pyside2-lupdate在使用全局函数时都无法猜测上下文,因此它们读取的每个字符串都会转到@default上下文
  • self.tr() 的默认实现在运行时获取其上下文,如官方 PyQt 文档所述

Differences Between PyQt5 and Qt

Qt implements internationalisation support through the QTranslator class, and the translate() and tr() methods. Usually tr() is used to obtain the correct translation of a message. The translation process uses a message context to allow the same message to be translated differently. In Qt tr() is actually generated by moc and uses the hardcoded class name as the context. On the other hand, translate allows the context to be specified explicitly.

Unfortunately, because of the way Qt implements tr() it is not possible for PyQt5 to exactly reproduce its behaviour. The PyQt5 implementation of tr() uses the class name of the instance as the context. The key difference, and the source of potential problems, is that the context is determined dynamically in PyQt5, but is hardcoded in Qt. In other words, the context of a translation may change depending on an instance’s class hierarchy.

我的解决方案是声明一个私有 __tr() 方法来包装 QCoreApplication.translate()

def __tr(self, txt, disambiguation=None, n=-1):
    return QCoreApplication.translate("TestWidget", txt, disambiguation, n)

并配置 pylupdate5 在从 setup.py:

调用时查找它
if has_build_ui:
    class build_res(build_ui):
        """Build UI, resources and translations."""

        def run(self):
            # build translations
            check_call(["pylupdate5", "-tr-function", "__tr", "app.pro"])

            lrelease = os.environ.get("LRELEASE_BIN")
            if not lrelease:
                lrelease = "lrelease"

            check_call([lrelease, "app.pro"])

            # build UI & resources
            build_ui.run(self)
            # create __init__ file for compiled ui
            open("app/ui/__init__.py", "a").close()

    cmdclass["build_res"] = build_res

这样 pylupdate5 在生成 .ts 文件时获得正确的上下文,并且 self.__tr() 在运行时始终具有正确的上下文。