Switch language in jinja template - django

I'm migrating a multi lingual Django application from Django's template engine to Jinja2. In the templates I currently switch the active language on a per object basis using Django's language template tag i.e.:
{% load i18n %}
<h1>{% trans 'Page title' %}</h1>
<ul>
{% for obj in object_list %}
{% language obj.language_code %}
<li>{% trans 'view' %}: {{ obj.title }}
{% endlanguage %}
{% endfor %}
</ul>
We also use i18n_patterns so the urls of each object are language specific as well.
I'm stuck on how to convert this to Jinja. I cannot use Django's i18n template tags and cannot find something equivalent for Jinja.
I was also looking at Babel to help with extracting messages from the templates. So a solution that works with Babel as well as with Django would be preferred.

It turns out it's fairly simple to do this by writing a custom jinja2 extension (I've based this on the example in the jinja2 docs):
from django.utils import translation
from jinja2.ext import Extension, nodes
class LanguageExtension(Extension):
tags = {'language'}
def parse(self, parser):
lineno = next(parser.stream).lineno
# Parse the language code argument
args = [parser.parse_expression()]
# Parse everything between the start and end tag:
body = parser.parse_statements(['name:endlanguage'], drop_needle=True)
# Call the _switch_language method with the given language code and body
return nodes.CallBlock(self.call_method('_switch_language', args), [], [], body).set_lineno(lineno)
def _switch_language(self, language_code, caller):
with translation.override(language_code):
# Temporarily override the active language and render the body
output = caller()
return output
# Add jinja2's i18n extension
env.add_extension('jinja2.ext.i18n')
# Install Django's translation module as the gettext provider
env.install_gettext_translations(translation, newstyle=True)
# Add the language extension to the jinja2 environment
environment.add_extension(LanguageExtension)
With this extension in place switching the active translation language is pretty much exactly like how you'd do it in Django:
{% language 'en' %}{{ _('Hello World'){% endlanguage %}
The only caveat is that when using Django as a gettext provider and Babel as a message extractor it's important to tell Babel to set the message domain to django when running init/update/compile_catalog.

I have this code snippet to switch between languages in jinja2.
def change_lang(request, lang=None, *args, **kwargs):
"""
Get active page's url by a specified language, it activates
Usage: {{ change_lang(request, 'en') }}
"""
path = request.path
url_parts = resolve(path)
url = path
cur_language = get_language()
try:
activate(lang)
url = reverse(url_parts.view_name, kwargs=url_parts.kwargs)
finally:
activate(cur_language)
return "%s" % url
in settings.py
TEMPLATES = [
{
"BACKEND": "django_jinja.backend.Jinja2",
'DIRS': [
os.path.join(BASE_DIR, 'templates/jinja'),
],
"OPTIONS": {
# Match the template names ending in .html but not the ones in the admin folder.
"match_extension": ".html",
"match_regex": r"^(?!admin/).*",
"newstyle_gettext": True,
"extensions": [
"jinja2.ext.do",
"jinja2.ext.loopcontrols",
"jinja2.ext.with_",
"jinja2.ext.i18n",
"jinja2.ext.autoescape",
"django_jinja.builtins.extensions.CsrfExtension",
"django_jinja.builtins.extensions.CacheExtension",
"django_jinja.builtins.extensions.TimezoneExtension",
"django_jinja.builtins.extensions.UrlsExtension",
"django_jinja.builtins.extensions.StaticFilesExtension",
"django_jinja.builtins.extensions.DjangoFiltersExtension",
],
'globals': {
'change_lang': 'drug.utils.change_lang'
},
"bytecode_cache": {
"name": "default",
"backend": "django_jinja.cache.BytecodeCache",
"enabled": False,
},
"autoescape": True,
"auto_reload": DEBUG,
"translation_engine": "django.utils.translation",
"context_processors": [
"dashboard.context_processors.auth",
# "django.template.context_processors.debug",
"django.template.context_processors.i18n",
# "django.template.context_processors.media",
# "django.template.context_processors.static",
# "django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
]
}
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
]
},
},]
and then you can use this in anywhere in your templates {{ _('Hello World') }}

Related

django can't find template when trying to use forms

