Load GTK-Glade translations in Windows using Python/PyGObject
Asked Answered
I

1

3

I have a Python script that loads a Glade-GUI that can be translated. Everything works fine under Linux, but I am having a lot of trouble understanding the necessary steps on Windows.

All that seems necessary under Linux is:

import locale
[...]
locale.setlocale(locale.LC_ALL, locale.getlocale())
locale.bindtextdomain(APP_NAME, LOCALE_DIR)
[...]
class SomeClass():
    self.builder = Gtk.Builder()
    self.builder.set_translation_domain(APP_NAME)

locale.getlocale() returns for example ('de_DE', 'UTF-8'), the LOCALE_DIR just points at the folder that has the compiled mo-files.

Under Windows this makes things more difficult:

locale.getlocale() in the Python console returns (None, None) and locale.getdefaultlocale() returns ("de_DE", "cp1252"). Furthermore when one tries to set locale.setlocale(locale.LC_ALL, "de_DE") will spit out this error:

locale.setlocale(locale.LC_ALL, "de_DE")
File "C:\Python34\lib\locale.py", line 592, in setlocale
return _setlocale(category, locale)
locale.Error: unsupported locale setting

I leave it to the reader to speculate why Windows does not accept the most common language codes. So instead one is forced to use one of the below lines:

  • locale.setlocale(locale.LC_ALL, "deu_deu")
  • locale.setlocale(locale.LC_ALL, "german_germany")

Furthermore the locale module on Windows does not have the bintextdomain function. In order to use it one needs to import ctypes:

import ctypes
libintl = ctypes.cdll.LoadLibrary("intl.dll")
libintl.bindtextdomain(APP_NAME, LOCALE_DIR)
libintl.bind_textdomain_codeset(APP_NAME, "UTF-8")

So my questions, apart from how this works, is:

  • Which intl.dll do I need to include? (I tried the gnome/libintl-8.dll from this source: http://sourceforge.net/projects/pygobjectwin32/, (pygi-aio-3.14.0_rev19-setup.exe))
  • How can I check if the e.g. locale deu_deu gets the correct /mo/de/LC_MESSAGES/appname.mo/?

Edit

My folder structure (Is it enough to have a de folder? I tried using a deu_deu folder but that did not help):

├── gnome_preamble.py
├── installer.cfg
├── pygibank
│   ├── __init__.py
│   ├── __main__.py
│   ├── mo
│   │   └── de
│   │       └── LC_MESSAGES
│   │           └── pygibank.mo
│   ├── po
│   │   ├── de.po
│   │   └── pygibank.pot
│   ├── pygibank.py
│   └── ui.glade
└── README.md

Short summary of the answer

The mo-files should go into the gnome-packages in this way:

├── gnome
│   └── share
│       └── locale
│           └── de
|               └── LC_MESSAGES
|                    └── pygibank.mo
Inexecution answered 16/8, 2015 at 16:48 Comment(0)
C
3

You are close. This is a very complicated subject.

As I wrote in Question 10094335 and in Question 3678174:

To setup the locale to user current locale do not call:

locale.setlocale(locale.LC_ALL, locale.getlocale())

Simply call:

locale.setlocale(locale.LC_ALL, '')

As explained in Python setlocale reference documentation.

This sets the locale for all categories to the user’s default setting (typically specified in the LANG environment variable).

Note that Windows doesn't have the LANG environment variable set up, so, you need to do this before that line:

import sys
import os
import locale

if sys.platform.startswith('win'):
    if os.getenv('LANG') is None:
        lang, enc = locale.getdefaultlocale()
        os.environ['LANG'] = lang

This will also make gettext to work for in-Python translations.

How this work you can check it in the source code here:

https://github.com/python/cpython/blob/master/Modules/_localemodule.c#L90

In particular, the error you're getting:

locale.Error: unsupported locale setting

Is expressed here:

https://github.com/python/cpython/blob/master/Modules/_localemodule.c#L112

Which is just a generic error message that the C call setlocale failed with the given parameters.

The C call setlocale is defined in the locale.h header. In Linux, this is:

In Windows, this is the one used:

In Windows locale.h documentation you can read:

The set of language and country/region strings supported by setlocale are listed in Language Strings and Country/Region Strings.

And that points to:

As you can see, for the 2010 version the setlocale function expects the locale in the format you found out: deu_deu, that differ from the one expected by the Linux version de_DE. Your only option is to use a list of os-dependent locales to setup the locale. Very very sad indeed.

There is another issue here. If you change the version of the toolchain you can see that newer version of the setlocale function now work more closelly to what Linux/POSIX does:

american english en-US

Visual Studio 2010 is the last release to support the old format, starting from version 2012 the new locale format is expected.

As you can imagine, the one you need to use depends on the version of the toolchain for which the CPython interpreter you're using was built to. I don't know which version are you using but according to the official Python Developer's Guide:

Python 3.5 and later use Microsoft Visual Studio 2015. [...] Python 3.3 and 3.4 use Microsoft Visual Studio 2010. [...] Most Python versions prior to 3.3 use Microsoft Visual Studio 2008. [...]

That is all related to the Python locale module. Now, for the gettext module or the gettext related functions in the locale module, this is another C library called libintl.

libintl is what is called the C library that is part of gettext for all this translation magic:

One relevant part of this documentation says:

Note that on GNU systems, you don’t need to link with libintl because the gettext library functions are already contained in GNU libc.

But in Windows, because of the issues explained in Question 10094335 you need to load the libintl library that is being used by PyGObject, that is, the very same that it was linked during build. That is done doing the steps you already wrote.

Which intl.dll do I need to include? (I tried the gnome/libintl-8.dll from this source: http://sourceforge.net/projects/pygobjectwin32/, (pygi-aio-3.14.0_rev19-setup.exe))

So, yes. The one that was used to link against when the pygobject AIO was build.

How can I check if the e.g. locale deu_deu gets the correct /mo/de/LC_MESSAGES/appname.mo/

Configure a few messages and note if they show translated. Just a note, is not a folder "/mo/de/LC_MESSAGES/appname.mo/", appname.mo is a file.

Check my first answer to how to create the translation .po file from the Glade file.

Caoutchouc answered 18/8, 2015 at 0:39 Comment(5)
Thank you for the detailed answer. I noticed that I was already working with your example. I am still stuck with the print-statements being translated and the UI-translations failing. I suspect that it is failing at libintl.bindtextdomain, but there is no sensible return value and the errors are probably hidden from the python traceback. I will update my question with some more information.Inexecution
I got some help from the GTK developers and apparently it is a bug. But there is a workaround: Placing the files into the bundled packages into /gnome/share/locale/Inexecution
Very nice indeed you find out! Is there a bug report already? Can you please link to it or to the log of the conversation for details? Thanks!Caoutchouc
I just asked the right Person :) I filed a bug here:bugzilla.gnome.org/show_bug.cgi?id=753991 But the translations still have encoding problems with rev13 of the pygi packages and dont work with rev21 (the newest package). I will also enquire with the pygi-packagers about this.Inexecution
BTW about encoding, there is the bind_textdomain_codeset function in gettext, that might help. I'm starting a new project to workarround all things I've found on this, I'm in particular worried about the setlocale differences between platforms and Python version, if you want to add stuffs: github.com/carlos-jenkins/l10nCaoutchouc

© 2022 - 2024 — McMap. All rights reserved.