After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 753991 - GUI translations fail when loaded from a custom directory
GUI translations fail when loaded from a custom directory
Status: RESOLVED OBSOLETE
Product: gtk+
Classification: Platform
Component: Backend: Win32
3.22.x
Other Windows
: Normal normal
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2015-08-23 15:25 UTC by Tobias Schönberg
Modified: 2018-05-02 16:42 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
minimal example (2.94 KB, application/x-zip-compressed)
2018-03-10 22:31 UTC, lovetox
Details
translation.py that worked for me (1.18 KB, text/plain)
2018-03-30 23:54 UTC, LRN
Details

Description Tobias Schönberg 2015-08-23 15:25:03 UTC
When trying to load the GUI translations in Python under Windows:

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

    import ctypes
    LIB_INTL = abspath(join(WHERE_AM_I, "../gnome/libintl-8.dll"))
    libintl = ctypes.cdll.LoadLibrary(LIB_INTL)
    lc = locale.setlocale(locale.LC_ALL, "")
    btd = libintl.bindtextdomain(APP, LOCALE_DIR)
    libintl.bind_textdomain_codeset(APP, "UTF-8")

a subsequent call to translate the GUI will fail (silently!):

    builder = Gtk.Builder()
    builder.set_translation_domain(APP)

This is probably a bug in g_dgettext, and Glib.dgettext(APP, "") should have a return value.

A workaround for this bug is to place the mo-files into (for German for example): /gnome/share/locale/de/LC_MESSAGES/myapp.mo

I have an example program here where I will inform about the bug and the workaround: https://github.com/tobias47n9e/pygobject-locale
Comment 1 Tobias Schönberg 2015-08-26 10:54:17 UTC
There is a related discussion here: https://sourceforge.net/p/pygobjectwin32/tickets/22/
Comment 2 Tobias Schönberg 2015-10-24 13:30:53 UTC
Bajusz Tamás found a workaround for the bug:

https://github.com/tobias47n9e/pygobject-locale/issues/1

I uploaded the working example packaged with pynsist here:

https://www.dropbox.com/s/cwmowy8oh6l9n99/pygibank-py3.4.4-rev22-64b.exe?dl=0

One can change between English and German locale and the GUI gets translated correctly. All non-ASCII characters are shown. Commenting out the line self.translate_gui() makes the translation fail again. My conclusion is that there is something going wrong with the translation of the GUI if only set_translation_domain() is used.
Comment 3 Matthias Clasen 2018-02-10 05:20:43 UTC
We're moving to gitlab! As part of this move, we are moving bugs to NEEDINFO if they haven't seen activity in more than a year. If this issue is still important to you and still relevant with GTK+ 3.22 or master, please reopen it and we will migrate it to gitlab.
Comment 4 lovetox 2018-03-10 22:31:37 UTC
Created attachment 369543 [details]
minimal example

Yes this is still a issue

Please tell us how to get translation on Windows working

Glade files are just not translated
Comment 5 Neustradamus 2018-03-30 14:49:09 UTC
This blocked bug is now on gitlab: https://gitlab.gnome.org/GNOME/gtk/issues/150

Hope a quickly good solution, we wait since 2015-08-23 15:25 UTC.

Thanks in advance.
Comment 6 Daniel Boles 2018-03-30 15:06:01 UTC
Please, if a bug already exists on BZ, just set it back to NEW; don't duplicate it on GitLab. (A later mass migration will do that when it's time.)

There's also https://gitlab.gnome.org/GNOME/gtk/issues/128 over there, which is probably the same thing.
Comment 7 Neustradamus 2018-03-30 16:14:13 UTC
Thanks Daniel Boles for your comment, this bug is not NEW but OLD ;)
2015-08-23 15:25 UTC
Hope a quickly solution.
Comment 8 LRN 2018-03-30 17:18:20 UTC
Neustradamus, lovetox, can you tell me what you are trying to achieve? It matters.

I understand that the aim is to have strings correctly translated. The big question is *into which language*, or, more precisely, *who decides which language to translate onto*.

