Opened 8 years ago

Closed 3 years ago

Last modified 3 years ago

#6195 closed New feature (fixed)

Allow caching of the javascript translation library by the client browser

Reported by: jdetaeye Owned by: aaugustin
Component: Documentation Version: master
Severity: Normal Keywords: i18 javascript
Cc: charette.s@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

A i18 javascript is dynamically generated by django. The script is recreated with every (admin) view using this library, which doesn't make much sense: the same translation dictionary is returned over and over again to the user, increasing the response times and wasting cpu and bandwidth.

This patch allows the client browser to cache this dictionary locally.

Caveats:

  • If a user switches languages on the fly or uses multiple languages at the same time, the javascript library isn't updated automatically to the new language. (A more advanced patch could use the Etag header to cover this case)
  • I put a hard-coded value of one day as the maximum time the user's browser can cache the page. This could be made a setting, with a default of infinite?

I'm putting the ticket in 'design decision required' for the core team to evaluate the usefulness of a patch like this and a choosing the good design for a more solid patch.

Attachments (1)

i18js_caching.patch (592 bytes) - added by jdetaeye 8 years ago.
simple patch

Download all attachments as: .zip

Change History (17)

Changed 8 years ago by jdetaeye

simple patch

comment:1 Changed 8 years ago by jdetaeye

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement set
  • Triage Stage changed from Unreviewed to Design decision needed

comment:2 Changed 8 years ago by arien

There isn't much of a case for admin views for this change, I think.

For any non-admin view this can be done using the django.views.decorators.http.conditional_page, django.views.decorators.cache.cache_page and django.views.decorators.vary.vary_on_headers decorators AFAICT. Maybe wrapping the last two up in e.g. django.views.decorators.i18n.cached would be an idea?

(Obviously, the same decorators could be used for the admin's i18n_view as well.)

You could try the group and see what people there think.

comment:3 follow-up: Changed 7 years ago by mtredinnick

You certainly have to work around the first problem you mention. If the user changes the language, the translations they see must all be updated correctly.

comment:4 Changed 7 years ago by mtredinnick

  • Triage Stage changed from Design decision needed to Someday/Maybe

comment:5 in reply to: ↑ 3 Changed 6 years ago by ramiro

Replying to jdetaeye:

Caveats:

  • If a user switches languages on the fly or uses multiple languages at the same time, the javascript library isn't updated automatically to the new language. (A more advanced patch could use the Etag header to cover this case)

Replying to mtredinnick:

You certainly have to work around the first problem you mention. If the user changes the language, the translations they see must all be updated correctly.

#5691 proposes adding the language name to the cache key.

comment:6 Changed 4 years ago by gabrielhurley

  • Severity set to Normal
  • Type set to New feature

comment:7 Changed 4 years ago by aaugustin

  • UI/UX unset

Change UI/UX from NULL to False.

comment:8 Changed 4 years ago by aaugustin

  • Easy pickings unset

Change Easy pickings from NULL to False.

comment:9 Changed 3 years ago by aaugustin

  • Resolution set to wontfix
  • Status changed from new to closed

I investigated this issue a few months ago for a project of mine.

I found three non-exclusive ways to decorate javascript_catalog to provide caching.

For providing translations in more than one language, the easiest is to include the language code in the URL with i18n_patterns.

1) Server-side caching

key_prefix = 'js18n-%s' % get_version()
@cache_page(86400, key_prefix=key_prefix)

You need a get_version() function that returns a different result whenever the translations change. In production, this isn't difficult, but it depends on your deployment strategy, making it hard for Django to provide a generic solution. In development, you have to use the dummy cache to avoid stale translations.

The full translations catalog will be resent every time, but you'll save the generation cost.

2) Client-side caching with If-Modified-Since:

last_modified_date = timezone.now()
@last_modified(lambda req, **kw: last_modified_date)

last_modified_date changes whenever the server is reloaded (in production, because the code was updated; in development, because the dev server auto-reloaded). It's even better if you can set last_modified_date to the software release date, but that's a bit more difficult to get right than version numbers.

3) Client-side caching with Etag:

etag_value = 'js18n-%s' % get_version()
@etag(lambda req, **kw: etag_value)

In production, this is perfect. Unfortunately, it doesn't work well in development, because it's hard to generate an etag_value that's guaranteed to change when you just edit .po and .mo files.


Neither of these techniques can provide proper cache invalidation without application-supplied information about the software version. Therefore, I think it's best to leave the implementation of caching to application developers. The cache framework provides tools to add caching with one-liners.

comment:10 Changed 3 years ago by charettes

  • Cc charette.s@… added

comment:11 Changed 3 years ago by aaugustin

  • Component changed from Internationalization to Documentation
  • Resolution wontfix deleted
  • Status changed from closed to new
  • Triage Stage changed from Someday/Maybe to Accepted

At the Utrecht sprint we agreed that it's be a good idea to suggest caching in the docs.

Requalifying as a docs ticket and reopening.

comment:12 Changed 3 years ago by aaugustin

  • Owner changed from nobody to aaugustin
  • Status changed from new to assigned

comment:13 Changed 3 years ago by michi88

Added docs as discussed on the django sprint (Utrecht)
pull request here: https://github.com/django/django/pull/779

As discussed I am not referencing https://pypi.python.org/pypi/django-statici18n but it is a great way to pre-generate the files and serve them as static.
I use this for my own projects.

comment:14 Changed 3 years ago by aaugustin

Thanks for your work on this patch!

I'd like to keep this digression on caching relatively short. I'll summarize a bit.

The BDFL lifted the restriction on linking to third-party projects. I'll restore the link to django-statici18n.

comment:15 Changed 3 years ago by Aymeric Augustin <aymeric.augustin@…>

  • Resolution set to fixed
  • Status changed from assigned to closed

In 0836670c5cd8bb17322504c46e07d3944add63c3:

Fixed #6195 -- Documented caching options for javascript_catalog.

comment:16 Changed 3 years ago by Aymeric Augustin <aymeric.augustin@…>

In 1742fd08f5e2f7353b67b58b4ef9185121ce300e:

[1.5.x] Fixed #6195 -- Documented caching options for javascript_catalog.

Backport of 0836670 from master

Note: See TracTickets for help on using tickets.
Back to Top