Friday, March 28, 2014

A First Look at Linguist

So, where were we? I've been away nearly a week, visiting wonderful Ames, Iowa. Not by choice, but by the whimsy of the NCAA Selection Committee, which in its wisdom chose to make the Stanford Women's Basketball team not only a number-2 seed, but make them play the first two rounds in Ames, on the campus of the University of Iowa, where the host school, the 4th-seeded Iowa Cyclones, draw 10,000 screaming fans to every home game.

Well, fortunately the Cyclones faded to a zephyr before the defense of the FSU Seminoles, so for the second game the Cardinal played in front of a half-empty and subdued arena, and won comfortably. Meanwhile we dealt with snow flurries and the difficulties of passing time among the limited amusements of Ames and Des Moines. If you'd rather know about that versus Qt Linguist, check the pictures.

A couple posts back I described the process of designing a widget with Qt Designer and how any string in the design could be designated "Translatable", and how that left distinct code in the generated Python of the widget class. With the result that, when the widget initializes itself, every translatable string will pass through the bowels of the QtCore.QCoreApplication.translate method before being assigned to its QLabel, push button, menu item or whatever its use.

The output of that method—usually just written tr() in the Qt documentation, but for an arcane reason having to do with the relationship of Python classes to C++ classes, PyQt5 needs to always call the Core version not the one inherited by every QObject—the method's output is either the original, or a translated string—if there exists a translation for that string for the current Locale.

But that leaves the question, where do translations come from? From work done by a Translator (a human) using Qt Linguist. I pursued the link between the widget code and Linguist a little further.

The bridge is the PyQt5 utility, pylupdate5. Its use is described in the PyQt5 online docs. One must create a minimal Qt project description file, in this case ppqt2.pro. Actually a make file for the Qt Make program, this file lists the relevant source files and the name of the translation file. Here is what I used:

SOURCES = editview_uic.py
TRANSLATIONS = ppqt2.ts

Listing just one source file now; later there would be many on that line.

Then you turn pylupdate5 loose on the .pro file and it fills up ppqt2.ts with a bunch of XML items like this:

    <message>
        <location filename="editview_uic.py" line="153"/>
        <source>Document filename</source>
        <translation type="unfinished"></translation>
    </message>

Now I could launch Qt Linguist from the Qt distribution, and use it to open the ppqt2.ts file. It presents me with a window whose top is like this:

Every string from every widget (just one widget for now) is shown. Click on one and prepare a translation for it in the bottom part of the window.

For some reason, spaces are shown as gray dots in this part of the window. There exist Qt "phrase books" for many languages, and the French one is open in the above image. It is offering "document" as a translation for "document". Fair enough, but I would have thought "document name" would be a common phrase. Apparently not. I typed in nom du document.

Anyway, that's what the Translator person works with. The texts for the given language would be saved back into the ppqt2.ts file. And somehow become available via the Core Translate method at run-time.

I'm not going to worry further about that last step, for now. I can see how translation would be done. I don't mean to actually do any translations (or request anyone to do any) until the whole app is in near-final state. But at least I know how it all works, I've seen it can work on my system, so that's one Unknown that's Known and I can relax about it.

No comments: