Our Blog

0

As much as you want to offer localization for your project in your templates, you also want to offer that in your Javascript. When a project supports Spanish you don’t want any alerts from Javascript to be in English, comprendo?
There are several ways to accomplish that, but it would be easy for your translation-team to have all texts in the same PO file, and easy for you to have it all in the same MO file 🙂

Having that done in Django is fairly straightforward: Create a .JS file, as if it was any other document Django renders for you. You have rendered HTML and probably JSON documents before. We build a Javascript document in the very same way. This .JS file contains all the strings you’re using in the client-side of your project. Include this file on all, or the required pages, and all your scripts will be able to access its variables, containing your texts – in Spanish.

Get busy!

Let’s start where everything starts in Django; the URLs:

myproject/urls.py

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',

    # Admin
    url(r'^admin/', include(admin.site.urls)),

    # JS locale
    url(r'^js/locale.js$', 'myproject.views.js_locale', name='js-locale'),

    # etc your apps here etc
)

In other words: “/js/locale.js” will be rendered by this view: “myproject.views.js_locale”. Let’s create that view:

myproject/views.py

from django.shortcuts import render, render_to_response
from django.template import RequestContext
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.cache import cache_page

@cache_page(60 * 60 * 24)
def js_locale(request, template_name = 'js/vars.js', **kwargs):
    '''
    Javascript document
    containing strings for
    the client-side scripts
   
    '''

    vars = [
        ['var SK',                        'SK || {}', False],
        ['SK.text',                       'SK.text || {}', False],
        ['SK.text.rusure_cancel_booking', _("Are you sure that you want to cancel this booking?"), True],
        ['SK.text.error_occorred',         _("An error occurred"), True],
    ]
    return render_to_response(template_name,
                              {
                                'vars' : vars,
                              },
                              mimetype="text/javascript")

You can see what I’m intending to do: Apart from the document is heavily cached 🙂 I’m sending definitions to a template which looklike they can become Javascript.
In the end the document gets the header “text/javascript”, so everybody knows what we’re talking about.

Let’s have a look at the template:

myproject/templates/js/vars.js

{% for var in vars %}{{ var.0 }}={% if var.2 %}"{% endif %}{{ var.1|escapejs }}{% if var.2 %}"{% endif %};{% endfor %}

So we’re looping over the list where each item (also) is a list with three indexes:

  • 0: variable definition
  • 1: variable value
  • 2: boolean; quote the value (for strings) or not

Accessing the URL “/js/locale.js” will now provide you this:

var SK = SK || {};
SK.text = SK.text || {};
SK.text.rusure_cancel_booking = "Are you sure that you want to cancel this booking?";
SK.text.error_occorred = "An error occurred";

Let’s include that file in our document template (or where ever you need it):

myproject/templates/doc.html

{% load static %}

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">

    <title>What you hear is not a ....</title>

    <script src="{% url 'myproject.views.js_locale' %}" type="text/javascript"></script>
    <script src="{% static 'assets/js/scripts.js' %}" type="text/javascript"></script>
</head>
    <body>
        {% block markup %}
        {% endblock %}
    </body>
</html>

From now on you can access those translated texts from any script.
For example “assets/js/scripts.js” could now display a confirm in Spanish, when translated:

// stuff above

if( confirm( SK.text.rusure_cancel_booking ) ){
    cancel_booking();
}

// stuff below

Keep DRY, not dirty

Everything works! But…
As your texts will grow while proceeding your client-side scripts, it will pollute your view with a lot of strings, who don’t really belong there. So let’s move those to a separate file. My approach is to put all the ‘common’ texts in there, and let the text-namespace be defined in the view (or perhaps several views).

myproject/views.py

from django.shortcuts import render, render_to_response
from django.template import RequestContext
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.cache import cache_page

# note this new import
from js_locale import sk_js_vars

@cache_page(60 * 60 * 24)
def js_locale(request, template_name = 'js/vars.js', **kwargs):
    '''
    Javascript document
    containing strings for
    the client-side scripts

    '''

    namespacing = [
        ['var SK',  'SK || {}', False],
        ['SK.text', 'SK.text || {}', False],
    ]
    vars = namespacing + cw_js_vars
    return render_to_response(template_name,
                              {
                                'vars' : vars,
                              },
                              content_type="text/javascript")

myproject/js_locale.py

from django.utils.translation import ugettext_lazy as _

'''
[name, value, value must be quoted or not]
'''


sk_js_vars = [
    ['SK.text.save',                    _("Save"), True],
    ['SK.text.delete',                  _("Delete"), True],
    ['SK.text.add',                     _("Add"), True],
    ['SK.text.remove',                  _("Remove"), True],
    ['SK.text.confirm',                 _("Confirm"), True],
    ['SK.text.confirmed',               _("Confirmed"), True],
    ['SK.text.decline',                 _("Decline"), True],
    ['SK.text.declined',                _("Declined"), True],
    ['SK.text.pending',                 _("Pending"), True],
    ['SK.text.unsaved',                 _("Unsaved"), True],
    ['SK.text.not_invited_yet',         _("Not invited yet"), True],
    ['SK.text.awaiting_invitation',     _("Awaiting invitation"), True],
    ['SK.text.rusure_cancel_booking',   _("Are you sure that you want to cancel this booking?"), True],
    ['SK.text.rusure_make_booking',     _("Are you sure that you want to make this booking?"), True],
    ['SK.text.error_occured',           _("An error occured"), True],
    ['SK.text.could_not_save_booking',  _("Could not save booking"), True],
    ['SK.text.not_enough_credits',      _("You don't have enough credits for this."), True],
    ['SK.text.buy_credits',             _("Buy credits"), True],
    ['SK.text.space_n_location',        _("Space and location"), True],
    ['SK.text.location',                _("Location"), True],
    ['SK.text.for',                     _("for"), True],
    ['SK.text.hours',                   _("hours"), True],
    ['SK.text.at',                      _("at"), True],
    ['SK.text.until',                   _("until"), True],
    ['SK.text.error',                   _("Error"), True],
    ['SK.text.now',                     _("Now"), True],
    ['SK.text.max_exceeded',            _("You have exceeded the allowed maximum"), True],
    ['SK.text.invalid_email',           _("E-mail address not valid"), True],
    ['SK.text.double_entry',            _("Double entry"), True],
]