To translate strings in PySide2 apps I need to call QCoreApplication.translate() and specify a context, which are a lot of characters to translate a short string.
ie: QCoreApplication.translate("MyClassName", "Hello")
I tried to do something like this:
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)
This way QCoreApplication.translate()
was called with the right context and in my sources I could have a shorter name, but the strings weren’t picked up and stored in the *.ts files.
Is there a way to at least reduce the amount of boilerplate characters needed to translate Qt software written in python? Maybe by configuring pyside2-lupdate?
Advertisement
Answer
Update
The issue has been fixed in Qt 5.15.4+ and Qt 6+
First of all thanks to both benjamin-forest and CryptoFool, their answers were useful.
I found out that there are actually three distinct issues here:
- Custom functions are supported only by
pylupdate5
from PyQt5,
whilepyside2-lupdate
has no configuration options, unlikelupdate
- Both
pylupdate5
andpyside2-lupdate
have no way to guess the context when using global functions, therefore every string read by them would go to the@default
context - The default implementation of
self.tr()
gets its context at runtime, as stated in the official PyQt documentation
Differences Between PyQt5 and Qt
Qt implements internationalisation support through the
QTranslator
class, and thetranslate()
andtr()
methods. Usuallytr()
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 Qttr()
is actually generated bymoc
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 oftr()
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.
My solution is to declare a private __tr()
method to wrap QCoreApplication.translate()
def __tr(self, txt, disambiguation=None, n=-1): return QCoreApplication.translate("TestWidget", txt, disambiguation, n)
and configure pylupdate5 to look for it when called from 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
This way both pylupdate5
gets the right context when generating the .ts
files and self.__tr()
has always the right context at runtime.