Translate Text in Sphinx Templates and Configurations
Tags: python, sphinx, tutorial
Published: 2020-03-11 10:25:47

---
title: Translate Text in Sphinx Templates and Configurations
published: true
date: 2020-03-07 11:48:25 UTC
tags: Python,Sphinx,tutorial
canonical_url: https://blog.1a23.com/2020/03/07/translate-text-in-sphinx-templates-and-configurations/?utm_source=rss&utm_medium=rss&utm_campaign=translate-text-in-sphinx-templates-and-configurations
cover_image: https://blog.1a23.com/wp-content/uploads/sites/2/2020/03/Alt.png
---

Weeks ago when I was playing around with [the docs of EFB](https://efb-docs.1a23.com) and the Crowdin translation widget, I realized that the default theme for Sphinx — Alabaster isn’t really doing well in term of translation. It seems like the author [isn’t really confident on that](https://github.com/bitprophet/alabaster/issues/71) _(or simply didn’t care since 4 years ago)_.

As the theme itself is open source, and Sphinx is flexible enough, couldn’t we just translate it ourselves? It turns out that things are not that complicated.

## Translate text in theme

This one is pretty straightforward. What we need to do is simply copy the template we want to change to our local directory, wrap the texts with `{{ _("") }}`, and then add it to a message catalog.

Taking example of the donate section in the sidebar:

```jinja2
{# TODO: wrap all these in their own divs for easier styling #}

{% if theme_donate_url or theme_opencollective or theme_tidelift_url %}
<h3 class="donation">Donate/support</h3>
{% endif %}

{% if theme_donate_url %}
<p>
<a class="badge" href="{{ theme_donate_url }}">
<img src="https://img.shields.io/badge/donate-%E2%9D%A4%C2%A0-ff69b4.svg?style=flat" alt="Donate">
</a>
</p>
{% endif %}

{% if theme_opencollective %}
<p>
<a class="badge" href="https://opencollective.com/{{ theme_opencollective }}/donate" target="_blank">
  <img src="https://opencollective.com/{{ theme_opencollective }}/donate/button.png?color={{ theme_opencollective_button_color }}" width=300 />
</a>
</p>
{% endif %}

{% if theme_tidelift_url %}
<p>
Professionally-supported {{ project }} is available with the
<a href="{{ theme_tidelift_url }}">Tidelift Subscription</a>.
</p>
{% endif %}
```

<figcaption><a href="https://github.com/bitprophet/alabaster/blob/master/alabaster/donate.html">alabaster/donate.html on GitHub</a></figcaption>

Copy this file to your documentation folder as `_template/donate.html` and edit it. The only visible text here is the title: “Donate/support”. Wrapping it with the `gettext` function, we can get:

```jinja2
{% if theme_donate_url or theme_opencollective or theme_tidelift_url %}
<h3 class="donation">{{ _("Donate/support") }}</h3>
{% endif %}
```

Repeat this on all strings untranslated in your theme.

Once we have everything we need to translate wrapped, `sphinx-build -b gettext` can automatically recognise these strings and put them in the `sphinx` catalog. We can then apply our normal translation workflow just like other catalogs.

## Translate strings in configuration

Translating text in the configuration is relatively more complicated than the previous one. We need to hook into the workflow of Sphinx for everything to work properly.

Firstly, we need to choose a catalog for our strings in `conf.py`. It is better to have a new catalog to prevent unnecessary effort of merging catalogs. Reference to the catalog can be obtained through `sphinx.locale.get_translation`.

```python
from sphinx.locale import get_translation

MESSAGE_CATALOG_NAME = "docs_conf"
_ = get_translation(MESSAGE_CATALOG_NAME)
```

Then you can use this function to replace strings.

```python
project = _('My Sphinx project')
copyright = _('2020 Eana Hufwe')
author = _('Eana Hufwe')
docs_title = _('My Sphinx Documentation')
description = _('A demo project with Sphinx')
```

`sphinx-build -b gettext` cannot extract these strings, so we need to extract it manually.

```shell
xgettext -o _build/locale/docs_conf.pot conf.py
```

If you then translate the catalog and test it locally, everything just seems to work. But if you are using any CI for your docs like ReadTheDocs, these strings are not translated. That is because these CIs relies on Sphinx to build `.mo` files, and you cannot add extra catalog before `.mo` are built. To workaround this, we need to hook onto the point before HTML files are generated.

```python
def html_page_context(self, pagename, templatename, context, doctree):
    if not self.catalog_added:
        package_dir = path.abspath(path.dirname( __file__ ))
        locale_dir = os.path.join(package_dir, 'locale')
        self.add_message_catalog(MESSAGE_CATALOG_NAME, locale_dir)
        self.catalog_added = True

def setup(self):
    self.catalog_added = False
    self.connect("html-page-context", html_page_context)
```

This hook will add the new catalog into the catalogs to consider, and build the `.mo` file if not available. The property `self.catalog_added` is added to ensure that the catalog is added only once per run instead of once per HTML file.

* * *

Both of the strategies are currently applied to EFB, and is working well both locally and on ReadTheDocs. You can see it live here: [https://efb-docs.1a23.studio/](https://efb-docs.1a23.studio/)

The post [Translate Text in Sphinx Templates and Configurations](https://blog.1a23.com/2020/03/07/translate-text-in-sphinx-templates-and-configurations/) appeared first on [1A23 Blog](https://blog.1a23.com).