Commits

Anonymous committed 981a69b

[soc2009/i18n] formatting system in development

  • Participants
  • Parent commits e18d768
  • Branches soc2009/i18n-improvements

Comments (0)

Files changed (8)

File django/conf/global_settings.py

 # 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'
 
 # 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?

File django/conf/locale/ca/formats.py

 
 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'
 
 
 DECIMAL_SEPARATOR = ','
 THOUSAND_SEPARATOR = '.'
+NUMBER_GROUPING = 3
 
-

File django/contrib/admin/templatetags/admin_list.py

 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
                 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)

File django/template/defaultfilters.py

 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()
 
         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

File django/utils/formats.py

 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 
     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):
     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:
 
 # 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):

File django/utils/numberformat.py

-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:
     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)

File docs/ref/settings.txt

 be useful for some test setups, and should never be used on a live
 site.
 
-
 .. setting:: DECIMAL_SEPARATOR
 
 DECIMAL_SEPARATOR
 
 Default: ``'.'`` (Dot)
 
-The default decimal separator used when formatting decimal numbers.
+Default decimal separator used when formatting decimal numbers.
 
 .. setting:: DEFAULT_CHARSET
 
 
 .. _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
 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
 
 .. _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
 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
 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

File tests/regressiontests/i18n/tests.py

 >>> 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'
 
 """