为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

万千封印 2021-08-17 09:56:40
语境:这是对我的另一个问题的一种跟进。我想提供一个包的本地化版本。按照 Python 文档,我用 pygettext 提取了一个 .pot 文件,在 .po 文件中准备了翻译,在 .mo 文件中编译了它。到那里一切都很好,我的包裹显示了翻译后的消息。但我的最终目标是让它在 PyPI 上可用。所以我做了一些研究,发现:setuptools 文档:关于本地化甚至一个字都没有...GNU MO 文件的格式它解释了格式取决于生成文件的平台的字节序。我的理解是只有 po 文件是可移植的...在 python 包中包含本地化的正确方法是什么?答案是完全相关的,并谈到了 setuptools/babel 集成,但是:该集成只允许构建mo 文件,而不涉及它们的分布作者描述了他们如何使用它,没有关于跨系统可移植性的参考Babel:调用 setup.py install 时编译翻译文件一种有趣的方式,即使它需要目标平台上的 babel 模块。不那么重,但比我自己的包重得多……事实上,这些发行版只包含 po 文件,它们在安装时用 babel 编译。问题:有没有办法构建包含编译的mo 文件的特定于平台的轮子?如果不是,我将不得不在目标上要求 babel 并尝试在安装时通过 mo 编译找到我的方法。
查看完整描述

1 回答

?
跃然一笑

TA贡献1826条经验 获得超6个赞

经过一些工作,我可以根据此答案中的内容构建一个特定的包。它可以从其他项目中使用,通过 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 gettextmsgfmt程序。但是 cPython 工具包含一个纯 Python 模块msgfmt.py,这里就足够了。不幸的是,在许多类似 Linux/Unix 的系统中,这个工具通常没有被默认安装。我的解决方案仅包含 3.7.1 版本的该模块的副本(仅 7k 文件)。它看起来像一个非常稳定的代码(近年来很少有变化),它应该适用于任何 >= 3.3 的 Python


设置工具集成


setuptools 的神奇之处在于,相同的 build 子命令在内部用于构建二进制轮,从源包中使用 pip 安装或python setup.py install从完整源包的副本(git clone)直接安装。所以我提供了一个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 位数字在运行时交换。性能影响可以忽略不计。


查看完整回答
反对 回复 2021-08-17
  • 1 回答
  • 0 关注
  • 145 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号