Migrating from Tower¶
Tower and Puente have some differences. If you’re using Tower, then you’ll need to do something like the following to switch to Puente.
Upgrade to Python 2.7.
Puente doesn’t support Python 2.6, so you’re going to have to upgrade.
Upgrade to Django 1.7.
If you’re using Jingo, switch to 0.7.1.
Switch from
messages.po(t)
todjango.po(t)
.Tower let you have many domains and defaulted to
messages.po(t)
.Puente is moving closer to vanilla Django. Django uses
django.po(t)
anddjangojs.po(t)
, so Puente does, too.- If you just have
messages.po(t)
andjavascript.po(t)
, then rename yourmessage.po(t)
todjango.po(t)
andjavascript.po(t)
todjangojs.po(t)
and sync with your translation system (e.g. Verbatim, Pontoon, etc). - If you have a bunch of domains and you can squash them all into
django.po(t)
anddjangojs.po(t)
, then do that. - If you have a bunch of domains and can’t squash them into
django.po(t)
anddjangojs.po(t)
, then we should talk–open up an issue.
- If you just have
Sync your strings
Using Tower, extract, merge and sync strings with Verbatim. That way you know exactly what changed for the next few steps.
Stop using Tower’s gettext.
Switch instances of this:
from tower import ugettext as _
to this:
from django.utils.translation import ugettext as _
You’ll encounter two possible issues here:
If you have consecutive sequences of whitespace in your gettext strings, then the msgids will change.
For example:
from tower import ugettext as _ _('knock knock. who is there?')
Tower collapses whitespace in all gettext strings, so that turns into
"knock knock. who is there?"
.When you switch that to Django’s ugettext, then you’re using Tower for extraction, but Django’s ugettext to look up the translation. Because Django’s ugettext doesn’t collapse whitespace, the msgid being used to look up the translation will have all the whitespace in it which won’t match the msgid in the
.po
file and thus even though the strings are translated, they won’t show up as translated on the site.You’ll need to fix that in the string so the resulting code should look like this:
from django.utils.translation import ugettext as _ _('knock knock. who is there?')
That way the msgid generated during extraction is the same as the one that’s generated when rendering and that’s the same as the string the translator translated, so everything should work super.
Definitely check the msgids after doing this to make sure they’re the same and fix any issues you see.
Tower’s gettext and ngettext supported msgctxt as a separate argument.
For example:
from tower import ugettext as _ _('Orange', 'joke response') _('Orange', context='joke response')
You’ll need to switch these to the Django pgettext calls:
from django import pgettext pgettext('joke response', 'Orange')
Note that the arguments are reversed!
https://docs.djangoproject.com/en/1.8/ref/utils/#django.utils.translation.pgettext
If your test suite covers all code paths that have gettext calls, then you can run your test suite and it should error out because Tower’s gettext and ngettext had an extra argument that Django’s do not.
[1] Orange who? Orange you glad this example was here to lighten the mood?
At the end of this step, you do not want to be using Tower’s gettext at all and none of your msgids should have changed.
You can tell whether msgids have changed by running:
./manage.py extract
And diffing the results.
Make sure
autoescape=True
in your Jinja2 environment.In your Django settings,
JINJA_CONFIG
should have this:JINJA_CONFIG = { 'autoescape': True, # ... }
Warning
Switching autoescape may cause strings with HTML in them to change. If that happens, you can use the
|safe
filter to undo the escaping.Switch from Tower to Puente.
Puente works with Django 1.7 and Jingo 0.7.1. It also works with Django 1.8+ and django-jinja. It probably works with other Django Jinja2 template environments. If not, let us know.
Remove Tower
Add Puente
Make the configuration changes
Tower configuration probably looks something like this:
# in settings.py DOMAIN_METHODS = { 'django': [ ('fjord/**.py', 'tower.tools.extract_tower_python'), ('fjord/**.html', 'tower.tools.extract_tower_template'), ], 'djangojs': [ ('**.js', 'javascript') ] }
The equivalent Puente configuration is something like this:
# in settings.py PUENTE = { 'BASE_DIR': BASE_DIR, 'DOMAIN_METHODS': { 'django': [ ('fjord/**.py', 'python'), ('fjord/**.html', 'jinja2'), ], 'djangojs': [ ('**.js', 'javascript') ] } }
If you have a more complex Tower configuration than that, hop on
#puente
onirc.mozilla.org
and we’ll work it out.Add the code to install ugettext/ungettext into the Jinja environment.
Jingo installs gettext/ngettext functions that don’t do anything. You will need to install Django’s gettext/ngettext functions into the environment.
Calling this during webapp bootstrap will fix that:
def install_jinja_translations(): """Install gettext functions into Jingo's Jinja2 environment""" from django.utils import translation import jingo jingo.env.install_gettext_translations(translation, newstyle=True)
Warning
Note that Tower does NOT use Jinja2’s newstyle gettext. In this step, you need to switch to the newstyle gettext since the combination of newstyle gettext and autoescape=True will give you the correct output for gettext functions.
When you push the update, make sure to nix your Jinja2 template cache.