Fixed #14181 -- Added a template tag and filters to allow localization to be disabled in a template. Thanks to Benjamin Wohlwend for the work on the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14395 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2010-10-29 16:48:58 +00:00
parent 269e921756
commit ccc49029b8
10 changed files with 245 additions and 29 deletions

View File

@ -501,6 +501,7 @@ answer newbie questions, and generally made Django that much better:
Joel Watts <joel@joelwatts.com>
Lakin Wecker <lakin@structuredabstraction.com>
Chris Wesseling <Chris.Wesseling@cwi.nl>
Benjamin Wohlwend <piquadrat@gmail.com>
James Wheare <django@sparemint.com>
Mike Wiacek <mjwiacek@google.com>
Frank Wierzbicki

View File

@ -1,6 +1,8 @@
{% load l10n %}
{% autoescape off %}
{% block vars %}var geodjango = {};{% for icon in icons %}
var {{ icon.varname }} = new GIcon(G_DEFAULT_ICON);
{% localize off %}
{% block vars %}var geodjango = {};{% for icon in icons %}
var {{ icon.varname }} = new GIcon(G_DEFAULT_ICON);
{% if icon.image %}{{ icon.varname }}.image = "{{ icon.image }}";{% endif %}
{% if icon.shadow %}{{ icon.varname }}.shadow = "{{ icon.shadow }}";{% endif %} {% if icon.shadowsize %}{{ icon.varname }}.shadowSize = new GSize({{ icon.shadowsize.0 }}, {{ icon.shadowsize.1 }});{% endif %}
{% if icon.iconanchor %}{{ icon.varname }}.iconAnchor = new GPoint({{ icon.iconanchor.0 }}, {{ icon.iconanchor.1 }});{% endif %} {% if icon.iconsize %}{{ icon.varname }}.iconSize = new GSize({{ icon.iconsize.0 }}, {{ icon.iconsize.1 }});{% endif %}
@ -32,4 +34,4 @@ var {{ icon.varname }} = new GIcon(G_DEFAULT_ICON);
alert("Sorry, the Google Maps API is not compatible with this browser.");
}
}
{% endblock load %}{% endblock functions %}{% endautoescape %}
{% endblock load %}{% endblock functions %}{% endlocalize %}{% endautoescape %}

View File

@ -825,7 +825,7 @@ def _render_value_in_context(value, context):
means escaping, if required, and conversion to a unicode object. If value
is a string, it is expected to have already been translated.
"""
value = localize(value)
value = localize(value, use_l10n=context.use_l10n)
value = force_unicode(value)
if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData):
return escape(value)

View File

@ -66,8 +66,9 @@ class BaseContext(object):
class Context(BaseContext):
"A stack container for variable context"
def __init__(self, dict_=None, autoescape=True, current_app=None):
def __init__(self, dict_=None, autoescape=True, current_app=None, use_l10n=None):
self.autoescape = autoescape
self.use_l10n = use_l10n
self.current_app = current_app
self.render_context = RenderContext()
super(Context, self).__init__(dict_)
@ -139,8 +140,8 @@ class RequestContext(Context):
Additional processors can be specified as a list of callables
using the "processors" keyword argument.
"""
def __init__(self, request, dict=None, processors=None, current_app=None):
Context.__init__(self, dict, current_app=current_app)
def __init__(self, request, dict=None, processors=None, current_app=None, use_l10n=None):
Context.__init__(self, dict, current_app=current_app, use_l10n=use_l10n)
if processors is None:
processors = ()
else:

View File

@ -1,3 +1,4 @@
from django.conf import settings
from django.template import Lexer, Parser, tag_re, NodeList, VariableNode, TemplateSyntaxError
from django.utils.encoding import force_unicode
from django.utils.html import escape
@ -87,7 +88,7 @@ class DebugVariableNode(VariableNode):
def render(self, context):
try:
output = self.filter_expression.resolve(context)
output = localize(output)
output = localize(value, use_l10n=use_l10n)
output = force_unicode(output)
except TemplateSyntaxError, e:
if not hasattr(e, 'source'):

View File

