这是我的another question的跟进活动。
我想提供软件包的本地化版本。根据Python文档,我用pygettext提取了一个.pot文件,准备了一个.po文件的翻译,并将其编译为.mo文件。
一切都很好,我的包裹会显示翻译后的消息。
但是我的最终目标是使其在PyPI上可用。因此,我进行了一些研究,发现:
它说明格式取决于生成文件的平台的字节顺序。我的理解是,只有po文件是可移植的...
What is the correct way to include localisation in python packages?
答案是完全相关的,它涉及setuptools / babel集成,但是:
Babel: compile translation files when calling setup.py install
一种有趣的方式,即使它在目标平台上需要babel模块也是如此。并不是那么重,但是比我自己的软件包要重...实际上,这些发行版仅包含po文件,并且它们在安装时使用babel进行编译。
有没有办法构建包含已编译 mo文件的平台专用轮子?
如果没有,我将不得不在目标上要求babel并尝试在安装时通过mo编译找到自己的方式。
答案 0 :(得分:2)
经过一些工作,我可以根据此答案中的以下内容构建特定的程序包。借助setuptools enty_points的魔力,可以在其他项目中使用它来自动编译po文件。现在它可以在GitHUB(https://github.com/s-ball/mo_installer)上使用,并可以在PyPI(https://pypi.org/project/mo_installer)上分发
我在提出问题之前所做的研究为我提供了足够的提示,以寻求可能的解决方案。
我现在可以说,有可能在轮子中包含特定于平台的mo文件-不幸的是,在我当前的解决方案中,轮子未提供特定于平台的指示。但是相同的解决方案允许构建在目标平台上构建mo文件的源分发。
详细信息:
在目标上编译mo文件所需的工具:
从Google或SO挑选的大多数解决方案都依赖Babel或GNU gettext msgfmt
程序。但是cPython工具包括一个纯Python模块msgfmt.py
,在这里就足够了。不幸的是,在许多类似Linux / Unix的系统中,通常默认情况下未安装此工具。我的解决方案仅包含用于3.7.1版本的该模块的副本(仅7k文件)。它看起来像一个非常稳定的代码(近年来变化不大),并且适用于任何Python> = 3.3
setuptools集成
setuptools的神奇之处在于,内部使用了相同的build子命令来构建二进制文件,使用源包中的pip进行安装或直接使用完整版本的副本(git clone)中的python setup.py install
进行安装源码包。因此,我在build
中提供了一个setup.py
子类,该子类在调用超类方法之前生成具有完整路径的.mo文件。我还使用MANIFEST.in
文件列出应在源分发中复制的文件,并使用package_data
安装参数列出二进制软件包或安装文件夹中应包含的内容
运行时间使用情况
提供了要在已知软件包下安装的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},
)
在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文件都可以在任何平台上使用。如果MO文件的字节序不是平台的字节序,则MO文件中的32位数字将在运行时交换。对性能的影响可以忽略不计。