You introduced an awesome piece of code and revel in your glorious accomplishment. Suddenly your world comes crashing down when a core hands you a -1 because your code is not translated. What gives?
If you are writing software for a global audience, you must ensure that it is translated so that other people around the world are able to use it. Adding translation to your code is not that hard and a requirement for horizon.
If you are interested in contributing translations, you may want to investigate Zanata and the upstream translations. You can visit the internationalization project IRC channel #openstack-i18n, if you need further assistance.
You can skip this section if you are only interested in learning how to use translation. This section explains the two main components to translation: message extraction and message substitution. We will briefly go over what each one does for translation as a whole.
Message extraction is the process of collecting translatable strings from the code. The diagram above shows the flow of how messages are extracted and then translated. Lets break this up into steps we can follow:
Note
When pushing code upstream, the only requirement is to mark the strings correctly. All creation of POT and PO files is handled by a daily upstream job. Further information can be found in the translation infrastructure documentation.
Message substitution is not the reverse process of message extraction. The process is entirely different. Lets walk through this process.
If you are setting up a project and need to know how to make it translatable, please refer to this guide.
To make your strings translatable, you need to mark it so that horizon can locate and extract it into a POT file. When a user from another locale visits your page, your string is replaced with the correct translated version.
To translate a string, simply wrap one of the gettext variants around the string. The examples below show you how to do translation for various scenarios, such as interpolation, contextual markers and translation comments.
from django.utils.translation import pgettext
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
class IndexView(request):
# Single example
_("Images")
# Plural example
ungettext(
'there is %(count)d object',
'there are %(count)d objects',
count) % { 'count': count }
# Interpolated example
mood = ‘wonderful’
output = _('Today is %(mood)s.') % mood
# Contextual markers
pgettext("the month name", "May")
# Translators: This message appears as a comment for translators!
ugettext("Welcome translators.")
Note
In the example above, we imported ugettext as _. This is a common alias for gettext or any of its variants. In Django, you have to explicitly spell it out with the import statement.
To use translation in your template, make sure you load the i18n module. To translate a line of text, use the trans template tag. If you need to translate a block of text, use the blocktrans template tag.
Sometimes, it is helpful to provide some context via the comment template tag. There a number of other tags and filters at your disposal should you need to use them. For more information, see the Django docs
{% extends 'base.html' %}
{% load i18n %}
{% block title %}
{% trans "Images" %}
{% endblock %}
{% block main %}
{% comment %}Translators: Images is an OpenStack resource{% endcomment %}
{% blocktrans with amount=images.length %}
There are {{ amount }} images available for display.
{% endblocktrans %}
{% endblock %}
The Django message catalog is injected into the front-end. The gettext function is available as a global function so you can just use it directly. If you are writing AngularJS code, we prefer that you use the gettext service, which is essentially a wrapper around the gettext function.
Angular
.module(…)
.controller(myCtrl);
myCtrl.$inject = [‘horizon.framework.util.i18n.gettext’];
function myCtrl(gettext) {
var translated = gettext(‘Images’);
}
Warning
For localization in AngularJS files, use the AngularJS service horizon.framework.util.i18n.gettext. Ensure that the injected dependency is named gettext or nggettext. If you do not do this, message extraction will not work properly!
To use translation in your AngularJS template, use the translate tag or the translate filter. Note that we are using angular-gettext for message substitution but not for message extraction.
<translate>Directive example</translate>
<div translate>Attribute example</div>
<div translate>Interpolated {{example}}</div>
<span>{$ ‘Filter example’|translate $}</span>
<span translate>
This <em>is</em> a <strong>bad</strong> example
because it contains HTML and makes it harder to translate.
However, it will still translate.
</span>
Note
The annotations in the example above are guaranteed to work. However, not all of the angular-gettext annotations are supported because we wrote our own custom babel extractor. If you need support for the annotations, ask on IRC in the #openstack-horizon room or report a bug. Also note that you should avoid embedding HTML fragments in your texts because it makes it harder to translate. Use your best judgement if you absolutely need to include HTML.
The pseudo translation tool can be used to verify that code is ready to be translated. The pseudo tool replaces a language’s translation with a complete, fake translation. Then you can verify that your code properly displays fake translations to validate that your code is ready for translation.
It should look weird. More specifically, the translatable segments are going to start and end with a bracket and they are going to have some added characters. For example, “Log In” will become “[~Log In~您好яшçあ]” This is useful because you can inspect for the following, and consider if your code is working like it should:
Don’t forget to remove any pseudo translated .pot or .po files. Those should not be submitted for review.