I am trying to follow the django documentation howto create a form using sjango form class. I am doing every step but when trying to access my form it says that is cannot find the template:
django.template.exceptions.TemplateDoesNotExist: lform
heres is my forms.py:
class LoginForm(Form):
uname=CharField(label='user name',max_length=20)
passw=CharField(label='password', max_length=20)
the template lform.html:
<form action="ulogin" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
and my view. I think the problem is in the render function:
def testform(request):
if request.method=='POST':
form=LoginForm(request.POST)
if form_is_valid():
return render('index')
else:
HttpResponseRedirect('index')
else:
form=LoginForm()
return render(request,'lform',{'form':form})
and url.py:
path('lform',views.testform,name='lform')
the last line in the view function,
return render(request,'lform',{'form':form})
gives the error any suggestions?
ok, the error was simply that the path to templates was missing somehow. I moved the template further up in the directory and now it displays the form. Thanks for all answers.
You are trying to render the template lform. Your template is lform.html.
You need to provide complete path to your template relative with your template path settings.
Correct your template name:
def testform(request):
if request.method=='POST':
form=LoginForm(request.POST)
if form_is_valid():
return render('index')
else:
HttpResponseRedirect('index')
else:
form=LoginForm()
return render(request,'lform.html',{'form':form})
In your settings.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ["templates"],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

Django & Jinja2 templates using {{ url() }}

I am trying to figure out how to pass my user_id within my html using jinja's {{ url() }}, using urls that don't need any id like /dashboard/ work fine but I need to pass an id to this- example: /user/3 . I have tried the following with no success:
{{ url('detail') }}
{{ url('detail', user_id=User.id) }}
{{ url('detail', User.id) }}
Here's part of my views and html:
views.py
urlpatterns = [
path('dashboard/', dashboard, name='dashboard'),
path('user/<int:user_id>/', detail, name='detail'),
]
dashboard.html
{% for User in all_users %}
{{ url('detail') }}
{% endfor %}
Any help on this would be appreciated, thanks
I found a solution:
{% for User in all_users %}
{{ url('detail', args=[User.id] )}}
{% endfor %}
This also works:
{% url 'detail' user.id %}
Then, in views.py, your corresponding function should receive user_id as an argument (internally user.id becomes user_id).
Django now has official backend support for Jinja2 - I'm guessing you were using django-jinja which provides an implementation of the url function but it's fairly straightforward to integrate Django and Jinja now without adding any additional dependencies (besides Jinja2).
First, configure your TEMPLATES in your settings file and add a dictionary entry with the jinja2 backend:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'APP_DIRS': True,
'OPTIONS': {
'extensions': [<custom extensions if any>]
'environment': 'yourapp.jinja2.environment',
...
},
},
...
]
The key part of this is the environment setting. The examples suggest creating a jinja2.py file in your app directory and defining an environment function there that you'll set to a function that returns a Jinja2 Environment, e.g.,
# example yourapp/jinja2.py
from django.conf import settings
from django.urls import reverse
from django.templatetags.static import static
from jinja2 import Environment
def environment(**options):
env = Environment(**options)
env.globals.update({
'static': static,
'url': reverse,
'settings': settings,
...
})
return env
Here we are binding Python functions (and Django things) to be made available in our Jinja2 environment, so now static in a jinja template can be called static(...) and invoke the Django static function defined in django.templatetags.static, and url is bound to the Django reverse function so url('detail', args=[user.id]) should work. If you prefer a signature like {{ url('detail', pk=123) }} without generating an invalid arguments error you can define your own function:
def jinja_url(viewname, *args, **kwargs):
return reverse(viewname, args=args, kwargs=kwargs)
and bind 'url': jinja_url in your TEMPLATES settings instead.
It's also important to note that Django looks for your jinja templates in a jinja2 directory under your app dir, not the templates directory.

How can I access environment variables directly in a Django template?

I'm looking to do something like this non-working code in my Django template:
{% if os.environ.DJANGO_SETTINGS_MODULE == "settings.staging" %}
Is something like this possible? My workaround is creating a context processor to make the variable available across all templates, but wanted to know if there is a more direct way to achieve the same result.
Use context_processor, do as below and you should be able to access os.environ['DJANGO_SETTINGS_MODULE'] as SETTING_TYPE in templates. Example you can use {% if SETTING_TYPE == "settings.staging" %}
project/context_processors.py
import os
def export_vars(request):
data = {}
data['SETTING_TYPE'] = os.environ['DJANGO_SETTINGS_MODULE']
return data
project/settings.py (your actual settings file)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
...
'project.context_processors.export_vars',
...
]
}
}
]
Extending on #vinay kumar's comment, you can create a custom template filter in the following way:
application/template_tags_folder/template_tags_file.py
from django.template.defaulttags import register
import os
#register.filter
def env(key):
return os.environ.get(key, None)
And then in your template you can access it this way:
template.html
{% if 'DJANGO_SETTINGS_MODULE'|env == 'app_name.staging_settings' %}
Finally I leave a django docs reference and a stackoverflow reference for creating custom template tags and filters in django.

Django: How to get language code in template?