The original poster did
> os.environ['LANG'] = lang
However, Python (where the script executes) and C runtime (which is what libintl uses) operate on different envtables, meaning that any environment change inside Python do not affect the environment that libintl can see with its getenv() calls. That is why you cannot change libintl behaviour by modifying os.environ

However, you *can* modify program behaviour by changing environment variables *before* the program is run (by using a special launcher program for it, or a script in which you modify the environment; or just modifying the global environment of your user session).

Other people who replied here have hit a *different* bug. Specifically, attachment 369543 [details] does not specify the language to use, meaning that it uses the default locale. Default locale is usually correct, but not always (it's a gettext/gnulib bug).

Attachment 369543 [details] also calls locale.setlocale(), not libintl.libintl_setlocale(). These two calls most likely result in different routines being invoked. Specifically, libintl_setlocale() has code that allows it to accept LC_MESSAGES category. locale.setlocale() might or might not do that, but either way it either doesn't accept LC_MESSAGES, or it does, but does not affect the internal LC_MESSAGES value that libintl_setlocale() stores for itself. So you must either use one API or the other, but not both. Or maybe use both to ensure that these two independent sets of routines do the same thing.

There's also a gettext bug where gettext does not call the right function in guess_category_value(), which causes it to ignore previous libintl_setlocale() calls, meaning that you cannot change its behaviour by calling libintl_setlocale().

This is truly a mess. Your last, best hope for translated text is to set LANG or LC_MESSAGES in your session envtable, or your system envtable, or use a launcher that sets one of these variables.
Comment 9 LRN 2018-03-30 17:19:58 UTC
(oh, in case it isn't clear from the context of my previous comment, MS C Runtime setlocale() does not support LC_MESSAGES)
Comment 10 lovetox 2018-03-30 18:32:52 UTC
Hi, i tried to apply libintl_setlocale() but it never fails doesnt matter what you pass it, i also find no documentation of this method anywhere, so im not sure what to pass here in what form.

Why does GTK if i create a standard Dialog with Stock Buttons correctly translate all Buttons, without me setting locale, textdomain or anything else anywhere. It just knows what is set on my System and translates accordingly.

But if i use Gtk Builder to read a glade file, suddenly i need external libs, set env vars and stuff to get this translated. Makes not much sense to me.

What i want to achieve is to translate widgets that are set through a GtkBuilder file.

It doesnt matter in which language, it can be default, or one that is set.

What i need is the most minimal example.
Comment 11 LRN 2018-03-30 19:20:41 UTC
(In reply to lovetox from comment #10)
> Why does GTK if i create a standard Dialog with Stock Buttons correctly
> translate all Buttons, without me setting locale, textdomain or anything
> else anywhere. It just knows what is set on my System and translates
> accordingly.
GTK has its own share/locale subdir with compiled translation files, it initializes libintl by itself, and calls it internally for of its own strings.

> 
> What i want to achieve is to translate widgets that are set through a
> GtkBuilder file.
> 
> It doesnt matter in which language, it can be default, or one that is set.
> 
> What i need is the most minimal example.

First you need to ensure that libintl knows which language to use. Environment variables are the surest way of doing that. If you can't change environment variables outside, do it inside like this:
> ctypes.cdll.msvcrt._putenv ("LANG=de")
This ensures that you modify the C runtime envtable, not the Python envtable. This way libintl will be able to pick it up.
libintl.libintl_setlocale(0, "de") should have worked, but it does not, because gettext has a bug.

Or you can set "Format:" dropdown in intl.cpl (just start->run->intl.cpl) to "German" (or whatever).

This only affects gtkbuilder. You need os.environ['LANG'] = 'de' for gettext._() to also work.

bindtextdomain() calls in your minimal examle seem to be correct.
Comment 12 lovetox 2018-03-30 22:03:04 UTC
Maybe does it matter that i use MSYS2 Env for all this?

so this is my last attempt

i executed intl.cpl and set to "German"
i set LANG=de in MSYS2 to env, before i executed this

import gettext
import locale
import ctypes
import ctypes.util

_ = gettext.gettext
locale.setlocale(locale.LC_ALL, '')
gettext.bindtextdomain('trans', './locale')
gettext.textdomain('trans')

ctypes.cdll.msvcrt._putenv("LANG=de")

libintl_path = ctypes.util.find_library('libintl-8')
libintl = ctypes.cdll.LoadLibrary(libintl_path)
libintl.libintl_setlocale(0, 'de')
libintl.bindtextdomain('trans', './locale')
libintl.bindtextdomain('trans', '')
libintl.bind_textdomain_codeset('trans', 'UTF-8')


i think i set the locale in every possible way

Also i tried the workaround, to put the translation files into share/locale/de/LC_MESSAGES/

which would be totally fine for me, except it also doesnt work.
Comment 13 LRN 2018-03-30 22:55:38 UTC
Strange, this worked for me. Though my MSYS2 version is somewhat uncommon, but it shouldn't be that much different.

If this doesn't work, then, i'm afraid, the next step is to get a debug build of libintl (or the regular build of libintl + debug symbols; i don't know whether MSYS2 distributes these; you might have to build gettext yourself - if you do, ask MSYS2 devs about doing that), and then see what's actually going inside it.

Run python -m pdb ./translation.py
then, separately, start gdb, then issue 'attach processidofthepythonexeprocess' command
then b libintl_dcigettext
then cond 1 domainname[0] == 't'
then c
then, back in python, also 'c'
then gettext will break on libintl_dcigettext (domainname="trans", msgid1="not translated", msgid2=0, plural=0, n=0, category=1729), and you'll be able to see what it's doing. Step until you come to 'categoryname = category_to_name (category);' line, then step into 'guess_category_value()' call to see how it determines the locale name for the LC_MESSAGES category.

It's very convenient to debug, since you can 'c' in Python over and over again, gdb will remain attached to python.exe as long as you don't quit out of it.

Wait...Why do you do
> libintl.bindtextdomain('trans', '')
after
> libintl.bindtextdomain('trans', './locale')
? That nullifies the effect of that call. That could be the reason why it doesn't work.
Comment 14 lovetox 2018-03-30 23:32:54 UTC
Thanks for helping on this.

that was just a copy past error, it does make no difference if i remove the libintl.bindtextdomain('trans', '')

So do in understand you correctly if i say you tried my example, with your added suggestions on MSYS on Windows, and it worked for you?

If so i will try this on another machine, and if it doesnt work try to debug it like you suggested.

This will need some time though as im not so fluent with building libs and debuging.
Comment 15 LRN 2018-03-30 23:54:28 UTC
Created attachment 370364 [details]
translation.py that worked for me

This is the translation.py that worked for me (both labels are translated). The rest of the files are the same as in your zip.
Comment 16 lovetox 2018-04-02 22:49:33 UTC
So, i read you bug reports at the gnulib and gettext mailinglists

I played around with intl.cpl, and also verified the GetLocalThread that the correct lcid is set (1031 for German)

but all this did not help, so installed python3 on windows (not in MSYS2), installed GTK and tried the workaround Tobias described in the starting post.

And this works, if i put my translations into gnome/share/locale/de/LC_MESSAGES they are picked up.

so does this not prove that the correct locale is determined by GTK (or the libs that GTK uses for that) ?

Could this not be a path problem? is this dir gnome/share/locale/de/LC_MESSAGES a fallback search path if nothing is found in the one set with bindtextdomain?

it seems the domain is set correctly otherwise the translations can not be picked up i guess, but maybe the path is not and so it falls back.

I googled much about building gettext with debug symbols, but its nowhere mentioned, maybe its so trivial that it needs no mention but i dont have much experience with building libs. i requested help from the msys guys, hopefully they come through. Gettext has install instructions for cygwin, so i guess it is not hard to build it myself, though i dont know what switch i have to use to get debug symbols
Comment 17 GNOME Infrastructure Team 2018-05-02 16:42:10 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/gtk/issues/569.