Babel: compile translation files when calling setu

2020-06-17 14:01发布

问题:

I'm developing a Flask application using Babel. Thanks to Distutils/Setuptools Integration, all the parameters of compile/extract/... functions are stored in setup.cfg and compiling the i18n files is as easy as

./setup.py compile_catalog

Great. Now I would like this to be done automatically when running

./setup.py install

In make's words, that would be letting install target depend on compile_catalog target.

The context

We store only translation (.po) files in the code repository. .gitignore excludes .mo and .pot files from being tracked.

When a developer pulls a new revision of the code, he runs

pip install -r requirements.txt

to update dependencies and install the project in development mode. Then, using above command line, he compiles the translation binary (.mo) files.

Is there a simple and recommended way to modify setup.py to do both operations in one step? Or am I trying to misuse setuptools?

Using a script that like this would work for development purposes:

#!/bin/sh
./setup.py compile_catalog
pip install -r requirements.txt

but I would like a solution that also works when the package is installed with usual setup.py install instructions, like if installed from PyPi.

Should I understand that setuptools are not meant to be used like this, and people distributing software compile their translation files either manually or using custom scripts when creating their archives, rather than relying on setup.py to compile them at installation time?

I didn't find many posts on the Internet addressing this. The ones I found involved running pybabel command line interface from a function in setup.py, which sounds like a shame as it misses the point of setuptools integration.

回答1:

I think your demand is totally valid and I'm quite surprised that there seems to be no official guideline on how to accomplish this.

The project I working on now also went multilingual and this is what I did:

  • In setup.cfg, make appropriate entries so that compile_catalog can be run without options.

  • In setup.py, subclass the install command from setuptools:

setup.py:

from setuptools import setup
from setuptools.command.install import install

class InstallWithCompile(install):
    def run(self):
        from babel.messages.frontend import compile_catalog
        compiler = compile_catalog(self.distribution)
        option_dict = self.distribution.get_option_dict('compile_catalog')
        compiler.domain = [option_dict['domain'][1]]
        compiler.directory = option_dict['directory'][1]
        compiler.run()
        super().run()

Then, when calling setup(), register our InstallWithCompile command with the name "install" and make sure that the *.mo files will be included in the package:

setup(
    ...
    cmdclass={
        'install': InstallWithCompile,
    },
    ...
    package_data={'': ['locale/*/*/*.mo', 'locale/*/*/*.po']},
)

Since babel is used during the setup, you should add it as a setup dependency:

setup_requires=[
    'babel',
],

Note that a package (here, babel) appearing in both setup_requires and install_requires won't be installed correctly using python setup.py install due to an issue in setuptools, but it works fine with pip install.