有没有一种可移植的方法来提供在 PyPI 上分发的包的本地化?

Is there a portable way to provide localization of a package distributed on PyPI?

上下文:

这是对我的 的跟进。

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

一切都很好,我的包裹显示翻译后的消息。

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

问题:

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

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

编辑 2018 年 7 月 12 日:

经过一些工作,我可以根据此答案中的以下内容构建特定的包。它可以从其他项目中使用,通过 setuptools enty_points 的魔力在构建时自动编译 po 文件。现在可以在 GitHUB (https://github.com/s-ball/mo_installer) and distributed on PyPI (https://pypi.org/project/mo_installer)


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

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

现在了解详情:

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

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

  2. setuptools 集成

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

  3. 运行 时间使用

    如果 mo 层次结构要安装在 knows 包下,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},
    )

MANIFEST.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

获得

最后但同样重要的是,在更深入地阅读 GNU gettext doc 之后,我可以说 gettext 可以处理 mo 文件,无论它们的字节顺序如何:

MO files of any endianness can be used on any platform. When a MO file has an endianness other than the platform’s one, the 32-bit numbers from the MO file are swapped at runtime. The performance impact is negligible.