I'm trying to add support for arabic languages to a site I'm working on, so currently my templates are like
/templates
/accounts/
/includes/
/rtl
/accounts/
/includes/
...
...
django-allauth will try to load the templates from /templates/accounts
no matter what (it's hardcoded in their views) but I want to load the RTL (Right To Left) version based on a context variable when necessary so I thought about four solutions, but none of them seem to be good enough to me (I'm not saying that I won't do them, I'm saying that I can't work out a better way, if there is one)
Set a conditional in my overrides to load the LTR or RTL versions (that will require the conditional in /templates/accounts
, a LTR version with the template somewhere else and the RTL version in /templates/rtl/accounts
.
Create a template tag which has a parameter with the template name and loads the template dynamically, this looks like a waste of resources
Create a mess in the main templates with tons of logic so it switches between LTR and RTL when needed (that will need tons of logic in the template, which is not good)
Fork allauth into my project and add the logic to the views. I really don't want to do this, because it will make maintenance a hell in the future.
I'm not using the standard django i18n, so I can't use the BIDI settings.
Anyone has a better approach to this?
Solved it, in the end I had to use a custom template loader to change the template directoy on the fly at the moment of the request. I followed a really useful tutorial from the guys at the washington times: http://opensource.washingtontimes.com/blog/2010/feb/17/loading-templates-based-request-headers-django/
I don't find really fancy the idea of creating a local thread to store the request (in my case a context value) but it seems that's the only way to actually pass data to the template loader.
My code:
< projectname >/settings/defaults.py
We load our template loader FIRST, so we can process the data before django loads anything. If something fails in our code, it will fallback to the default django loaders.
TEMPLATE_LOADERS = (
'projectname.templateloaders.arabic.load_template_source',
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
#'django.template.loaders.eggs.Loader',
)
< projectname >/middleware/templaterequest.py
# -*- coding: utf-8 -*-
try:
from threading import local
except ImportError:
from django.utils._threading_local import local
from apps.locales.models import Locale
from apps.markets.models import Market
# for more info:
# http://opensource.washingtontimes.com/blog/2010/feb/17/loading-templates-based-request-headers-django/
_thread_locals = local()
def get_current_request():
return getattr(_thread_locals, 'arabic', None)
class RequestMiddleware(object):
"""
This middleware will store the market.rtl value at each request that is made.
Basically here you can do anything that you can get from the request.
In this case we get the locale and the market settings and extract the RTL
value, which we store in a local thread in memory an will be retrieved
later by the template loader when calling for get_current_request()
"""
def process_request(self, request):
site = request.META['HTTP_HOST']
locale = Locale.objects.get(site=site)
market = Market.objects.get(locale=locale)
_thread_locals.arabic = market.rtl
< projectname >/templateloaders/arabic.py
# -*- coding: utf-8 -*-
from django.conf import settings
from django.template.loader import BaseLoader, TemplateDoesNotExist
from django.utils._os import safe_join
from tipx.middleware.templaterequest import get_current_request
def get_template_sources(template_name, template_dirs=None):
"""
This class will modify the template directory in case the market has
RTL activated in the market settings. If RTL if False it will pass and
let the standard django template loaders to work.
Explanation of how it behaves (it's weird...) the request comes trough and
hits first our code, tries to determine if the market is arabic or not.
It it's arabic it changes the template directory to /rtl/, but for example
third party templates are not there (except for the overrides), so it will
continue processing through the template loaders until it finds the right
template. This guarantees that no matter how twisted our template locations
are, it will always load the right templates in the right moment even
when the templates have includes from RTL to english.
"""
arabic = get_current_request()
if arabic:
# Loop through the template dirs
for directory in settings.TEMPLATE_DIRS:
new_directory = directory + '/rtl/'
yield safe_join(new_directory, template_name)
def load_template_source(template_name, template_dirs=None):
for filepath in get_template_sources(template_name, template_dirs):
try:
file = open(filepath)
try:
return (file.read().decode(settings.FILE_CHARSET), filepath)
finally:
file.close()
except IOError:
pass
raise TemplateDoesNotExist(template_name)
load_template_source.is_usable = True
The last thing is to have our RTL templates folder. If any of your templates have includes, you will need to append the foldername that you have for your arabic version, example:
Original include:
{% include 'whatever/template.html' %}
RTL include:
{% include 'rtl/whatever/template' %}
If someone finds this answer incomplete just tell me! :)