Интернационализировать проект Python, используя PyGTK3 и Glade - PullRequest
1 голос
/ 04 мая 2019

Я занимаюсь разработкой программы на Python с использованием PyGTK3 и Glade для пользовательского интерфейса.Я хотел бы интернационализировать пользовательский интерфейс не на основе локали в системе, а на языках, «установленных» вместе с программой, и я хотел бы иметь возможность изменять язык интерфейса «на лету», например, выбравязык в меню.Я нахожусь на Windows 10, работаю с Python 3.4.

Я провел много исследований и перепробовал множество вещей, и добился определенного прогресса.Мне удалось установить пакет gettext for Windows (ссылка?), Но он, по-видимому, довольно минимален.xgettext извлекает _ ("строки") из моего исходного кода Python.Но если я попробую xgettext -L Glade (за https://askubuntu.com/questions/218432/how-extract-strings-from-a-ui-file-glade-with-gettext),, то не получится сказать, что эта версия была построена без экспата. Также, кажется, что различные варианты intltool не доступны. Нет проблем. Основано на выводе intltool-Извлечение, описанное на этой странице (http://www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application), Я знал, что могу просто вручную извлечь переводимые строки из файла .glade и поместить их в файл в формате C .h:

char *s = N_("_File");

Затем яЯ мог бы добавить этот файл .h и -kN_ в мою команду xgettext, чтобы получить файл .pot со строками как из исходного кода Python, так и из файла пользовательского интерфейса .glade. Я также мог бы просто написать скрипт Python, который извлекает различные строки из .gladeИсходный файл и добавляет их в виде пар, с пустой строкой, в файл .pot. Насколько я понимаю, это всего лишь методы, чтобы получить соответствующие строки в файл .pot, чтобы их можно было перевести, но, похоже, я могтакже просто вручную создайте файл .pot…

Как только у меня будет файл .pot со строками для перевода, я могу создать .poфайлы с помощью команды msginit:

msginit --input=test.pot --locale=fr_FR     (this creates a file fr.po)

Затем я вручную перевожу (или заставляю кого-то переводить) все строки в каждом текстовом файле .po, и как только это будет сделано, я создаю файлы .mo и помещаюих в соответствующую структуру каталогов (все они являются подкаталогами \ po):

msgfmt --output-file=fr_FR\LC_MESSAGES\test.mo fr.po

Пока все хорошо.

В моем коде Python для включения перевода я пришелсо следующим минимальным кодом:

import gettext
APP_NAME = "test"
locale_path = os.path.realpath('.\po')
langs = ['fr_FR', 'en_US']
lang = gettext.translation(APP_NAME, locale_path, languages=langs, fallback = True)
_ = lang.gettext
builder = Gtk.Builder()
builder.set_translation_domain(APP_NAME)
builder.add_from_file("test.glade")
builder.connect_signals(Handler())

Это прекрасно работает для перевода строк, которые есть в коде Python, но ни один из интерфейса Glade не переводится .Это моя основная проблема.

Установка текста вручную в Glade XML Я нашел эту страницу: Перевод файлов Python GTK Builder (http://martens.me/programming/translating-python-gtk-builder-files.html),, который объясняет, как создать производный классGtk.Builder, который просматривает XML-файл .glade и вручную вызывает set_ {} (в моем контексте это будет set_label, set_text или set_title) для каждого с параметром _ (child.text), так чтоможет произойти перевод. Я сделал несколько изменений в его коде, поскольку объекты не были загружены в Builder до сканирования XML, поэтому я загружаю файл .glade перед синтаксическим анализом дерева, например:

class GtkBuilder(Gtk.Builder):

    def __init__(self, glade_file, APP_NAME):
        super().__init__()
        self.set_translation_domain(APP_NAME)
        self.add_from_file(glade_file)

        tree = ET.parse(glade_file)
        root = tree.getroot()
        self.recursive_xml_translate(root)

И перевод происходит немного ниже (с _ (child.text) для запуска перевода):

    # translate property value
    if child.tag == 'property' and parent_id and child.attrib.get('name'):
        name = child.attrib.get('name')
        obj = self.get_object(parent_id)
        func = getattr(obj, 'set_{}'.format(name), func_not_found)
        func(_(child.text))

В моем коде вместо 4 строк компоновщика, показанных выше, яиспользуйте:

builder = GtkBuilder("PrimerPrep.glade", APP_NAME)
builder.connect_signals(Handler())

Это похоже на работу, но кажется очень запутанным способом добраться туда, куда я хочу.

Другие попытки перевести интерфейс Glade Я также прочиталпроблема в том, что интерфейс Glade представлен с использованием кода C, так что переопределение _ () не переводит строки Glade.Итак, я попробовал варианты этого кода:

try:
    import ctypes
    libintl = ctypes.cdll.LoadLibrary('libintl-8.dll')
    libintl.bindtextdomain(APP_NAME, locale_path)
    libintl.bind_textdomain_codeset(APP_NAME, 'UTF-8')
except:
    print('Error loading translations into Glade file.')

Я также попытался установить переменную окружения:

os.environ['LANG'] = 'fr_FR'

Я поиграл с различными вариантами локали, включая то, что описано здесь: Как связать текстовый домен с локальной папкой для gettext под GTK3 ), но так как мой модуль локали не имеет домена привязки объект, который не принес никаких плодов. И я не хочу устанавливать язык компьютера. Например. У меня нет французского языка на моем компьютере, но я хочу изменить интерфейс программы на французский. Если я пытаюсь установить язык, который не установлен, он выдает ошибку.

Кажется, что ни одна из этих опций не переводит интерфейс Glade.

gettext должен уметь работать лучше? Из документации gettext (https://docs.python.org/3.5/library/gettext.html), в 23.1.3.3) видно, что это не должно быть сложно:

lang1 = gettext.translation('myapplication', languages=['en'])
lang2 = gettext.translation('myapplication', languages=['fr'])
# start by using language1
lang1.install()
# ... time goes by, user selects language 2
lang2.install()

Я пробовал различные комбинации подобных вещей, но ни одна из них, похоже, не влияет на интерфейс Glade (возможно, проблема с языком C, упомянутая выше).

Получение интерфейса для обновления Таким образом, запутанное решение, приведенное выше, является единственным, которое работает в некоторой степени. Но даже тогда я смог установить локаль только при запуске программы, чтобы она вступила в силу. Каков будет процесс изменения языка интерфейса во время работы программы. Я попытался снова запустить процедуру builder.recursive_xml_translate (), как только я вручную изменил процедуру _ () на новый язык перевода, но это, похоже, не обновило интерфейс. Я не смог найти ничего похожего на метод Gtk.Builder.refresh (). Любые предложения о том, как мне обновить язык интерфейса?

Заключение Таким образом, я нашел один запутанный способ перевода интерфейса Glade, так что, похоже, я мог бы выполнить эту работу с достаточными усилиями (если я смогу найти способ обновить его на лету). Но должен быть более легкий путь! Пожалуйста, скажите мне, что я просто скучаю по какой-то маленькой вещи, которая облегчит мою жизнь ...

...