Making translations appear without server restart?

330 views
Skip to first unread message

James

unread,
Dec 19, 2007, 6:14:44 PM12/19/07
to Django users
Hello,

I have developed a Django application that provides an interface for
editing gettext message files in a Django project. It accepts
translations from English to a different language, merges those
translations with the existing message catalog, then does a recompile
on the message files. Unfortunately, the new translations don't
immediately show in the site pages; it seems a server restart is
required.

I tried calling translation.deactivate_all() and then calling
translation.activate(<translation language >) from my code but
apparently only a restart does the trick.

Does anyone know how to make Django's i18n machinery pick up new
translations as soon as they are generated?

Thanks,

James

Rajesh Dhawan

unread,
Dec 19, 2007, 7:07:02 PM12/19/07
to Django users
Hi,

> I tried calling translation.deactivate_all() and then calling
> translation.activate(<translation language >) from my code but
> apparently only a restart does the trick.
>
> Does anyone know how to make Django's i18n machinery pick up new
> translations as soon as they are generated?

Try something like this. Hopefully this will clear out the global
translations and force a reload:

translation.deactivate_all()
globals _translations
_translations = {}
translation.activate(<translation language >)

See django.utils.translation.trans_real for more.

-Rajesh

Rajesh Dhawan

unread,
Dec 19, 2007, 7:28:50 PM12/19/07
to Django users
Hi again,

>
> Try something like this. Hopefully this will clear out the global
> translations and force a reload:
>
> translation.deactivate_all()
> globals _translations
> _translations = {}
> translation.activate(<translation language >)
>
> See django.utils.translation.trans_real for more.

On second thoughts, I think you will need to do more work than this
since this will only force a reload of the translations within the
current process. Other processes will have their own global
_translations cache and without further work those won't get reloaded.
If you try the above idea and it seems to work for one process (the
process of the request/view in which you run the above code), consider
the following:

Maintain the timestamp (say, catalog_change_timestamp) of when the
translation catalog last changed in a resource that all processes can
access (e.g. memcache, DB, or filesystem). Secondly, modify the above
snippet like this:

translation.deactivate_all()
globals _translations
_translations = {}
from datetime import datetime
globals _translations_loadtime
_translations_loadtime = datetime.now()
translation.activate(<translation language >)

Lastly, add a custom middleware that checks the global
_translations_loadtime against catalog_change_timestamp and rerun the
above snippet. This should reset the translations in every process
that has not yet loaded your modified language catalog.

FAT DISCLAIMER: I have not tested any of this and am not at all sure
if this work. So, please take it with lots of salt.

-Rajesh

James

unread,
Dec 19, 2007, 8:12:50 PM12/19/07
to Django users


On Dec 19, 4:07 pm, Rajesh Dhawan <rajesh.dha...@gmail.com> wrote:
> Hi,
>
> > I tried calling translation.deactivate_all() and then calling
> > translation.activate(<translation language >) from my code but
> > apparently only a restart does the trick.
>
> > Does anyone know how to make Django's i18n machinery pick up new
> > translations as soon as they are generated?
>
> Try something like this. Hopefully this will clear out the global
> translations and force a reload:
>
> translation.deactivate_all()
> globals _translations
> _translations = {}
> translation.activate(<translation language >)
>

Thanks, Rajesh, but this doesn't work.

You've put me on the right track though: I just need to find a
reliable way to clear out that _translations global. With the above
code, printing out the _translations dictionary inside the activate()
method shows it to still be populated, which prevents the
translation() method from reloading the message files from disk.

James

James

unread,
Dec 19, 2007, 8:27:04 PM12/19/07
to Django users
Sorry, I replied to your first post before I saw this one..

On Dec 19, 4:28 pm, Rajesh Dhawan <rajesh.dha...@gmail.com> wrote:
> Hi again,
> On second thoughts, I think you will need to do more work than this
> since this will only force a reload of the translations within the
> current process. Other processes will have their own global
> _translations cache and without further work those won't get reloaded.
> If you try the above idea and it seems to work for one process (the
> process of the request/view in which you run the above code), consider
> the following:

As I mentioned in my other post, it doesn't work for the one process.

I don't know if I really need to bother with the timestamp as you
outline below. I want the message files reloaded no matter what
whenever someone submits new translations using my app.

I'm probably understanding Python's 'global' declaration imperfectly,
but from the print statements I have littered about in both my code
and Django's, the _translations object I'm referring to in my view
code doesn't seem to be the same object being referred to in
trans_real.py.

still looking,
James

Rajesh Dhawan

unread,
Dec 19, 2007, 10:47:02 PM12/19/07
to Django users
> As I mentioned in my other post, it doesn't work for the one process.

My previous code snippet missed an important statement. You will need
to first get an instance of _transactions from trans_real.py before
you declare it to be global. So, check if something like this helps:

from django.utils import translation
translation.deactivate_all()
_translations = translation.trans_real._translations # <--- Note this
globals _translations
_translations = {}
from datetime import datetime
globals _translations_loadtime
_translations_loadtime = datetime.now()
translation.activate(<translation language >)


>
> I don't know if I really need to bother with the timestamp as you
> outline below. I want the message files reloaded no matter what
> whenever someone submits new translations using my app.

In a production setting, you might have Django running under multiple
long running processes (each servicing an HTTP request at a time.)
Each of these processes has its own instance of the _translations
dictionary in question. So resetting it in one process won't be
sufficient. You need to somehow indicate to all processes that the
catalog needs to be reloaded. You could reload the catalog every time
by clearing out _translations on every request, but that's going to
affect performance. It's not optimal because you would only want a
reload when a translation has changed and a given process has not yet
loaded this changed translation. That's why I was suggesting the
timestamp approach (I am sure there are other ways to achieve that.)

>
> I'm probably understanding Python's 'global' declaration imperfectly,
> but from the print statements I have littered about in both my code
> and Django's, the _translations object I'm referring to in my view
> code doesn't seem to be the same object being referred to in
> trans_real.py.

It should be since you've i18n enabled. Django uses a nifty lazy
loading mechanism that dynamically switches between two different
implementations (trans_real and trans_null) depending on whether
you've i18n activated in your settings or not. See __init__.py in
django.utils.translation.

James Bennett has a nice article about server startup in which one
section talks about registries. It might be worth it to peruse that
even though it doesn't directly address this problem.

See: http://www.b-list.org/weblog/2007/nov/05/server-startup/

-Rajesh

James

unread,
Dec 20, 2007, 7:05:37 PM12/20/07
to Django users
On Dec 19, 7:47 pm, Rajesh Dhawan <rajesh.dha...@gmail.com> wrote:
> > As I mentioned in my other post, it doesn't work for the one process.
>
> My previous code snippet missed an important statement. You will need
> to first get an instance of _transactions from trans_real.py before
> you declare it to be global. So, check if something like this helps:
>
> from django.utils import translation
> translation.deactivate_all()
> _translations = translation.trans_real._translations # <--- Note this
> globals _translations
> _translations = {}
> from datetime import datetime
> globals _translations_loadtime
> _translations_loadtime = datetime.now()
> translation.activate(<translation language >)
>

Tried the above, but _translations is still getting repopulated
somewhere and won't pick up the new translations.

> <snip>
> James Bennett has a nice article about server startup in which one
> section talks about registries. It might be worth it to peruse that
> even though it doesn't directly address this problem.
>
> See:http://www.b-list.org/weblog/2007/nov/05/server-startup/

Useful information, thanks.
James
>
> -Rajesh
Reply all
Reply to author
Forward
0 new messages