With gettext
you can either use the default system-wide locale directory, or specify one yourself using bindtextdomain
. This is useful when running a program directly from source when the compiled .mo translation files are not available in the system's default location.
In Python you would do this:
import gettext
from gettext import gettext as _
gettext.bindtextdomain('nautilus-image-manipulator', '/path/to/mo/folder')
gettext.textdomain('nautilus-image-manipulator')
where /path/to/mo/folder
contains the familiar fr/LC_MESSAGES/nautilus-image-manipulator.mo
structure. Calls like this:
print _("Delete this profile")
return the properly translated string from the local .mo files, thank you very much.
In GTK+2/pygtk, there existed gtk.glade.bindtextdomain
, but I'm wondering if there is any equivalent in GTK+3/PyGObject.
To give you a specific example, this is how Nautilus Image Manipulator;s UI is created from its Glade file:
from gi.repository import Gtk
builder = Gtk.Builder()
builder.set_translation_domain('nautilus-image-manipulator')
builder.add_from_file(ui_filename)
return builder
Parts of the UI that are not built from the Glade file (i.e. set from code) show up properly translated, but the strings from the Glade file still show up in English.
It seems to me that I'm missing a call to some kind of builder.bind_text_domain('nautilus-image-manipulator', '/path/to/mo/folder')
before the call to builder.set_translation_domain
... Any idea how to perform this?
In PyGtk you can use Gtk.Builder too. Accordingly to the PyGtk Gtk.Builder documentation:
http://developer.gnome.org/pygtk/stable/class-gtkbuilder.html#properties-gtkbuilder
The translation domain used when translating property values that have
been marked as translatable in interface descriptions. If the
translation domain is None, GtkBuilder uses gettext(), otherwise
dgettext(). Default value: None
That is, Gtk.Builder uses dgettext() from "C library". The problem is that Python's gettext module, function bindtextdomain(), for some reason unknown to me, don't set the "C library". The option is to use the locale module that also exposes that interface. From the Python locale module documentation:
http://docs.python.org/library/locale#access-to-message-catalogs
The locale module exposes the C library’s gettext interface on systems
that provide this interface. It consists of the functions gettext(),
dgettext(), dcgettext(), textdomain(), bindtextdomain(), and
bind_textdomain_codeset(). These are similar to the same functions in
the gettext module, but use the C library’s binary format for message
catalogs, and the C library’s search algorithms for locating message
catalogs.
Python applications should normally find no need to invoke these
functions, and should use gettext instead. A known exception to this
rule are applications that link with additional C libraries which
internally invoke gettext() or dcgettext(). For these applications, it
may be necessary to bind the text domain, so that the libraries can
properly locate their message catalogs.
Which, is the current case. What a hack :S
This will do it, file test.py:
from gi.repository import Gtk
from os.path import abspath, dirname, join, realpath
import gettext
import locale
APP = 'myapp'
WHERE_AM_I = abspath(dirname(realpath(__file__)))
LOCALE_DIR = join(WHERE_AM_I, 'mo')
locale.setlocale(locale.LC_ALL, '')
locale.bindtextdomain(APP, LOCALE_DIR)
gettext.bindtextdomain(APP, LOCALE_DIR)
gettext.textdomain(APP)
_ = gettext.gettext
print('Using locale directory: {}'.format(LOCALE_DIR))
class MyApp(object):
def __init__(self):
# Build GUI
self.builder = Gtk.Builder()
self.glade_file = join(WHERE_AM_I, 'test.glade')
self.builder.set_translation_domain(APP)
self.builder.add_from_file(self.glade_file)
print(_('File'))
print(_('Edit'))
print(_('Find'))
print(_('View'))
print(_('Document'))
# Get objects
go = self.builder.get_object
self.window = go('window')
# Connect signals
self.builder.connect_signals(self)
# Everything is ready
self.window.show()
def main_quit(self, widget):
Gtk.main_quit()
if __name__ == '__main__':
gui = MyApp()
Gtk.main()
My Glade file test.glade:
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkWindow" id="window">
<property name="can_focus">False</property>
<property name="window_position">center-always</property>
<property name="default_width">400</property>
<signal name="destroy" handler="main_quit" swapped="no"/>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">File</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Edit</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Find</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">View</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Document</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
Remember to create the mo in mo/LANG/LC_MESSAGES/myapp.mo based on .po extracted with:
xgettext --keyword=translatable --sort-output -o en.po test.glade
What it looks like:
Kind regards
The solution to activate gettext translations in Gtk / python under Windows is elib_intl.py
It is easy to find the file with Google.
This allows translation of text in the code and text in a glade UI.
Here is the code used for the following environment :
Windows 7
Python 2.7
Gtk 3+ loaded by : pygi-aio-3.10.2-win32_rev18-setup.exe
It should work in any windows and also for Python 3.
elib_intl.py can be used with pyGtk (Gtk 2).
from gi.repository import Gtk, Gdk
import cairo
import locale #for multilanguage support
import gettext
import elib_intl
elib_intl.install("pdfbooklet", "share/locale")
If you are using Gtk 3, you will probably receive an error : for line 447 :
libintl = cdll.intl
This error indicates : module not found.
The reason is that in Gtk3, the name of the dll has been changed. It is no longer intl.dll. In the described Pygi installation the name is : libintl-8. You must replace the line which provoques the error by :
libintl = cdll.LoadLibrary("libintl-8.dll")
You can find a full working example here : pdfBooklet 2.4.0
(Warning : not yet in line when I write)
thanks to dieter Verfaillie who has written elib_intl
Well after the bounty failing so miserably to attract a mac os x answer, I had to do my own research. Here's the snippet I use:
import locale, ctypes, sys, os
import gettext
# setup textdomain and install _() for strings from python
gettext.install('domain', '/path/to/locale/dir')
try:
if hasattr(locale, 'bindtextdomain'):
libintl = locale
elif os.name == 'nt':
libintl = ctypes.cdll.LoadLibrary('libintl-8.dll')
elif sys.platform == 'darwin':
libintl = ctypes.cdll.LoadLibrary('libintl.dylib')
# setup the textdomain in gettext so Gtk3 can find it
libintl.bindtextdomain('domain', '/path/to/locale/dir')
except (OSError, AttributeError):
# disable translations altogether for consistency
gettext.install('')
Later, when you have the Gtk.Builder, set the domain:
builder.set_translation_domain('domain')
This will only work if gettext's library libintl
is in the library path, otherwise fail gracefully. For transaltions to work, you will need to install gettext as a dependency.