Is there's some global variable for gettin' language code in django template or atleast passing it through view?
something like: {{ LANG }} should produce "en" for example..
I really not comfortable when people using request.LANGUAGE_CODE.
Detailed explanation would be appreciated =)
It's an old topic. But some might find it useful.
{% load i18n %}
...
{% get_current_language as LANGUAGE_CODE %}
Django reference and example.
If it didn't already exist, you would need to write a template context processor. Here's how you'd do that.
Put this somewhere:
def lang_context_processor(request):
return {'LANG': request.LANGUAGE_CODE}
And then, add a reference to it the TEMPLATE_CONTEXT_PROCESSORS setting. Something like this:
from django.conf import global_settings
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'myproject.myapp.templatecontext.lang_context_processor',
)
(I recommend adding to the global setting because it means you don't break things accidentally when a new context processor is added to the defaults.)
However, it does exist, as the inbuilt template context processor django.template.context_processors.i18n. You can access it as LANGUAGE_CODE.
Purely for interest, here's the definition of that function:
def i18n(request):
from django.utils import translation
return {
'LANGUAGES': settings.LANGUAGES,
'LANGUAGE_CODE': translation.get_language(),
'LANGUAGE_BIDI': translation.get_language_bidi(),
}
Make sure that you're using a RequestContext for your template rendering, not a plain Context, or it won't work.
Tested with Django==1.11.2.
Enable I18N and employ i18n template context processor.
# setings.py
USE_I18N = True
# ...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
# ...
'django.template.context_processors.i18n',
# ...
],
},
},
]
And then it's simple in the template.
# template.html
{% load i18n %}
{{ LANGUAGE_CODE }}
But use render(), not render_to_response(), in your view function so the LANGUAGE_CODE variable is accessible in the template:
render_to_response()
This function preceded the introduction of render() and works
similarly except that it doesn’t make the request available in the
response. It’s not recommended and is likely to be deprecated in the
future.

Django: sorl-thumbnail and easy-thumbnail in same project

I'm working on and project that uses two separate modular Django apps. However, one app requires easy-thumbnails and the other requires sorl-thumbnails. Unfortunately, the two thumbnail libraries make use of the template tag syntax {% load thumbnail %}, so they clash and break when a template using them tries to render.
Are there any approaches to solve this type of clash? (For example, a template option does to the effect of {% load thumbnail as easy_thumbnail %}). Am I going to have to fork one of the apps and replace one of the thumbnail libraries with another? If so, which should I choose to go with?
Thank you for considering my question,
Joe
In Django 1.9, you can use the libraries option of DjangoTemplates to include a tag library under a specified name. In the example below, the thumbnail library from sorl.thumbnail is included under the name sorl_thumbnail.
Note: the templatetag itself is not changed within the template... ie. remains thumbnail
Usage:
settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "foo", "templates")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'libraries': {
'sorl_thumbnail': 'sorl.thumbnail.templatetags.thumbnail',
},
},
},
]
your_template.html
{% load sorl_thumbnail %}
{% thumbnail mymodel.image "640x480" crop="center" as im %}
<img src="{{ im.url }}" width="{{im.width}}" height="{{im.height}}"/>
{% endthumbnail %}
Sure, just write your own stub easy_thumbnail wrapper...
Create a thumbnailtags package in one of your django apps...
...making sure it's got an empty __init__.py
In thumbnailtags/easy_thumbnail.py do something like:
from django.template import Library
from easy_thumbnails.templatetags import thumbnail
register = Library()
def easy_thumbnail(parser, token):
return thumbnail(parser, token)
register.tag(easy_thumbnail)
Use {% load easy_thumbnail %}
Note:
You might also be able to do 'import thumbnail as easy_thumbnail, and skip the def easy_thumbnail bit, tho I've not tried that.
This blog link shows how to handle this.
https://timmyomahony.com/blog/using-sorl-thumbnail-and-easy-thumbnails-same-template/
(previously
http://timmyomahony.com/blog/2012/10/22/using-sorl-thumbnail-and-easy-thumbnails-same-template/)
UPDATE 2015
I had to do the following modifications to Tom Christie's answer in order to get this to work:
create a templatetags package in one of you local apps. It is important to name it templatetags. See django docs for template tags.
... make sure it has an __init__.py, empty or not.
In templatetags/easy_thumbnail.py do this:
from django.template import Library
from easy_thumbnails.templatetags import thumbnail
register = Library()
def easy_thumbnail(parser, token):
return thumbnail.thumbnail(parser, token) # the important bit
register.tag(easy_thumbnail)
Use {% load easy_thumbnail %} or - load easy_thumbnail with pyjade