如何在GTK3下将文本域绑定到本地文件夹以进行gettext翻译

24

使用gettext,您可以使用默认的系统范围本地化目录,或使用bindtextdomain指定一个自己的目录。当直接从源代码运行程序时,编译的.mo翻译文件不在系统默认位置上时,这将非常有用。

在Python中,您可以这样做:

import gettext
from gettext import gettext as _
gettext.bindtextdomain('nautilus-image-manipulator', '/path/to/mo/folder')
gettext.textdomain('nautilus-image-manipulator')

假设 /path/to/mo/folder 包含熟悉的 fr/LC_MESSAGES/nautilus-image-manipulator.mo 结构。像这样的调用:

print _("Delete this profile")

请返回适当翻译后的字符串,从本地的.mo文件中获取,非常感谢。

在GTK+2/pygtk中,存在gtk.glade.bindtextdomain,但我想知道在GTK+3/PyGObject中是否有等效的函数。

举个具体的例子,这是从其Glade文件创建Nautilus Image Manipulator的UI的方法:

from gi.repository import Gtk
builder = Gtk.Builder()
builder.set_translation_domain('nautilus-image-manipulator')
builder.add_from_file(ui_filename)
return builder

界面的一部分不是从Glade文件构建的(即从代码设置),这些部分能够正确翻译,但是来自Glade文件的字符串仍然显示为英文。

我觉得在调用builder.set_translation_domain之前,我可能错过了一些调用builder.bind_text_domain('nautilus-image-manipulator', '/path/to/mo/folder')的步骤。有任何想法如何执行此操作吗?


你尝试过常规的 gettext.bindtextdomain() 吗? - ptomato
3个回答

15
在PyGtk中,您也可以使用Gtk.Builder。根据PyGtk Gtk.Builder文档的说明:

http://developer.gnome.org/pygtk/stable/class-gtkbuilder.html#properties-gtkbuilder

当翻译接口描述中标记为可翻译的属性值时,使用的翻译域。如果翻译域为None,则GtkBuilder使用gettext(),否则使用dgettext()。默认值:None。 换句话说,Gtk.Builder使用来自"C库"的dgettext()。问题在于,Python的gettext模块函数bindtextdomain()出于我不知道的某种原因不设置"C库"。解决方法是使用locale模块,该模块还公开了该接口。从Python locale模块文档中可以看到:

http://docs.python.org/library/locale#access-to-message-catalogs

模块locale在支持的系统上提供了C库的gettext接口。它由函数gettext()、dgettext()、dcgettext()、textdomain()、bindtextdomain()和bind_textdomain_codeset()组成。这些函数与gettext模块中的相同函数类似,但使用C库的消息目录二进制格式和C库的搜索算法来定位消息目录。
Python应用程序通常不需要调用这些函数,而应该使用gettext。这个规则的一个已知例外是链接了其他C库的应用程序,这些库内部调用gettext()或dcgettext()。对于这些应用程序,可能需要绑定文本域,以便库可以正确地定位其消息目录。
这就是当前的情况。好恶心啊:S
这将完成它,文件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()

我的 Glade 文件 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>

记得根据使用以下命令提取的 .po 文件,在 mo/LANG/LC_MESSAGES/myapp.mo 中创建 mo 文件:
xgettext --keyword=translatable --sort-output -o en.po test.glade

它的外观是什么:

enter image description here

敬祝好运


谢谢,这很好用。但我想知道这行代码 "locale.setlocale(locale.LC_ALL, '')" 实际上是做什么的 - 即使我删除它,我的应用程序也会被翻译... 这个调用是否真的必要? - oliver
我按照这里的建议添加:http://docs.python.org/2/library/locale.html#locale.setlocale“这将所有类别的区域设置为用户的默认设置(通常在LANG环境变量中指定)。”我想,在某些情况下,您可能会遇到LC_*变量与LC_ALL不同的情况,或者在没有区域变量的情况下,但我不是很确定。 - Havok
我知道这很老,但现在我正在面对它... 由于Windows没有带有gettext的C库,因此此方法似乎仅适用于Linux。 - Zvika
@Zvika 如果我没记错的话,你需要在Windows上像这样做:https://github.com/carlos-jenkins/nested/blob/1.x/nested/context.py#L110 ... 可能还需要设置环境变量以使gettext正常工作:https://github.com/carlos-jenkins/nested/blob/1.x/nested/context.py#L147 - Havok
它能在OS X上运行吗? 我上次检查时,locale.bindtextdomain在这个平台上不存在...(请参阅https://github.com/multani/sonata/issues/67) - Jonathan Ballet

2

由于奖励未能吸引到 Mac OS X 的答案,我不得不自己进行研究。以下是我使用的代码片段:

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('')

稍后,当您拥有Gtk.Builder时,请设置域:
builder.set_translation_domain('domain')

这只有在gettext的库 libintl 在库路径中时才能工作,否则会优雅地失败。为了使翻译正常工作,您需要安装gettext作为依赖项。

1
在Windows下激活Gtk / Python的gettext翻译解决方案是elib_intl.py。可以通过谷歌轻松找到该文件。这允许对代码中的文本和glade UI中的文本进行翻译。以下是用于以下环境的代码: - Windows 7 - Python 2.7 - 由pygi-aio-3.10.2-win32_rev18-setup.exe加载的Gtk 3+ 它应该适用于任何Windows系统,并且也适用于Python 3。elib_intl.py可与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")

如果您正在使用Gtk 3,您可能会收到一个错误:对于第447行:

libintl = cdll.intl

这个错误表示:找不到模块。 原因是在Gtk3中,dll的名称已更改。不再是intl.dll。在描述的Pygi安装中,名称为:libintl-8。你必须将引发错误的行替换为:

libintl = cdll.LoadLibrary("libintl-8.dll")

您可以在这里找到完整的工作示例: pdfBooklet 2.4.0(警告:我写此时可能还没有更新)

感谢Dieter Verfaillie编写了elib_intl。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接