Localizing a PyQt app on MacOS OSX

Brief notes, possibly wrong, from my experience:

Your app must negotiate with OSX using QLocale.uiLanguages, to decide what translators to install.  You have translated your app’s localizable strings to a set of languages (using Qt tools for i18n.)  OSX has a prioritized list of the languages the user prefers (from System Preferences>Languages.)  Your app must find the best fit.  Your app using translators for the language given by QLocale.system().name() is not usually the right thing to do.

In Info.plist, the key CFBundleLocalizations, which appears as key “Localizations” in the Xcode GUI.  I don’t think this has any effect on how OSX treats your app.  I think it is just used in marketing, that is the stores use this to decide whether your app should be in a store in a certain country.

In OSX, a terminal always shows environment variable LANG equal to ‘en’ or similar, that is, English.  Unless you change your environment variables, or the settings of the terminal.  That is, this is not affected by the user reprioritizing languages using System Preferences>Languages.

The file locversion.plist does not seem to be necessary anymore (on OSX 10.9 and Qt5) to get a Qt app to be localized (despite what certain Qt documentation says.)

OSX knows what translations your Qt app supports only by the set of xx.lproj folders in the Resources folder of your app bundle.  For a Qt app, these xx.lproj folders will contain little if anything, just a placeholder, say a single Localizable.string, which really is not used by your app.  By “OSX knows” I mean: OSX translates native dialog strings according to.  Much of your GUI is from Qt, and is translated by Qt, but Qt displays some native dialogs (provided by OSX) and these are localized by OSX according to the presence of the xx.lproj files.

A localization has these components:

  • native dialogs (localized by platform, but depends on your bundling/packaging) e.g. “Save File” dialog
  • Qt dialogs (localized by Qt project, e.g. qt_xx.qm) e.g. standard button “Cancel”
  • your GUI face (localized by your projects .ts files)

In a PyQt projects, localizable resources will be in a myApp_rc.py file.  Your app won’t contain any localizable resources that OSX understands or needs, i.e. in myApp.app/Contents/Resources.

To test, use System Preferences>Languages.  Move a language to the top of the list and close the dialog.  Don’t restart your computer (that just means that Finder, and other apps already running, may not use the chosen language until you do restart, or unless the apps are designed to change language dynamically.)  Start your app.  All the components listed above should appear in the chosen language.

 

 

Using Qt Linguist Phrasebooks

Phrasebooks in Linguist offer suggestions, and makes your job of translating much easier.

About Linguist phrasebooks

Your app will use many phrases that others in the Qt community have already translated.  But your app uses the phrases in a different context than the phrases were originally translated.  This usually is irrelevant, and your session with Linguist then becomes mostly verifying that the existing translation is appropriate for your context.  In other words, Linguist suggests a translation (from a phrasebook) to you for your context, and you OK it for use in your context.  You just click on the suggestion.  You might not need to think much about the meaning of the translation, or its grammar.

A Linguist translation file comprises translations in context.  A Linguist phrasebook comprises translations without context.

Obtaining phrasebooks

Phrasebooks are files with the .qph suffix.

You can often find them at /usr/share/qt5/phrasebooks.   (I suppose installing Qt installs them.)

You can also use other translation files  as a phrasebook, after converting them.

Converting a .ts to a phrasebook

You can convert a translations files ( a .ts file) to a phrasebook.

You need the lconvert tool distributed with Qt.

lconvert -of qph -i  <foo_xx>.ts -o <foo_xx>.qph

This says:

  • -i:  convert the named input file
  • -o: to the named output file
  • -of: outputting the format qph.

Where you substitute the name of an app for foo, and the name of a language for xx, before submitting the command.  For example ‘qt_ja.ts’ for the Qt app (library), and the Japanese language.

Qt .ts files are a very good to convert to phrasebooks.  The Qt library uses many phrases that your app also probably uses, although in a different context.

All the files are XML files.  In a translations files, the translations are nested in contexts.  The conversion just removes the nesting within contexts.  Leaving just a sequence of translations.

Opening multiple phrasebooks

You can open many phrasebooks at the same time.  For example, you can open both:

  • /usr/share/qt5/japanese.qph
  • qt_ja.qph (that you converted from qt_ja.ts)

Opening multiple translations files

Other translation files (not phrasebooks) are NOT a source of suggestions for translations (only translations in other languages that you might understand better).

You can open many translation files simultaneously in Linguist.  But they must all be for the same app.  For example:

  • qt_ja.ts
  • qt_kr.ts

You can’t for example concurrently open:

  • myApp_ja.ts
  • qt_ja.ts

because qt and myApp are different applications.

When you open many translation files, the result is that you see together (in the Linguist GUI) two foreign phrases for a single (context/sourcephrase.)  In other words, the view is of the two translation files merged, sorted first by context, then by source phrase.

Nested translation files

When converting community translation files, you might find some that are nested.  In other words, one just includes others.   For example qt_de.ts includes several other files, such as qtbase_de.ts.

You might only need to convert qtbase_de.ts or other included files.  (I didn’t try to convert the top level file.)

Creating personal phrasebooks

You might want to do that if you translate many related apps using a common set of specialized phrases.  But community phrasebooks, full of common computer terms, will probably be more useful.

Contributing translations back to the community

I don’t know how you would contribute your translations back to the community.  I don’t know who maintains /usr/share/qt5/phrasebooks nor why it doesn’t already include translations from qt_xx.ts.

Other sources of phrasebooks

Several large users of Qt:

  • KDE
  •  Ubuntu

I don’t know whether they have their own translations and phrasebooks.

Licensing

I suppose that if you can find a copy of a phrasebook, you can use it without worrying about licensing?  Although you are copying a translation, you are copying just a few words at a time and putting them in another context, creating a new work.

 

 

 

 

File chosen via Qt OSX dialog not writeable() but writes() in sandbox!

This is strangeness I encountered while porting my Python Qt app to OSX, sandboxed.

After getting a filename using a QFileDialog, my code was preflighting: checking whether the file was writeable, before actually trying to write it.  I checked using QFileInfo().  My check said the file was NOT writeable.

Then I changed the code to try the write, and only check whether the file was writeable as part of error reporting when an exception was thrown by the write.  Strangely, the write succeeds.

A filename obtained using QFileDialog should be sandbox safe, since it uses a native dialog, the native dialog is part of the sandbox mechanism, and since the user has chosen the file the sandbox mechanism should let you write the file.  (Sidenote: Qt docs and other bug reports explain that it only works if you provide a default filename to QFileDialog.)

I think my check for writeable is flawed: directory is writeable OR (file exists AND file is writeable.) In this case, the user does not have permission to write the directory (Documents) and the file does not exist yet.  Now I am wondering if there is a way to preflight file creation properly.  Sandboxing complicates it.