DEV Community

Cover image for Translate Text in Sphinx Templates and Configurations
Eana Hufwe
Eana Hufwe

Posted on • Originally published at blog.1a23.com on

Translate Text in Sphinx Templates and Configurations

Weeks ago when I was playing around with the docs of EFB 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 (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:

{# 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 %}
Enter fullscreen mode Exit fullscreen mode
alabaster/donate.html on GitHub

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:

{% if theme_donate_url or theme_opencollective or theme_tidelift_url %}
<h3 class="donation">{{ _("Donate/support") }}</h3>
{% endif %}
Enter fullscreen mode Exit fullscreen mode

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.

from sphinx.locale import get_translation

MESSAGE_CATALOG_NAME = "docs_conf"
_ = get_translation(MESSAGE_CATALOG_NAME)
Enter fullscreen mode Exit fullscreen mode

Then you can use this function to replace strings.

project = _('My Sphinx project')
copyright = _('2020 Eana Hufwe')
author = _('Eana Hufwe')
docs_title = _('My Sphinx Documentation')
description = _('A demo project with Sphinx')
Enter fullscreen mode Exit fullscreen mode

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

xgettext -o _build/locale/docs_conf.pot conf.py
Enter fullscreen mode Exit fullscreen mode

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.

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)
Enter fullscreen mode Exit fullscreen mode

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/

The post Translate Text in Sphinx Templates and Configurations appeared first on 1A23 Blog.

Top comments (0)