In a previous article I covered how CakePHP would potentially be moving to using sphinx for the 2.0 documentation. Myself and some of the other CakePHP developers have been working on this option, and seeing if it has any legs. Turns out that sphinx is actually a pretty great tool. It works great out of the box for generating single language docs, but multi-language docs are a bit undocumented, and require some digging.
Thankfully, the bazaar project also has documentation in several languages. I used their implementation as the basis for our own multi-language documentation.
Splitting up the content
We decided very quickly that each language would be in a separate directory, and each version of the documentation would be a separate branch. This allows you to easily generate all the documentation for all the languages for a single version, without resorting to branch shenanigans. This resulted in a repository that looks like:Show Plain Text
- â”œâ”€â”€ Makefile
- â”œâ”€â”€ _templates
- â”‚Â Â â””â”€â”€ // custom templates here
- â”œâ”€â”€ build
- â”œâ”€â”€ config
- â”‚Â Â â”œâ”€â”€ __init__.py
- â”‚Â Â â””â”€â”€ all.py
- â”œâ”€â”€ en
- â”‚Â Â â”œâ”€â”€ Makefile
- â”‚Â Â â”œâ”€â”€ _static
- â”‚Â Â â”œâ”€â”€ .. rest of the documentation here.
- â”‚Â Â â”œâ”€â”€ conf.py
- â”‚Â Â â””â”€â”€ index.rst
- â””â”€â”€ es
- â”œâ”€â”€ Makefile
- â”œâ”€â”€ _static
- â”œâ”€â”€ conf.py
- â”œâ”€â”€ .. rest of the documentation here
- â””â”€â”€ index.rst
Each language is a top level directory, along with templates and documentation wide configuration files. Having languages as top-level directories makes sense if you think about the end urls we might want.
http://book.cakephp.org/2.0/en/index.html. With version and language as the first path segments, its easy to switch languages, and versions.
The config folder contains an
all.py file, which contains all the generic configuration information used across the various translations of the documentation. Each language also contains a
conf.py file that contains language specific configuration values. Finally, tempaltes are shared, as all translations need to look the same.
Makefiles and building
Makefiles by default, and while fairly old,
make is still a great tool for build tasks, especially when they are simple ones. I decided to use one master Makefile, and additional Makefiles for each translation, this way you could run
make for one, or all the languages pretty easily. The top level Makefile looks like:
- # MakeFile for building all the docs at once.
- # Inspired by the Makefile used by bazaar.
- # http://bazaar.launchpad.net/~bzr-pqm/bzr/2.3/
- PYTHON = python
- .PHONY: all clean html latexpdf epub htmlhelp
- # Dependencies to perform before running other builds.
- SPHINX_DEPENDENCIES = \
- # Copy-paste the english Makefile everwhere its needed.
- %/Makefile : en/Makefile
- $(PYTHON) -c "import shutil; shutil.copyfile('$<', '$@')"
- # Make the HTML version of the documentation with correctly nested language folders.
- html: $(SPHINX_DEPENDENCIES)
- cd en && make html LANG=en
- cd es && make html LANG=es
- htmlhelp: $(SPHINX_DEPENDENCIES)
- cd en && make htmlhelp LANG=en
- cd es && make htmlhelp LANG=es
- epub: $(SPHINX_DEPENDENCIES)
- cd en && make epub LANG=en
- cd es && make epub LANG=es
- latexpdf: $(SPHINX_DEPENDENCIES)
- cd en && make latexpdf LANG=en
- cd es && make latexpdf LANG=es
- rm -rf build/*
For each task that we have, I just manually enumerate all the languages and call the same task on all the translations. I pass a language flag in, as a precondition to each build, is to clone the english Makefile to all the translations. This way I don’t have to copy & paste Makefile changes around.
The translation Makefiles look fairly similar to the standard sphinx Makefiles with a few addition for languages. The most important changes look like:Show Plain Text
- ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees/$(LANG) $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
- # more stuff
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html/$(LANG)
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html/$(LANG)."
By re-targeting the build and doctree directories, all the languages can build into one destination. This saves an additional step of collecting an collating all the various builds together.
So there you have it, a reasonably straight-forward way of generating multi-language documentation with sphinx.