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:
parent
269e921756
commit
ccc49029b8
1
AUTHORS
1
AUTHORS
|
@ -501,6 +501,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Joel Watts <joel@joelwatts.com>
|
Joel Watts <joel@joelwatts.com>
|
||||||
Lakin Wecker <lakin@structuredabstraction.com>
|
Lakin Wecker <lakin@structuredabstraction.com>
|
||||||
Chris Wesseling <Chris.Wesseling@cwi.nl>
|
Chris Wesseling <Chris.Wesseling@cwi.nl>
|
||||||
|
Benjamin Wohlwend <piquadrat@gmail.com>
|
||||||
James Wheare <django@sparemint.com>
|
James Wheare <django@sparemint.com>
|
||||||
Mike Wiacek <mjwiacek@google.com>
|
Mike Wiacek <mjwiacek@google.com>
|
||||||
Frank Wierzbicki
|
Frank Wierzbicki
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
{% load l10n %}
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{% block vars %}var geodjango = {};{% for icon in icons %}
|
{% localize off %}
|
||||||
var {{ icon.varname }} = new GIcon(G_DEFAULT_ICON);
|
{% 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.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.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 %}
|
{% 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.");
|
alert("Sorry, the Google Maps API is not compatible with this browser.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endblock load %}{% endblock functions %}{% endautoescape %}
|
{% endblock load %}{% endblock functions %}{% endlocalize %}{% endautoescape %}
|
||||||
|
|
|
@ -825,7 +825,7 @@ def _render_value_in_context(value, context):
|
||||||
means escaping, if required, and conversion to a unicode object. If value
|
means escaping, if required, and conversion to a unicode object. If value
|
||||||
is a string, it is expected to have already been translated.
|
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)
|
value = force_unicode(value)
|
||||||
if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData):
|
if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData):
|
||||||
return escape(value)
|
return escape(value)
|
||||||
|
|
|
@ -66,8 +66,9 @@ class BaseContext(object):
|
||||||
|
|
||||||
class Context(BaseContext):
|
class Context(BaseContext):
|
||||||
"A stack container for variable context"
|
"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.autoescape = autoescape
|
||||||
|
self.use_l10n = use_l10n
|
||||||
self.current_app = current_app
|
self.current_app = current_app
|
||||||
self.render_context = RenderContext()
|
self.render_context = RenderContext()
|
||||||
super(Context, self).__init__(dict_)
|
super(Context, self).__init__(dict_)
|
||||||
|
@ -139,8 +140,8 @@ class RequestContext(Context):
|
||||||
Additional processors can be specified as a list of callables
|
Additional processors can be specified as a list of callables
|
||||||
using the "processors" keyword argument.
|
using the "processors" keyword argument.
|
||||||
"""
|
"""
|
||||||
def __init__(self, request, dict=None, processors=None, current_app=None):
|
def __init__(self, request, dict=None, processors=None, current_app=None, use_l10n=None):
|
||||||
Context.__init__(self, dict, current_app=current_app)
|
Context.__init__(self, dict, current_app=current_app, use_l10n=use_l10n)
|
||||||
if processors is None:
|
if processors is None:
|
||||||
processors = ()
|
processors = ()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.conf import settings
|
||||||
from django.template import Lexer, Parser, tag_re, NodeList, VariableNode, TemplateSyntaxError
|
from django.template import Lexer, Parser, tag_re, NodeList, VariableNode, TemplateSyntaxError
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_unicode
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
@ -87,7 +88,7 @@ class DebugVariableNode(VariableNode):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
try:
|
try:
|
||||||
output = self.filter_expression.resolve(context)
|
output = self.filter_expression.resolve(context)
|
||||||
output = localize(output)
|
output = localize(value, use_l10n=use_l10n)
|
||||||
output = force_unicode(output)
|
output = force_unicode(output)
|
||||||
except TemplateSyntaxError, e:
|
except TemplateSyntaxError, e:
|
||||||
if not hasattr(e, 'source'):
|
if not hasattr(e, 'source'):
|
||||||
|
|
|
@ -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)
|
|
@ -41,14 +41,17 @@ def get_format_modules(reverse=False):
|
||||||
modules.reverse()
|
modules.reverse()
|
||||||
return modules
|
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
|
For a specific format type, returns the format for the current
|
||||||
language (locale), defaults to the format in the settings.
|
language (locale), defaults to the format in the settings.
|
||||||
format_type is the name of the format, e.g. 'DATE_FORMAT'
|
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)
|
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:
|
if lang is None:
|
||||||
lang = get_language()
|
lang = get_language()
|
||||||
cache_key = (format_type, lang)
|
cache_key = (format_type, lang)
|
||||||
|
@ -65,48 +68,60 @@ def get_format(format_type, lang=None):
|
||||||
_format_cache[cache_key] = None
|
_format_cache[cache_key] = None
|
||||||
return getattr(settings, format_type)
|
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
|
Formats a datetime.date or datetime.datetime object using a
|
||||||
localizable format
|
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
|
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
|
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()
|
lang = get_language()
|
||||||
else:
|
else:
|
||||||
lang = None
|
lang = None
|
||||||
return numberformat.format(
|
return numberformat.format(
|
||||||
value,
|
value,
|
||||||
get_format('DECIMAL_SEPARATOR', lang),
|
get_format('DECIMAL_SEPARATOR', lang, use_l10n=use_l10n),
|
||||||
decimal_pos,
|
decimal_pos,
|
||||||
get_format('NUMBER_GROUPING', lang),
|
get_format('NUMBER_GROUPING', lang, use_l10n=use_l10n),
|
||||||
get_format('THOUSAND_SEPARATOR', lang),
|
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
|
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)):
|
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):
|
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):
|
elif isinstance(value, datetime.date):
|
||||||
return date_format(value)
|
return date_format(value, use_l10n=use_l10n)
|
||||||
elif isinstance(value, datetime.time):
|
elif isinstance(value, datetime.time):
|
||||||
return time_format(value, 'TIME_FORMAT')
|
return time_format(value, 'TIME_FORMAT', use_l10n=use_l10n)
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
|
@ -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``
|
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
|
setting but rather set :setting:`USE_I18N` to True, then loading it with
|
||||||
``{% load i18n %}``. See :ref:`specifying-translation-strings-in-template-code`.
|
``{% 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`.
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
Localization
|
Localization
|
||||||
============
|
============
|
||||||
|
|
||||||
This document covers two localization-related topics: `Creating language
|
This document covers three localization-related topics: `Creating language
|
||||||
files`_ and `locale aware date, time and numbers input/output in forms`_
|
files`_ , `locale aware date, time and numbers input/output in forms`_,
|
||||||
|
and `controlling localization in templates`_.
|
||||||
|
|
||||||
.. _`Creating language files`: how-to-create-language-files_
|
.. _`Creating language files`: how-to-create-language-files_
|
||||||
.. _`locale aware date, time and numbers input/output in forms`: format-localization_
|
.. _`locale aware date, time and numbers input/output in forms`: format-localization_
|
||||||
|
.. _`controlling localization in templates`: topic-l10n-templates
|
||||||
|
|
||||||
.. seealso::
|
.. 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,
|
to use a space as a thousand separator, instead of the default for English,
|
||||||
a comma.
|
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.
|
||||||
|
|
|
@ -453,6 +453,33 @@ class FormattingTests(TestCase):
|
||||||
settings.FORMAT_MODULE_PATH = old_format_module_path
|
settings.FORMAT_MODULE_PATH = old_format_module_path
|
||||||
deactivate()
|
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):
|
class MiscTests(TestCase):
|
||||||
|
|
||||||
def test_parse_spec_http_header(self):
|
def test_parse_spec_http_header(self):
|
||||||
|
|
Loading…
Reference in New Issue