carlxaeron / django-rosetta

Automatically exported from code.google.com/p/django-rosetta
MIT License
0 stars 0 forks source link

Solution to show translation changes live. #55

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
I found a way to automatically reload translation files for each server.
Maybe it should be part of Rosetta itself.

It's a simple middleware that deletes the translation cache for every request:

    class ResetTranslationMiddleware(object):

        def process_request(self, request):
            from django.utils.translation import trans_real
            from django.utils.thread_support import currentThread
            trans_real._translations = {}
            trans_real._default = None
            trans_real._accepted = {}
            trans_real._active.pop(currentThread(), None)

That the attributes are private might make this harder to maintain of course,
but adding this is a reset_cache() in django itself might be an eventual 
solution.

Original issue reported on code.google.com by askso...@gmail.com on 27 Aug 2009 at 1:52

GoogleCodeExporter commented 8 years ago
This is actually an excellent idea, except:

* You don't want this middleware to run on production, ever. I might have to 
find a way of checking this.
* It doesn't work :(

Here is how I tested:

* added the middlware to my settings, before the 
django.middleware.locale.LocaleMiddleware
* loaded a german page with untranslated strings (i.e. still in english)
* translated them in Rosetta. On the next request, the middleware should unload 
the content of trans_real 
and reload it
* reloaded the page with a german locale. -> strings aren't translated.
* restarted the server -> strings are translated

Am I missing something?

Original comment by mbonetti on 27 Aug 2009 at 2:13

GoogleCodeExporter commented 8 years ago
@mbonetti:

> You don't want this middleware to run on production, ever.

We have an internal server for translators, running it in production is 
definitely a bad idea.

>  It doesn't work :(

Could you try to add it after the LocaleMiddleware? (even last, maybe)

Original comment by askso...@gmail.com on 27 Aug 2009 at 2:26

GoogleCodeExporter commented 8 years ago
If I set it after the LocaleMiddleware I'm no longer able to change languages 
using:

url(r'^i18n/', include('django.conf.urls.i18n')),

Original comment by mbonetti on 27 Aug 2009 at 2:37

GoogleCodeExporter commented 8 years ago
Ok. We don't use setlang/set_language, (i'm not sure what we use yet to change 
the language, actually, have to 
ask around). Either way, it's probably not that hard to fix. Even if you're not 
able to use setlang, does it display 
changes?

Original comment by askso...@gmail.com on 27 Aug 2009 at 3:02

GoogleCodeExporter commented 8 years ago
Could you try to remove the line that says:

    trans_real._accepted = {}

I wasn't sure if it was needed, and I think it could affect set_language

Original comment by askso...@gmail.com on 27 Aug 2009 at 3:11

GoogleCodeExporter commented 8 years ago
Nope: it just sticks to the default english language setting and english 
translations.

I also tried to clean trans_real on process_response instead of 
process_request, but to no avail: I get exactly the 
same results.

Original comment by mbonetti on 27 Aug 2009 at 3:19

GoogleCodeExporter commented 8 years ago
What version of django are you using?

Original comment by askso...@gmail.com on 27 Aug 2009 at 3:21

GoogleCodeExporter commented 8 years ago
> Could you try to remove the line that says:  trans_real._accepted = {}

No change, same result, still cannot change languages.

> What version of django are you using?

Trunk (rev 11477)

Original comment by mbonetti on 27 Aug 2009 at 3:24

GoogleCodeExporter commented 8 years ago
Ok. You we're right indeed. The reason I thought it was working, was because 
Apache launched a new process 
*blush*.

This seems to work for me:

    class ResetTranslationMiddleware(object):

        def process_request(self, request):
            reset_translation_cache()

    def reset_translation_cache():
        from django.utils.translation import trans_real
        from django.utils.thread_support import currentThread
        import gettext
        gettext._translations = {}
        trans_real._translations = {}
        trans_real._default = None
        prev = trans_real._active.pop(currentThread(), None)
        if prev:
            trans_real.activate(prev.language())

It should even re-select any previously activated language.

Original comment by askso...@gmail.com on 27 Aug 2009 at 4:01

GoogleCodeExporter commented 8 years ago
Hmm, will this work with any webserver (including the development server) and 
module (mod_python, 
mod_wsgi) etc... ?

Original comment by mbonetti on 28 Aug 2009 at 8:31

GoogleCodeExporter commented 8 years ago
I've only tested it with mod_wsgi, but I can't see any reason why it wouldn't 
work with mod_python/runserver.
Guess it would need some testing.

Since it's using "private" attributes, it could maybe handle errors gracefully 
by wrapping it into a try: block 
which is catching AttributeErrors.

class ResetTranslationMiddleware(object):

    def process_request(self, request):
    reset_translation_cache()

def reset_translation_cache():
    from django.utils import translation
    from django.utils.translation import trans_real
    from django.utils.thread_support import currentThread
    from django.conf import settings
    import gettext
    if settings.USE_I18N:
        try:
            # Reset gettext.GNUTranslation cache.
            gettext._translations = {}

            # Reset Django by-language translation cache.
            trans_real._translations = {}

            # Delete Django current language translation cache.
            trans_real._default = None

            # Delete translation cache for the current thread,
            # and re-activate the currently selected language (if any)
            prev = trans_real._active.pop(currentThread(), None)
            if prev:
                translation.activate(prev.language())
        except AttributeError:
            pass

Or instead of silently ignoring the error, you could raise an exception saying 
something like,
"You should upgrade to the latest django-rosetta"

Btw, I have also made a proper setup.py for django-rosetta so it can be 
uploaded as a PyPI package if you're 
interested. We use an internal PyPI server, but it would be very convenient if 
it was available through the 
standard python package distribution system.

Also, why don't you host it at github or bitbucket? It's so much easier to 
contribute that way :)

Original comment by askso...@gmail.com on 28 Aug 2009 at 8:52

GoogleCodeExporter commented 8 years ago
Oh, another thing. We could make it so writing a change in the rosetta 
interface sets a flag, so the translation 
cache is only reset if there has been a change.

Original comment by askso...@gmail.com on 28 Aug 2009 at 8:56

GoogleCodeExporter commented 8 years ago
re: setup.py, what is wrong with the one I wrote? 
http://code.google.com/p/django-
rosetta/source/browse/trunk/setup.py 

By the way, Rosetta has been on PyPI for months.

Also, you mention you're running mod_wsgi. Have you tried the WSGI_AUTO_RELOAD 
flag from Rosetta's 
conf/settings.py ? It basically does what you're trying to achieve, but in a 
much more robust and elegant way, 
since the contents of the catalogs are reloaded *only when they have been 
modified* and not on each request.  

Original comment by mbonetti on 28 Aug 2009 at 9:08

GoogleCodeExporter commented 8 years ago
Yeah, I've had too many problems with autoreload in the past, didn't really 
want to go down that route.

Great that you've created a setup.py.

    ask@pc189:~$> pip install django-rosetta
    Downloading/unpacking django-rosetta
      Could not find any downloads that satisfy the requirement django-rosetta
    No distributions at all found for django-rosetta
   Storing complete log in ./pip-log.txt

Thanks for the software, it's a great solution.

Original comment by askso...@gmail.com on 28 Aug 2009 at 9:26

GoogleCodeExporter commented 8 years ago
I fixed the setup.py auto-install feature (in issue 56). 

I think I'll stick to the currently working wsgi auto-reload mechanism, as your 
method looks a wee to hacky to 
me. I'll keep an eye out for this ticket and will give it a second try if need 
be.

Thanks for your hints, though.

Original comment by mbonetti on 1 Sep 2009 at 5:39