是否有一种可移植的方式来提供 PyPI 上分发的包的本地化?

Ser*_*sta 8 python localization setuptools

语境:

这是我的另一个问题的后续。

我想提供包的本地化版本。按照 Python 文档,我使用 pygettext 提取了一个 .pot 文件,在 .po 文件中准备了翻译,并将其编译在 .mo 文件中。

直到那里一切都很好,我的包显示翻译后的消息。

但我的最终目标是使其在 PyPI 上可用。所以我做了一些研究并发现:

  • setuptools 文档:关于本地化甚至没有一个字......

  • GNU MO 文件的格式

    它解释了格式取决于生成文件的平台的字节序。我的理解是只有 po 文件是可移植的......

  • 在 python 包中包含本地化的正确方法是什么?

    答案是完全相关的,并且谈到了 setuptools/babel 集成,但是:

    • 该集成仅允许构建mo 文件,而不涉及它们的分发
    • 作者描述了他们如何使用它,没有跨系统可移植性的参考
  • Babel:调用 setup.py install 时编译翻译文件

    一种有趣的方式,即使它需要目标平台上的 babel 模块。不是那么重,但比我自己的包重得多...事实上,发行版仅包含 po 文件,并且它们在安装时使用 babel 进行编译。

问题:

有没有办法构建包含已编译mo 文件的平台特定轮子?

如果没有,我将不得不在目标上要求 babel 并尝试在安装时通过 mo 编译找到我的方法。

Ser*_*sta 3

编辑 2018 年 7 月 12 日:

\n

经过一番工作后,我可以根据这个答案中的内容构建一个特定的包。它可以从其他项目中使用,通过 setuptools enty_points 的魔力在构建时自动编译 po 文件。它现已在 GitHUB ( https://github.com/s-ball/mo_installer )上提供,并在 PyPI ( https://pypi.org/project/mo_installer )上分发

\n
\n

我在提出问题之前所做的研究给了我足够的提示来找到可能的解决方案。

\n

我现在可以说,可以在轮子中包含特定于平台的 mo 文件 - 不幸的是,在我当前的解决方案中,轮子没有表明它是特定于平台的。但相同的解决方案允许构建一个在目标平台上构建 mo 文件的源发行版。

\n

现在详细说明:

\n
    \n
  1. 在目标上编译 mo 文件所需的工具:

    \n

    大多数从 Google 或 SO 挑选的解决方案都依赖于 Babel 或 GNU gettextmsgfmt程序。但 cPython 工具包含一个纯 Python 模块msgfmt.py,这在这里就足够了。不幸的是,在许多类似 Linux/Unix 的系统中,这个工具通常没有默认安装。我的解决方案仅包含 3.7.1 版本的该模块的副本(仅 7k 文件)。它看起来像一个非常稳定的代码(近年来几乎没有变化)并且它应该适用于任何> = 3.3的Python

    \n
  2. \n
  3. 设置工具集成

    \n

    setuptools 的神奇之处在于,相同的 build 子命令在内部用于构建二进制轮,从源包使用 pip 安装或直接从python setup.py install完整源包的副本(git clone)安装。因此,我提供了一个build子类setup.py,在调用超类方法之前生成 .mo 文件及其完整路径。我还使用一个MANIFEST.in文件来列出应在源发行版中复制的文件,并使用一个package_data设置参数来列出二进制包或安装文件夹中应包含的内容

    \n
  4. \n
  5. 运行时使用

    \n

    提供要安装在已知包下的 mo 层次结构,os.dirname(__file__)从该包的模块调用给出其父文件夹

    \n
  6. \n
\n
\n

代码(假设msgfmt.py文件复制到一个tools_i18n文件夹下,po文件在一个src文件夹下):

\n

setup.py

\n
...\nsys.path.append(os.path.join(os.path.dirname(__file__), "tools_i18n"))\nimport msgfmt\nfrom distutils.command.build import build as _build\n\nclass Builder(_build):\n    def run(self):\n        # po files in src folder are named domain_lang.po\n        po = re.compile(r"(.*)_(.*).po")\n        for file in os.listdir("src"):\n            m = po.match(file)\n            if m:\n                # create the LANG/LC_MESSAGES subdir of "locale"\n                path = os.path.join(self.build_lib, NAME, "locale",\n                                 m.group(2), "LC_MESSAGES")\n                os.makedirs(path, exist_ok=True)\n                # use msgfmt.py to compile the po file\n                msgfmt.make(os.path.join("src", file),\n                            os.path.join(path, m.group(1) + ".mo"))\n        _build.run(self)\n        \nsetup(\n    name=NAME,\n    ...\n    package_data = { "": [..., "locale/*/*/*.mo"]}, # ensure .mo file are copied\n    cmdclass = {"build": Builder},\n    )\n
Run Code Online (Sandbox Code Playgroud)\n

MANIFEST.in

\n
...\ninclude src/*\ninclude tools_i18n/*\n
Run Code Online (Sandbox Code Playgroud)\n

要在运行时使用翻译:

\n
locpath = os.path.dirname(__file__)\nlang = locale.getdefaultlocale()[0]   # to get platform default language, or whatever...\ntr = gettext.translation("argparse", os.path.join(locpath, "locale"),\n                         [lang], fallback=True)\n
Run Code Online (Sandbox Code Playgroud)\n

使用此方法的完整项目位于https://github.com/s-ball/i18nparse

\n
\n

最后但并非最不重要的一点是,在更深入地阅读GNU gettext 文档之后,我可以说 gettext 可以处理 mo 文件,无论其字节序如何:

\n
\n

任何字节顺序的 MO 文件都可以在任何平台上使用。当 MO 文件的字节顺序不同于 platform\xe2\x80\x99s 时,MO 文件中的 32 位数字将在运行时交换。性能影响可以忽略不计。

\n
\n