Существует ли портативный способ обеспечить локализацию пакета, распространяемого по PyPI? - PullRequest
0 голосов
/ 13 ноября 2018

Контекст:

Это своего рода продолжение другого вопроса моего.

Я хотел бы предоставить локализованные версии пакета. Следуя документации по Python, я извлек файл .pot с pygettext, подготовил перевод в файл .po, скомпилировал его в файл .mo.

Пока все хорошо, и мой пакет отображает переведенные сообщения.

Но моей конечной целью было бы сделать его доступным в PyPI. Итак, я провел некоторое исследование и обнаружил:

  • документация к setuptools : ни одного слова о локализации ...
  • Формат файлов GNU MO

    Это объясняет, что формат зависит от порядкового номера платформы, на которой был создан файл. Я понимаю, что только файлы po являются переносимыми ...

  • Как правильно включить локализацию в пакеты Python?

    Ответ полностью актуален и говорит об интеграции setuptools / babel, но:

    • , что интеграция позволяет создавать mo-файл и не говорит об их распространении
    • автор описывает, как они используют его, нет ссылок на переносимость между системами
  • Babel: компилировать файлы перевода при вызове setup.py install

    Интересный способ, даже если для этого требуется модуль babel на целевой платформе. Не такой тяжелый, но намного тяжелее, чем мой собственный пакет ... На самом деле, дистрибутивы содержат только po-файлы, и они компилируются с babel во время установки.

Вопрос:

Есть ли способ создать диски для конкретной платформы, содержащие скомпилированные mo-файлы?

Если нет, мне нужно будет указать babel на цели и попытаться найти способ компиляции mo во время установки.

1 Ответ

0 голосов
/ 15 ноября 2018

РЕДАКТИРОВАТЬ 12/12/2018:

После некоторой работы я мог бы создать конкретный пакет на основе того, что приведено ниже в этом ответе.Его можно использовать из других проектов для автоматической компиляции po-файлов во время сборки с помощью волшебства setuptools enty_points.Теперь он доступен на GitHUB (https://github.com/s-ball/mo_installer) и распространяется на PyPI (https://pypi.org/project/mo_installer)


). Исследования, которые я проводил до того, как задать вопрос, дали мне достаточно подсказок, чтобы найти возможное решение.

Теперь я могу сказать, что возможно включить mo-файл для конкретной платформы в колесо - к сожалению, в моем текущем решении колесо не указывает, что оно зависит от платформы. Но это же решение позволяет создать источник.дистрибутив, который строит файл mo на целевой платформе.

Теперь для подробностей:

  1. инструменты, необходимые для компиляции файла mo на целевой:

    Большинство решений, выбранных из Google или SO, основаны либо на Babel, либо на программе GNU gettext * 1019. * Но инструменты cPython включают в себя чистый Python-модуль msgfmt.py, которого здесь достаточно. К сожалению, этот инструмент часто не устанавливается по умолчанию вмногие Linux / Unix-подобные. Мое решение просто включает в себя копию этого модуля (простой файл 7k) для версии 3.7.1. Он выглядит как очень стабильный код(несколько изменений за последние годы), и он должен работать для любого Python> = 3.3

  2. интеграция с setuptools

    Волшебство setuptools заключается в том, что одна и та же подкоманда build внутреннеиспользуется для сборки бинарного колеса, для установки с помощью pip из исходного пакета или для непосредственной установки с python setup.py install из копии (git clone) полного исходного пакета.Поэтому я предоставляю build подкласс в setup.py, который генерирует файлы .mo с их полным путем перед вызовом метода суперкласса.Я также использую файл MANIFEST.in, чтобы вывести список файлов, которые должны быть скопированы в исходном дистрибутиве, и аргумент установки package_data, чтобы вывести список того, что должно быть в бинарном пакете или папке установки

  3. использование во время выполнения

    При условии, что иерархия mo, устанавливаемая в известном пакете, os.dirname(__file__), вызываемая из модуля этого пакета, дает его родительскую папку


Код (при условии, что файл msgfmt.py скопирован в папку tools_i18n, а файлы po находятся в папке src):

в setup.py

...
sys.path.append(os.path.join(os.path.dirname(__file__), "tools_i18n"))
import msgfmt
from distutils.command.build import build as _build

class Builder(_build):
    def run(self):
        # po files in src folder are named domain_lang.po
        po = re.compile(r"(.*)_(.*).po")
        for file in os.listdir("src"):
            m = po.match(file)
            if m:
                # create the LANG/LC_MESSAGES subdir of "locale"
                path = os.path.join(self.build_lib, NAME, "locale",
                                 m.group(2), "LC_MESSAGES")
                os.makedirs(path, exist_ok=True)
                # use msgfmt.py to compile the po file
                msgfmt.make(os.path.join("src", file),
                            os.path.join(path, m.group(1) + ".mo"))
        _build.run(self)

setup(
    name=NAME,
    ...
    package_data = { "": [..., "locale/*/*/*.mo"]}, # ensure .mo file are copied
    cmdclass = {"build": Builder},
    )

InMANIFEST.in:

...
include src/*
include tools_i18n/*

Чтобы использовать переводы во время выполнения:

locpath = os.path.dirname(__file__)
lang = locale.getdefaultlocale()[0]   # to get platform default language, or whatever...
tr = gettext.translation("argparse", os.path.join(locpath, "locale"),
                         [lang], fallback=True)

Полный проект с использованием этого метода доступен по адресу https://github.com/s-ball/i18nparse


И последнее, но не менее важное: после более глубокого чтения gettext doc GNU я могу сказать, что gettext может обрабатывать mo-файлы независимо от их порядкового номера:

MO-файлов любогоEndianness может использоваться на любой платформе.Когда MO-файл имеет порядковый номер, отличный от порядкового номера платформы, 32-разрядные числа из MO-файла меняются местами во время выполнения.Влияние на производительность незначительно.

...