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],
]