@ -0,0 +1,67 @@
from django.conf import settings
from django.template import Node
from django.template import TemplateSyntaxError, Library
from django.utils import formats
from django.utils.encoding import force_unicode
register = Library()
def localize(value):
"""
Forces a value to be rendered as a localized value,
regardless of the value of ``settings.USE_L10N``.
"""
return force_unicode(formats.localize(value, use_l10n=True))
localize.is_safe = False
def unlocalize(value):
"""
Forces a value to be rendered as a non-localized value,
regardless of the value of ``settings.USE_L10N``.
"""
return force_unicode(value)
unlocalize.is_safe = False
class LocalizeNode(Node):
def __init__(self, nodelist, use_l10n):
self.nodelist = nodelist
self.use_l10n = use_l10n
def __repr__(self):
return "<LocalizeNode>"
def render(self, context):
old_setting = context.use_l10n
context.use_l10n = self.use_l10n
output = self.nodelist.render(context)
context.use_l10n = old_setting
return output
@register.tag('localize')
def localize_tag(parser, token):
"""
Forces or prevents localization of values, regardless of the value of
`settings.USE_L10N`.
Sample usage::
{% localize off %}
var pi = {{ 3.1415 }};
{% endlocalize %}
"""
use_l10n = None
bits = list(token.split_contents())
if len(bits) == 1:
use_l10n = True
elif len(bits) > 2 or bits[1] not in ('on', 'off'):
raise TemplateSyntaxError("%r argument should be 'on' or 'off'" % bits[0])
else:
use_l10n = bits[1] == 'on'
nodelist = parser.parse(('endlocalize',))
parser.delete_first_token()
return LocalizeNode(nodelist, use_l10n)
register.filter(localize)
register.filter(unlocalize)

View File

@ -41,14 +41,17 @@ def get_format_modules(reverse=False):
modules.reverse()
return modules
def get_format(format_type, lang=None):
def get_format(format_type, lang=None, use_l10n=None):
"""
For a specific format type, returns the format for the current
language (locale), defaults to the format in the settings.
format_type is the name of the format, e.g. 'DATE_FORMAT'
If use_l10n is provided and is not None, that will force the value to
be localized (or not), overriding the value of settings.USE_L10N.
"""
format_type = smart_str(format_type)
if settings.USE_L10N:
if use_l10n or (use_l10n is None and settings.USE_L10N):
if lang is None:
lang = get_language()
cache_key = (format_type, lang)
@ -65,48 +68,60 @@ def get_format(format_type, lang=None):
_format_cache[cache_key] = None
return getattr(settings, format_type)
def date_format(value, format=None):
def date_format(value, format=None, use_l10n=None):
"""
Formats a datetime.date or datetime.datetime object using a
localizable format
"""
return dateformat.format(value, get_format(format or 'DATE_FORMAT'))
def time_format(value, format=None):
If use_l10n is provided and is not None, that will force the value to
be localized (or not), overriding the value of settings.USE_L10N.
"""
return dateformat.format(value, get_format(format or 'DATE_FORMAT', use_l10n=use_l10n))
def time_format(value, format=None, use_l10n=None):
"""
Formats a datetime.time object using a localizable format
"""
return dateformat.time_format(value, get_format(format or 'TIME_FORMAT'))
def number_format(value, decimal_pos=None):
If use_l10n is provided and is not None, that will force the value to
be localized (or not), overriding the value of settings.USE_L10N.
"""
return dateformat.time_format(value, get_format(format or 'TIME_FORMAT', use_l10n=use_l10n))
def number_format(value, decimal_pos=None, use_l10n=None):
"""
Formats a numeric value using localization settings
If use_l10n is provided and is not None, that will force the value to
be localized (or not), overriding the value of settings.USE_L10N.
"""
if settings.USE_L10N:
if use_l10n or (use_l10n is None and settings.USE_L10N):
lang = get_language()
else:
lang = None
return numberformat.format(
value,
get_format('DECIMAL_SEPARATOR', lang),
get_format('DECIMAL_SEPARATOR', lang, use_l10n=use_l10n),
decimal_pos,
get_format('NUMBER_GROUPING', lang),
get_format('THOUSAND_SEPARATOR', lang),
get_format('NUMBER_GROUPING', lang, use_l10n=use_l10n),
get_format('THOUSAND_SEPARATOR', lang, use_l10n=use_l10n),
)
def localize(value):
def localize(value, use_l10n=None):
"""
Checks if value is a localizable type (date, number...) and returns it
formatted as a string using current locale format
formatted as a string using current locale format.
If use_l10n is provided and is not None, that will force the value to
be localized (or not), overriding the value of settings.USE_L10N.
"""
if isinstance(value, (decimal.Decimal, float, int, long)):
return number_format(value)
return number_format(value, use_l10n=use_l10n)
elif isinstance(value, datetime.datetime):
return date_format(value, 'DATETIME_FORMAT')
return date_format(value, 'DATETIME_FORMAT', use_l10n=use_l10n)
elif isinstance(value, datetime.date):
return date_format(value)
return date_format(value, use_l10n=use_l10n)
elif isinstance(value, datetime.time):
return time_format(value, 'TIME_FORMAT')
return time_format(value, 'TIME_FORMAT', use_l10n=use_l10n)
else:
return value

