summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/conf/global_settings.py25
-rw-r--r--django/conf/locale/ca/formats.py4
-rw-r--r--django/contrib/admin/templatetags/admin_list.py14
-rw-r--r--django/template/defaultfilters.py5
-rw-r--r--django/utils/formats.py48
-rw-r--r--django/utils/numberformat.py32
-rw-r--r--docs/ref/settings.txt61
-rw-r--r--tests/regressiontests/i18n/tests.py59
8 files changed, 203 insertions, 45 deletions
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index a8f180a426..e09164e530 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -259,6 +259,12 @@ FILE_UPLOAD_TEMP_DIR = None
# you'd pass directly to os.chmod; see http://docs.python.org/lib/os-file-dir.html.
FILE_UPLOAD_PERMISSIONS = None
+# Python module path where user will place custom format definition.
+# The directory where this setting is pointing should contain subdirectories
+# named as the locales, containing a formats.py file
+# (i.e. "myproject.locale" for myproject/locale/en/formats.py etc. use)
+FORMAT_MODULE_PATH = None
+
# Default formatting for date objects. See all available format strings here:
# http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
DATE_FORMAT = 'N j, Y'
@@ -283,21 +289,28 @@ MONTH_DAY_FORMAT = 'F j'
# Default shortformatting for date objects. See all available format strings here:
# http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
-SHORT_DATE_FORMAT = 'N j, Y'
+SHORT_DATE_FORMAT = 'm/d/Y'
# Default short formatting for datetime objects.
# See all available format strings here:
# http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
-SHORT_DATETIME_FORMAT = 'N j, Y, P'
+SHORT_DATETIME_FORMAT = 'm/d/Y P'
-# Default first day of week, to be used on calendars
-# 0 means Sunday
+# First day of week, to be used on calendars
+# 0 means Sunday, 1 means Monday...
FIRST_DAY_OF_WEEK = 0
-# Default decimal separator symbol
+# Decimal separator symbol
DECIMAL_SEPARATOR = '.'
-# Default thousand separator symbol
+# Boolean that sets whether to add thousand separator when formatting numbers
+USE_THOUSAND_SEPARATOR = False
+
+# Number of digits that will be togheter, when spliting them by THOUSAND_SEPARATOR
+# 0 means no grouping, 3 means splitting by thousands...
+NUMBER_GROUPING = 0
+
+# Thousand separator symbol
THOUSAND_SEPARATOR = ','
# Do you want to manage transactions manually?
diff --git a/django/conf/locale/ca/formats.py b/django/conf/locale/ca/formats.py
index 414fe86e26..cc8a494f7c 100644
--- a/django/conf/locale/ca/formats.py
+++ b/django/conf/locale/ca/formats.py
@@ -3,7 +3,7 @@
DATE_FORMAT = 'j \de F \de Y'
DATETIME_FORMAT = 'j \de F \de Y \\a \le\s H:i'
-TIME_FORMAT = 'P'
+TIME_FORMAT = 'H:i'
YEAR_MONTH_FORMAT = 'F \de Y'
MONTH_DAY_FORMAT = 'j \de F'
@@ -14,5 +14,5 @@ FIRST_DAY_OF_WEEK = 1 # 0 means Sunday
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = '.'
-
+NUMBER_GROUPING = 3
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index 45bab2d6b4..add72037bb 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -8,7 +8,6 @@ from django.utils.html import escape, conditional_escape
from django.utils.text import capfirst
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
-from django.utils.formats import localize
from django.utils.encoding import smart_unicode, smart_str, force_unicode
from django.template import Library
import datetime
@@ -183,14 +182,17 @@ def items_for_result(cl, result, form):
else:
result_repr = EMPTY_CHANGELIST_VALUE
# Dates and times are special: They're formatted in a certain way.
- elif isinstance(f, models.DateField) or isinstance(f, models.TimeField) or isinstance(f, models.DecimalField):
+ elif isinstance(f, models.DateField) or isinstance(f, models.TimeField):
if field_val:
- result_repr = localize(field_val)
- # result_repr = ('%%.%sf' % f.decimal_places) % field_val
+ result_repr = formats.localize(field_val)
else:
result_repr = EMPTY_CHANGELIST_VALUE
- if not isinstance(f, models.DecimalField):
- row_class = ' class="nowrap"'
+ elif isinstance(f, models.DecimalField):
+ if field_val:
+ result_repr = formats.number_format(field_val, f.decimal_places)
+ else:
+ result_repr = EMPTY_CHANGELIST_VALUE
+ row_class = ' class="nowrap"'
# Booleans are special: We use images.
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
result_repr = _boolean_icon(field_val)
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 47e116cd1a..94108f2d23 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -18,6 +18,7 @@ from django.conf import settings
from django.utils.translation import ugettext, ungettext
from django.utils.encoding import force_unicode, iri_to_uri
from django.utils.safestring import mark_safe, SafeData
+from django.utils.formats import number_format
register = Library()
@@ -166,14 +167,14 @@ def floatformat(text, arg=-1):
return input_val
if not m and p < 0:
- return mark_safe(u'%d' % (int(d)))
+ return mark_safe(number_format(u'%d' % (int(d)), 0))
if p == 0:
exp = Decimal(1)
else:
exp = Decimal('1.0') / (Decimal(10) ** abs(p))
try:
- return mark_safe(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)))
+ return mark_safe(number_format(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)), abs(p)))
except InvalidOperation:
return input_val
floatformat.is_safe = True
diff --git a/django/utils/formats.py b/django/utils/formats.py
index ad22e7600c..2b8e2704c0 100644
--- a/django/utils/formats.py
+++ b/django/utils/formats.py
@@ -1,6 +1,8 @@
import decimal
import datetime
+from django.conf import settings
+from django.utils.translation import get_language
from django.utils.importlib import import_module
from django.utils import dateformat
from django.utils import numberformat
@@ -11,7 +13,6 @@ def getformat_null(format_type):
set on the settings.
format_type is the name of the format, for example 'DATE_FORMAT'
"""
- from django.conf import settings
return getattr(settings, format_type)
def getformat_real(format_type):
@@ -20,15 +21,21 @@ def getformat_real(format_type):
current language (locale) defaulting to the format on settings.
format_type is the name of the format, for example 'DATE_FORMAT'
"""
- from django.utils.translation import get_language
import_formats = lambda s: import_module('.formats', 'django.conf.locale.%s' % s)
- tmp = import_formats('ca')
- format = None
- try:
- module = import_formats(get_language())
- except ImportError:
- pass
- else:
+ module = format = None
+ if settings.FORMAT_MODULE_PATH:
+ try:
+ module = import_module('.formats', '%s.%s' % (settings.FORMAT_MODULE_PATH, get_language()))
+ except ImportError:
+ pass
+
+ if not module:
+ try:
+ module = import_module('.formats', 'django.conf.locale.%s' % get_language())
+ except ImportError:
+ pass
+
+ if module:
try:
format = getattr(module, format_type)
except AttributeError:
@@ -37,28 +44,43 @@ def getformat_real(format_type):
# getformat will just return the value on setings if
# we don't use i18n in our project
-from django.conf import settings
if settings.USE_I18N and settings.USE_FORMAT_I18N:
getformat = getformat_real
else:
getformat = getformat_null
def date_format(value, format=None):
+ """
+ Formats a datetime.date or datetime.datetime object using a
+ localizable format
+ """
return dateformat.format(value, getformat(format or 'DATE_FORMAT'))
-def number_format(value):
+def number_format(value, decimal_pos=None):
+ """
+ Formats a numeric value using localization settings
+ """
return numberformat.format(
value,
getformat('DECIMAL_SEPARATOR'),
- 2, # TODO: decide how to set decimal positions later
- 3, # TODO: get it from formats
+ decimal_pos,
+ getformat('NUMBER_GROUPING'),
getformat('THOUSAND_SEPARATOR'),
)
def localize(value):
+ """
+ Checks value, and if it has a localizable type (date,
+ number...) it returns the value as a string using
+ current locale format
+ """
if settings.USE_I18N and settings.USE_FORMAT_I18N:
if isinstance(value, decimal.Decimal):
return number_format(value)
+ elif isinstance(value, float):
+ return number_format(value)
+ elif isinstance(value, int):
+ return number_format(value)
elif isinstance(value, datetime.datetime):
return date_format(value, 'DATETIME_FORMAT')
elif isinstance(value, datetime.date):
diff --git a/django/utils/numberformat.py b/django/utils/numberformat.py
index 0abf5b35c5..6223dbc26f 100644
--- a/django/utils/numberformat.py
+++ b/django/utils/numberformat.py
@@ -1,5 +1,13 @@
-def format(number, decimal_sep, decimal_pos, groupping=0, thousand_sep=''):
+from django.conf import settings
+
+def format(number, decimal_sep, decimal_pos, grouping=0, thousand_sep=''):
"""
+ Gets a number (as a number or string), and returns it as a string,
+ using formats definied as arguments:
+ * decimal_sep: Decimal separator symbol (for example ".")
+ * decimal_pos: Number of decimal positions
+ * grouping: Number of digits in every group limited by thousand separator
+ * thousand_sep: Thousand separator symbol (for example ",")
"""
# sign
if number < 0:
@@ -7,28 +15,26 @@ def format(number, decimal_sep, decimal_pos, groupping=0, thousand_sep=''):
else:
sign = ''
# decimal part
- str_number = str(abs(number))
+ str_number = unicode(number)
+ if str_number[0] == '-':
+ str_number = str_number[1:]
if '.' in str_number:
int_part, dec_part = str_number.split('.')
- dec_part = dec_part[:decimal_pos]
+ if decimal_pos:
+ dec_part = dec_part[:decimal_pos]
else:
int_part, dec_part = str_number, ''
- dec_part = dec_part + ('0' * (decimal_pos - len(dec_part)))
+ if decimal_pos:
+ dec_part = dec_part + ('0' * (decimal_pos - len(dec_part)))
if dec_part: dec_part = decimal_sep + dec_part
- # groupping
- if groupping:
+ # grouping
+ if settings.USE_THOUSAND_SEPARATOR and grouping:
int_part_gd = ''
for cnt, digit in enumerate(int_part[::-1]):
- if cnt and not cnt % groupping:
+ if cnt and not cnt % grouping:
int_part_gd += thousand_sep
int_part_gd += digit
int_part = int_part_gd[::-1]
-
return sign + int_part + dec_part
-if __name__ == '__main__':
- import decimal
- print format_number(-1000000, ',', 4, 3, '.')
- print format_number(-100.100, ',', 4, 3, '.')
- print format_number(decimal.Decimal('1000.100'), ',', 4)
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index 597aeaa89d..13bcbd24e3 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -302,7 +302,6 @@ will be suppressed, and exceptions will propagate upwards. This can
be useful for some test setups, and should never be used on a live
site.
-
.. setting:: DECIMAL_SEPARATOR
DECIMAL_SEPARATOR
@@ -310,7 +309,7 @@ DECIMAL_SEPARATOR
Default: ``'.'`` (Dot)
-The default decimal separator used when formatting decimal numbers.
+Default decimal separator used when formatting decimal numbers.
.. setting:: DEFAULT_CHARSET
@@ -537,6 +536,21 @@ system's standard umask.
.. _documentation for os.chmod: http://docs.python.org/lib/os-file-dir.html
+.. setting:: FIRST_DAY_OF_WEEK
+
+FIRST_DAY_OF_WEEK
+-----------------
+
+Default: ``0`` (Sunday)
+
+Number representing the first day of the week. This is specially useful
+when displaying a calendar. This value is only used when not using
+format internationalization, or when a format cannot be found for the
+current locale.
+
+The value must be an integer from 0 to 6, where 0 means Sunday, 1 means
+Monday and so on.
+
.. setting:: FIXTURE_DIRS
FIXTURE_DIRS
@@ -783,6 +797,21 @@ locales have different formats. For example, U.S. English would say
See :ttag:`allowed date format strings <now>`. See also ``DATE_FORMAT``,
``DATETIME_FORMAT``, ``TIME_FORMAT`` and ``YEAR_MONTH_FORMAT``.
+.. setting:: NUMBER_GROUPING
+
+NUMBER_GROUPING
+----------------
+
+Default: ``0``
+
+Number of digits grouped together on the integer part of a number. Common use
+is to display a thousand separator. If this setting is ``0``, then, no grouping
+will be applied to the number. If this setting is greater than ``0`` then the
+setting ``THOUSAND_SEPARATOR`` will be used as the separator between those
+groups.
+
+See also ``THOUSAND_SEPARATOR``
+
.. setting:: PREPEND_WWW
PREPEND_WWW
@@ -1119,6 +1148,18 @@ The name of the method to use for starting the test suite. See
.. _Testing Django Applications: ../testing/
+.. setting:: THOUSAND_SEPARATOR
+
+THOUSAND_SEPARATOR
+------------------
+
+Default ``,`` (Comma)
+
+Default thousand separator used when formatting numbers. This setting is
+used only when ``NUMBER_GROUPPING`` is set.
+
+See also ``NUMBER_GROUPPING``, ``DECIMAL_SEPARATOR``
+
.. setting:: TIME_FORMAT
TIME_FORMAT
@@ -1186,6 +1227,20 @@ A boolean that specifies whether to output the "Etag" header. This saves
bandwidth but slows down performance. This is only used if ``CommonMiddleware``
is installed (see :ref:`topics-http-middleware`).
+.. setting:: USE_FORMAT_I18N
+
+USE_FORMAT_I18N
+---------------
+
+Default ``False``
+
+A boolean that specifies if data will be localized by default or not. If this is
+set to ``True``, Django will display numbers and dates using the format of the
+current locale. It is required to set ``USE_I18N`` to ``True`` to allow data
+format localization.
+
+See also ``USE_I18N``
+
.. setting:: USE_I18N
USE_I18N
@@ -1198,6 +1253,8 @@ enabled. This provides an easy way to turn it off, for performance. If this is
set to ``False``, Django will make some optimizations so as not to load the
internationalization machinery.
+See also ``USE_FORMAT_I18N``
+
.. setting:: YEAR_MONTH_FORMAT
YEAR_MONTH_FORMAT
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
index dc875c6a31..a67b84712d 100644
--- a/tests/regressiontests/i18n/tests.py
+++ b/tests/regressiontests/i18n/tests.py
@@ -65,13 +65,70 @@ status.
>>> print s
Password
->>> from django.utils.formats import getformat
+Localization of dates and numbers
+
+>>> import datetime
+>>> import decimal
+>>> from django.utils.formats import getformat, date_format, number_format, localize
+
+>>> n = decimal.Decimal('66666.666')
+>>> d = datetime.date(2009, 7, 6)
+>>> dt = datetime.datetime(2009, 7, 6, 20, 50)
+
+Locale independent
+
+>>> number_format(n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=',')
+'66,666.66'
+>>> number_format(n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B')
+'6B6B6B6B6BA6'
+
+English locale
+
>>> activate('en')
>>> getformat('DATE_FORMAT')
'N j, Y'
+>>> getformat('FIRST_DAY_OF_WEEK')
+0
+>>> getformat('DECIMAL_SEPARATOR')
+'.'
+>>> date_format(d)
+'July 6, 2009'
+>>> date_format(d, 'YEAR_MONTH_FORMAT')
+'July 2009'
+>>> date_format(d, 'SHORT_DATETIME_FORMAT')
+'07/06/2009 8:50 p.m.'
+>>> localize('No localizable')
+'No localizable'
+>>> localize(n)
+'66666.666'
+>>> localize(d)
+'July 6, 2009'
+>>> localize(dt)
+'July 6, 2009, 8:50 p.m.'
+
+Catalan locale
+
>>> activate('ca')
>>> getformat('DATE_FORMAT')
'j \de N \de Y'
+>>> getformat('FIRST_DAY_OF_WEEK')
+1
+>>> getformat('DECIMAL_SEPARATOR')
+','
+>>> date_format(d)
+'6 de juliol de 2009'
+>>> date_format(d, 'YEAR_MONTH_FORMAT')
+'juliol de 2009'
+>>> date_format(d, 'SHORT_DATETIME_FORMAT')
+'06/07/2009 20:50'
+>>> localize('No localizable')
+'No localizable'
+>>> localize(n)
+'66.666,666'
+>>> localize(d)
+'6 de juliol de 2009'
+>>> localize(dt)
+'6 de juliol de 2009 a les 20:50'
"""