View File

@ -2113,3 +2113,12 @@ Django templates. It is slightly different from the libraries described
above because you don't need to add any application to the ``INSTALLED_APPS``
setting but rather set :setting:`USE_I18N` to True, then loading it with
``{% load i18n %}``. See :ref:`specifying-translation-strings-in-template-code`.
l10n
~~~~
Provides a couple of templatetags that allow control over the localization of
values in Django templates. It is slightly different from the libraries described
above because you don't need to add any application to the ``INSTALLED_APPS``;
you only need to load the library using ``{% load l10n %}``. See
:ref:`topic-l10n-templates`.

View File

@ -2,11 +2,13 @@
Localization
============
This document covers two localization-related topics: `Creating language
files`_ and `locale aware date, time and numbers input/output in forms`_
This document covers three localization-related topics: `Creating language
files`_ , `locale aware date, time and numbers input/output in forms`_,
and `controlling localization in templates`_.
.. _`Creating language files`: how-to-create-language-files_
.. _`locale aware date, time and numbers input/output in forms`: format-localization_
.. _`controlling localization in templates`: topic-l10n-templates
.. seealso::
@ -315,3 +317,94 @@ where :file:`formats.py` contains custom format definitions. For example::
to use a space as a thousand separator, instead of the default for English,
a comma.
.. topic-l10n-templates:
Controlling localization in templates
=====================================
When you have enabled localization using :setting:`USE_L10N`, Django
will try to use a locale specific format whenever it outputs a value
in a template.
However, it may not always be appropriate to use localized values --
for example, if you're outputting Javascript or XML that is designed
to be machine-readable, you will always want unlocalized values. You
may also want to use localization in selected templates, rather than
using localization everywhere.
To allow for fine control over the use of localization, Django
provides a the ``l10n`` template library that contains the following
tags and filters.
Template tags
-------------
.. templatetag:: localize
localize
~~~~~~~~
.. versionadded:: 1.3
Enables or disables localization of template variables in the
contained block.
This tag allows a more fine grained control of localization than
:setting:`USE_L10N`.
To activate or deactivate localization for a template block, use::
{% localize on %}
{{ value }}
{% endlocalize %}
{% localize off %}
{{ value }}
{% endlocalize %}
.. note::
The value of :setting:`USE_L10N` is not respected inside of a
`{% localize %}` block.
See :tfilter:`localized` and :tfilter:`unlocalized` for a template filter that will
do the same job on a per-variable basis.
Template filters
----------------
.. templatefilter:: localize
localize
~~~~~~~~
.. versionadded:: 1.3
Forces localization of a single value.
For example::
{{ value|localize }}
To disable localization on a single value, use :tfilter:`unlocalize`. To control
localization over a large section of a template, use the :ttag:`localize` template
tag.
.. templatefilter:: unlocalize
unlocalize
~~~~~~~~~~
.. versionadded:: 1.3
Forces a single value to be printed without localization.
For example::
{{ value|unlocalize }}
To force localization of a single value, use :tfilter:`localize`. To
control localization over a large section of a template, use the
:ttag:`localize` template tag.

View File

@ -453,6 +453,33 @@ class FormattingTests(TestCase):
settings.FORMAT_MODULE_PATH = old_format_module_path
deactivate()
def test_localize_templatetag_and_filter(self):
"""
Tests the {% localize %} templatetag
"""
context = Context({'value': 3.14 })
template1 = Template("{% load l10n %}{% localize %}{{ value }}{% endlocalize %};{% localize on %}{{ value }}{% endlocalize %}")
template2 = Template("{% load l10n %}{{ value }};{% localize off %}{{ value }};{% endlocalize %}{{ value }}")
template3 = Template('{% load l10n %}{{ value }};{{ value|unlocalize }}')
template4 = Template('{% load l10n %}{{ value }};{{ value|localize }}')
output1 = '3,14;3,14'
output2 = '3,14;3.14;3,14'
output3 = '3,14;3.14'
output4 = '3.14;3,14'
old_localize = settings.USE_L10N
try:
activate('de')
settings.USE_L10N = False
self.assertEqual(template1.render(context), output1)
self.assertEqual(template4.render(context), output4)
settings.USE_L10N = True
self.assertEqual(template1.render(context), output1)
self.assertEqual(template2.render(context), output2)
self.assertEqual(template3.render(context), output3)
finally:
deactivate()
settings.USE_L10N = old_localize
class MiscTests(TestCase):
def test_parse_spec_http_header(self):