Code

Ticket #7980: i18n_simple.diff

File i18n_simple.diff, 79.4 KB (added by garcia_marc, 5 years ago)

diff of all work developed on the [soc2009/i18n] with .po and formats files removed

Line 
1Index: django/conf/global_settings.py
2===================================================================
3--- django/conf/global_settings.py      (.../trunk)     (revision 11464)
4+++ django/conf/global_settings.py      (.../branches/soc2009/i18n-improvements)        (revision 11464)
5@@ -1,3 +1,4 @@
6+# -*- encoding: utf-8 -*-
7 # Default Django settings. Override these with settings in the module
8 # pointed-to by the DJANGO_SETTINGS_MODULE environment variable.
9 
10@@ -75,7 +76,7 @@
11     ('lt', gettext_noop('Lithuanian')),
12     ('mk', gettext_noop('Macedonian')),
13     ('nl', gettext_noop('Dutch')),
14-    ('no', gettext_noop('Norwegian')),
15+    ('nb', gettext_noop(u'Norwegian Bokmål')),
16     ('pl', gettext_noop('Polish')),
17     ('pt', gettext_noop('Portuguese')),
18     ('pt-br', gettext_noop('Brazilian Portuguese')),
19@@ -103,6 +104,10 @@
20 LOCALE_PATHS = ()
21 LANGUAGE_COOKIE_NAME = 'django_language'
22 
23+# If you set this to True, Django will format dates, numbers and calendars
24+# according to user current locale
25+USE_FORMAT_I18N = False
26+
27 # Not-necessarily-technical managers of the site. They get broken link
28 # notifications and other various e-mails.
29 MANAGERS = ADMINS
30@@ -255,6 +260,12 @@
31 # you'd pass directly to os.chmod; see http://docs.python.org/lib/os-file-dir.html.
32 FILE_UPLOAD_PERMISSIONS = None
33 
34+# Python module path where user will place custom format definition.
35+# The directory where this setting is pointing should contain subdirectories
36+# named as the locales, containing a formats.py file
37+# (i.e. "myproject.locale" for myproject/locale/en/formats.py etc. use)
38+FORMAT_MODULE_PATH = None
39+
40 # Default formatting for date objects. See all available format strings here:
41 # http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
42 DATE_FORMAT = 'N j, Y'
43@@ -277,6 +288,72 @@
44 # http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
45 MONTH_DAY_FORMAT = 'F j'
46 
47+# Default shortformatting for date objects. See all available format strings here:
48+# http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
49+SHORT_DATE_FORMAT = 'm/d/Y'
50+
51+# Default short formatting for datetime objects.
52+# See all available format strings here:
53+# http://docs.djangoproject.com/en/dev/ref/templates/builtins/#now
54+SHORT_DATETIME_FORMAT = 'm/d/Y P'
55+
56+# Default formats tried to parse dates from input boxes
57+# These formats are tried in the specified order
58+# See all available format string here:
59+# http://docs.python.org/library/datetime.html#strftime-behavior
60+# * Note that these format strings are different from the ones to display dates
61+DATE_INPUT_FORMATS = (
62+    '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
63+    '%b %d %Y', '%b %d, %Y',            # 'Oct 25 2006', 'Oct 25, 2006'
64+    '%d %b %Y', '%d %b, %Y',            # '25 Oct 2006', '25 Oct, 2006'
65+    '%B %d %Y', '%B %d, %Y',            # 'October 25 2006', 'October 25, 2006'
66+    '%d %B %Y', '%d %B, %Y',            # '25 October 2006', '25 October, 2006'
67+)
68+
69+# Default formats tried to parse times from input boxes
70+# These formats are tried in the specified order
71+# See all available format string here:
72+# http://docs.python.org/library/datetime.html#strftime-behavior
73+# * Note that these format strings are different from the ones to display dates
74+TIME_INPUT_FORMATS = (
75+    '%H:%M:%S',     # '14:30:59'
76+    '%H:%M',        # '14:30'
77+)
78+
79+# Default formats tried to parse dates and times from input boxes
80+# These formats are tried in the specified order
81+# See all available format string here:
82+# http://docs.python.org/library/datetime.html#strftime-behavior
83+# * Note that these format strings are different from the ones to display dates
84+DATETIME_INPUT_FORMATS = (
85+    '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
86+    '%Y-%m-%d %H:%M',        # '2006-10-25 14:30'
87+    '%Y-%m-%d',              # '2006-10-25'
88+    '%m/%d/%Y %H:%M:%S',     # '10/25/2006 14:30:59'
89+    '%m/%d/%Y %H:%M',        # '10/25/2006 14:30'
90+    '%m/%d/%Y',              # '10/25/2006'
91+    '%m/%d/%y %H:%M:%S',     # '10/25/06 14:30:59'
92+    '%m/%d/%y %H:%M',        # '10/25/06 14:30'
93+    '%m/%d/%y',              # '10/25/06'
94+)
95+
96+# First day of week, to be used on calendars
97+# 0 means Sunday, 1 means Monday...
98+FIRST_DAY_OF_WEEK = 0
99+
100+# Decimal separator symbol
101+DECIMAL_SEPARATOR = '.'
102+
103+# Boolean that sets whether to add thousand separator when formatting numbers
104+USE_THOUSAND_SEPARATOR = False
105+
106+# Number of digits that will be togheter, when spliting them by THOUSAND_SEPARATOR
107+# 0 means no grouping, 3 means splitting by thousands...
108+NUMBER_GROUPING = 0
109+
110+# Thousand separator symbol
111+THOUSAND_SEPARATOR = ','
112+
113 # Do you want to manage transactions manually?
114 # Hint: you really don't!
115 TRANSACTIONS_MANAGED = False
116Index: django/forms/extras/widgets.py
117===================================================================
118--- django/forms/extras/widgets.py      (.../trunk)     (revision 11464)
119+++ django/forms/extras/widgets.py      (.../branches/soc2009/i18n-improvements)        (revision 11464)
120@@ -8,6 +8,8 @@
121 from django.forms.widgets import Widget, Select
122 from django.utils.dates import MONTHS
123 from django.utils.safestring import mark_safe
124+from django.utils.formats import getformat
125+from django.conf import settings
126 
127 __all__ = ('SelectDateWidget',)
128 
129@@ -45,38 +47,27 @@
130                 if match:
131                     year_val, month_val, day_val = [int(v) for v in match.groups()]
132 
133+        choices = [(i, i) for i in self.years]
134+        year_html = self.create_select(name, self.year_field, value, year_val, choices)
135+        choices = MONTHS.items()
136+        month_html = self.create_select(name, self.month_field, value, month_val, choices)
137+        choices = [(i, i) for i in range(1, 32)]
138+        day_html = self.create_select(name, self.day_field, value, day_val,  choices)
139+
140+        format = getformat('DATE_FORMAT')
141+        escaped = False
142         output = []
143-
144-        if 'id' in self.attrs:
145-            id_ = self.attrs['id']
146-        else:
147-            id_ = 'id_%s' % name
148-
149-        month_choices = MONTHS.items()
150-        if not (self.required and value):
151-            month_choices.append(self.none_value)
152-        month_choices.sort()
153-        local_attrs = self.build_attrs(id=self.month_field % id_)
154-        s = Select(choices=month_choices)
155-        select_html = s.render(self.month_field % name, month_val, local_attrs)
156-        output.append(select_html)
157-
158-        day_choices = [(i, i) for i in range(1, 32)]
159-        if not (self.required and value):
160-            day_choices.insert(0, self.none_value)
161-        local_attrs['id'] = self.day_field % id_
162-        s = Select(choices=day_choices)
163-        select_html = s.render(self.day_field % name, day_val, local_attrs)
164-        output.append(select_html)
165-
166-        year_choices = [(i, i) for i in self.years]
167-        if not (self.required and value):
168-            year_choices.insert(0, self.none_value)
169-        local_attrs['id'] = self.year_field % id_
170-        s = Select(choices=year_choices)
171-        select_html = s.render(self.year_field % name, year_val, local_attrs)
172-        output.append(select_html)
173-
174+        for char in format:
175+            if escaped:
176+                escaped = False
177+            elif char == '\\':
178+                escaped = True
179+            elif char in 'Yy':
180+                output.append(year_html)
181+            elif char in 'bFMmNn':
182+                output.append(month_html)
183+            elif char in 'dj':
184+                output.append(day_html)
185         return mark_safe(u'\n'.join(output))
186 
187     def id_for_label(self, id_):
188@@ -90,5 +81,27 @@
189         if y == m == d == "0":
190             return None
191         if y and m and d:
192-            return '%s-%s-%s' % (y, m, d)
193+            if settings.USE_FORMAT_I18N:
194+                input_format = getformat('DATE_INPUT_FORMATS')[0]
195+                try:
196+                    date_value = datetime.date(int(y), int(m), int(d))
197+                except ValueError:
198+                    pass
199+                else:
200+                    return date_value.strftime(input_format)
201+            else:
202+                return '%s-%s-%s' % (y, m, d)
203         return data.get(name, None)
204+
205+    def create_select(self, name, field, value, val, choices):
206+        if 'id' in self.attrs:
207+            id_ = self.attrs['id']
208+        else:
209+            id_ = 'id_%s' % name
210+        if not (self.required and value):
211+            choices.insert(0, self.none_value)
212+        local_attrs = self.build_attrs(id=field % id_)
213+        s = Select(choices=choices)
214+        select_html = s.render(field % name, val, local_attrs)
215+        return select_html
216+
217Index: django/forms/fields.py
218===================================================================
219--- django/forms/fields.py      (.../trunk)     (revision 11464)
220+++ django/forms/fields.py      (.../branches/soc2009/i18n-improvements)        (revision 11464)
221@@ -26,6 +26,7 @@
222 import django.core.exceptions
223 from django.utils.translation import ugettext_lazy as _
224 from django.utils.encoding import smart_unicode, smart_str
225+from django.utils.formats import getformat
226 
227 from util import ErrorList, ValidationError
228 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
229@@ -33,9 +34,7 @@
230 
231 __all__ = (
232     'Field', 'CharField', 'IntegerField',
233-    'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
234-    'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
235-    'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 'TimeField',
236+    'DateField', 'TimeField', 'DateTimeField', 'TimeField',
237     'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
238     'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
239     'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
240@@ -210,7 +209,9 @@
241         if not self.required and value in EMPTY_VALUES:
242             return None
243         try:
244-            value = float(value)
245+            # We always accept dot as decimal separator
246+            if isinstance(value, str) or isinstance(value, unicode):
247+                value = float(value.replace(getformat('DECIMAL_SEPARATOR'), '.'))
248         except (ValueError, TypeError):
249             raise ValidationError(self.error_messages['invalid'])
250         if self.max_value is not None and value > self.max_value:
251@@ -246,7 +247,9 @@
252             return None
253         value = smart_str(value).strip()
254         try:
255-            value = Decimal(value)
256+            # We always accept dot as decimal separator
257+            if isinstance(value, str) or isinstance(value, unicode):
258+                value = Decimal(value.replace(getformat('DECIMAL_SEPARATOR'), '.'))
259         except DecimalException:
260             raise ValidationError(self.error_messages['invalid'])
261 
262@@ -274,14 +277,6 @@
263             raise ValidationError(self.error_messages['max_whole_digits'] % (self.max_digits - self.decimal_places))
264         return value
265 
266-DEFAULT_DATE_INPUT_FORMATS = (
267-    '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
268-    '%b %d %Y', '%b %d, %Y',            # 'Oct 25 2006', 'Oct 25, 2006'
269-    '%d %b %Y', '%d %b, %Y',            # '25 Oct 2006', '25 Oct, 2006'
270-    '%B %d %Y', '%B %d, %Y',            # 'October 25 2006', 'October 25, 2006'
271-    '%d %B %Y', '%d %B, %Y',            # '25 October 2006', '25 October, 2006'
272-)
273-
274 class DateField(Field):
275     widget = DateInput
276     default_error_messages = {
277@@ -290,7 +285,7 @@
278 
279     def __init__(self, input_formats=None, *args, **kwargs):
280         super(DateField, self).__init__(*args, **kwargs)
281-        self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
282+        self.input_formats = input_formats
283 
284     def clean(self, value):
285         """
286@@ -304,18 +299,13 @@
287             return value.date()
288         if isinstance(value, datetime.date):
289             return value
290-        for format in self.input_formats:
291+        for format in self.input_formats or getformat('DATE_INPUT_FORMATS'):
292             try:
293                 return datetime.date(*time.strptime(value, format)[:3])
294             except ValueError:
295                 continue
296         raise ValidationError(self.error_messages['invalid'])
297 
298-DEFAULT_TIME_INPUT_FORMATS = (
299-    '%H:%M:%S',     # '14:30:59'
300-    '%H:%M',        # '14:30'
301-)
302-
303 class TimeField(Field):
304     widget = TimeInput
305     default_error_messages = {
306@@ -324,7 +314,7 @@
307 
308     def __init__(self, input_formats=None, *args, **kwargs):
309         super(TimeField, self).__init__(*args, **kwargs)
310-        self.input_formats = input_formats or DEFAULT_TIME_INPUT_FORMATS
311+        self.input_formats = input_formats
312 
313     def clean(self, value):
314         """
315@@ -336,25 +326,13 @@
316             return None
317         if isinstance(value, datetime.time):
318             return value
319-        for format in self.input_formats:
320+        for format in self.input_formats or getformat('TIME_INPUT_FORMATS'):
321             try:
322                 return datetime.time(*time.strptime(value, format)[3:6])
323             except ValueError:
324                 continue
325         raise ValidationError(self.error_messages['invalid'])
326 
327-DEFAULT_DATETIME_INPUT_FORMATS = (
328-    '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
329-    '%Y-%m-%d %H:%M',        # '2006-10-25 14:30'
330-    '%Y-%m-%d',              # '2006-10-25'
331-    '%m/%d/%Y %H:%M:%S',     # '10/25/2006 14:30:59'
332-    '%m/%d/%Y %H:%M',        # '10/25/2006 14:30'
333-    '%m/%d/%Y',              # '10/25/2006'
334-    '%m/%d/%y %H:%M:%S',     # '10/25/06 14:30:59'
335-    '%m/%d/%y %H:%M',        # '10/25/06 14:30'
336-    '%m/%d/%y',              # '10/25/06'
337-)
338-
339 class DateTimeField(Field):
340     widget = DateTimeInput
341     default_error_messages = {
342@@ -363,7 +341,7 @@
343 
344     def __init__(self, input_formats=None, *args, **kwargs):
345         super(DateTimeField, self).__init__(*args, **kwargs)
346-        self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
347+        self.input_formats = input_formats
348 
349     def clean(self, value):
350         """
351@@ -383,7 +361,7 @@
352             if len(value) != 2:
353                 raise ValidationError(self.error_messages['invalid'])
354             value = '%s %s' % tuple(value)
355-        for format in self.input_formats:
356+        for format in self.input_formats or getformat('DATETIME_INPUT_FORMATS'):
357             try:
358                 return datetime.datetime(*time.strptime(value, format)[:6])
359             except ValueError:
360Index: django/forms/widgets.py
361===================================================================
362--- django/forms/widgets.py     (.../trunk)     (revision 11464)
363+++ django/forms/widgets.py     (.../branches/soc2009/i18n-improvements)        (revision 11464)
364@@ -15,6 +15,7 @@
365 from django.utils.translation import ugettext
366 from django.utils.encoding import StrAndUnicode, force_unicode
367 from django.utils.safestring import mark_safe
368+from django.utils.formats import localize
369 from django.utils import datetime_safe
370 from datetime import time
371 from util import flatatt
372@@ -213,7 +214,7 @@
373         final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
374         if value != '':
375             # Only add the 'value' attribute if a value is non-empty.
376-            final_attrs['value'] = force_unicode(value)
377+            final_attrs['value'] = force_unicode(localize(value, is_input=True))
378         return mark_safe(u'<input%s />' % flatatt(final_attrs))
379 
380 class TextInput(Input):
381Index: django/core/management/commands/importcldr.py
382===================================================================
383--- django/core/management/commands/importcldr.py       (.../trunk)     (revision 0)
384+++ django/core/management/commands/importcldr.py       (.../branches/soc2009/i18n-improvements)        (revision 11464)
385@@ -0,0 +1,221 @@
386+import sys
387+import os
388+import re
389+from optparse import make_option, OptionParser
390+
391+from django.core.management.base import LabelCommand, CommandError
392+
393+try:
394+    from lxml import etree
395+except ImportError:
396+    raise CommandError('You need to install `python-lxml` to run this script')
397+
398+FORMATS_FILE_NAME = 'formats.py'
399+FORMATS_FILE_HEADER = '''# -*- encoding: utf-8 -*-
400+# This file is distributed under the same license as the Django package.
401+#
402+
403+'''
404+
405+def quote(nodes, name,  locale, previous):
406+    if len(nodes):
407+        return "'%s'" % unicode(nodes[0].text).replace("'", "\\'")
408+    else:
409+        return None
410+
411+def convert_time(nodes, name,  locale, previous):
412+    SPECIAL_CHARS = ('a', 'A', 'b', 'B', 'd', 'D', 'f', 'F', 'g', 'G', 'h',
413+        'H', 'i', 'I', 'j', 'l', 'L', 'm', 'M', 'n', 'N', 'O', 'P', 'r',
414+        's', 'S', 't', 'T', 'U', 'w', 'W', 'y', 'Y', 'z', 'Z')
415+    FORMAT_STR_MAP = ( # not using a dict, because we have to apply formats in order
416+        ('dd', 'd'),
417+        ('d', 'j'),
418+        ('MMMM', 'F'),
419+        ('MMM', 'M'),
420+        ('MM', 'm'),
421+        ('M', 'n'),
422+        ('yyyy', 'Y'),
423+        ('yy', 'y'),
424+        ('y', 'Y'),
425+        ('hh', 'h'),
426+        ('h', 'g'),
427+        ('HH', 'H'),
428+        ('H', 'G'),
429+        ('mm', 'i'),
430+        ('ss', 's'),
431+        ('a', 'A'),
432+        ('LLLL', 'F'),
433+    )
434+    if len(nodes):
435+        original = nodes[0].text
436+        result = ''
437+        for cnt, segment in enumerate(original.split("'")):
438+            if cnt % 2:
439+                for char in SPECIAL_CHARS:
440+                    segment = segment.replace(char, '\\%s' % char)
441+                result += segment
442+            else:
443+                while segment:
444+                    found = False
445+                    for src, dst in FORMAT_STR_MAP:
446+                        if segment[0:len(src)] == src:
447+                            result += dst
448+                            segment = segment[len(src):]
449+                            found = True
450+                            break
451+                    if not found:
452+                        result += segment[0]
453+                        segment = segment[1:]
454+
455+        return "'%s'" % result
456+    else:
457+        return None
458+
459+def datetime(nodes, name, locale, previous):
460+    result = None
461+    if len(nodes) and 'DATE_FORMAT' in previous and 'TIME_FORMAT' in previous:
462+        result = nodes[0].text
463+        result = result.replace('{0}', previous['TIME_FORMAT'][1:-1])
464+        if name == 'SHORT_DATETIME_FORMAT' and 'SHORT_DATE_FORMAT' in previous:
465+            result = result.replace('{1}', previous['SHORT_DATE_FORMAT'][1:-1])
466+        else:
467+            result = result.replace('{1}', previous['DATE_FORMAT'][1:-1])
468+    if result:
469+        return "'%s'" % result
470+    else:
471+        return None
472+
473+FORMATS_MAP = [
474+    {
475+        'name': 'DATE_FORMAT',
476+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
477+        'pattern': "/ldml/dates/calendars/calendar[@type='gregorian']/dateFormats/dateFormatLength[@type='long']/dateFormat/pattern",
478+        'conversion': convert_time,
479+    },
480+    {
481+        'name': 'TIME_FORMAT',
482+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
483+        'pattern': "/ldml/dates/calendars/calendar[@type='gregorian']/timeFormats/timeFormatLength[@type='medium']/timeFormat/pattern",
484+        'conversion': convert_time,
485+    },
486+    {
487+        'name': 'DATETIME_FORMAT',
488+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
489+        'pattern': "/ldml/dates/calendars/calendar[@type='gregorian']/dateTimeFormats/dateTimeFormatLength[@type='long']/dateTimeFormat/pattern",
490+        'conversion': datetime,
491+    },
492+    {
493+        'name': 'YEAR_MONTH_FORMAT',
494+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
495+        'pattern': "/ldml/dates/calendars/calendar[@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[@id='yMMMM']",
496+        'conversion': convert_time,
497+    },
498+    {
499+        'name': 'MONTH_DAY_FORMAT',
500+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
501+        'pattern': "/ldml/dates/calendars/calendar[@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[@id='MMMMd']",
502+        'conversion': convert_time,
503+    },
504+    {
505+        'name': 'SHORT_DATE_FORMAT',
506+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
507+        'pattern': "/ldml/dates/calendars/calendar[@type='gregorian']/dateFormats/dateFormatLength[@type='medium']/dateFormat/pattern",
508+        'conversion': convert_time,
509+    },
510+    {
511+        'name': 'SHORT_DATETIME_FORMAT',
512+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
513+        'pattern': "/ldml/dates/calendars/calendar[@type='gregorian']/dateTimeFormats/dateTimeFormatLength[@type='short']/dateTimeFormat/pattern",
514+        'conversion': datetime,
515+    },
516+    {'name': 'FIRST_DAY_OF_WEEK'},
517+    {'name': 'DATE_INPUT_FORMATS'},
518+    {'name': 'TIME_INPUT_FORMATS'},
519+    {'name': 'DATETIME_INPUT_FORMATS'},
520+    {
521+        'name': 'DECIMAL_SEPARATOR',
522+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
523+        'pattern': "/ldml/numbers/symbols/decimal",
524+        'conversion': quote,
525+    },
526+    {
527+        'name': 'THOUSAND_SEPARATOR',
528+        'file': os.path.join('common', 'main', '%(locale)s.xml'),
529+        'pattern': "/ldml/numbers/symbols/group",
530+        'conversion': quote,
531+    },
532+    {'name': 'NUMBER_GROUPING'},
533+]
534+"""
535+"""
536+
537+def get_locales(django_locale_dir, locale=None):
538+    if locale:
539+        yield locale
540+    else:
541+        locale_re = re.compile('[a-z]{2}(_[A-Z]{2})?')
542+        for locale in os.listdir(django_locale_dir):
543+            if locale_re.match(locale):
544+                yield locale
545+
546+def import_cldr(cldr_dir, locale=None, overwrite=False):
547+    """
548+    For every locale defined in Django, get from the CLDR locale file all
549+    settings defined in output_structure, and write the result to the
550+    locale directories on Django.
551+    """
552+    if not os.path.isdir(cldr_dir):
553+        raise Exception, "Specified CLDR directory '%s' does not exist" % cldr_dir
554+
555+    import django
556+    django_locale_dir = os.path.join(os.path.dirname(django.__file__), 'conf', 'locale')
557+
558+    for locale in get_locales(django_locale_dir, locale):
559+        output_filename = os.path.join(django_locale_dir, locale, FORMATS_FILE_NAME)
560+        if os.path.isfile(output_filename) and not overwrite:
561+            print "'%s' locale already exists. Skipping" % locale
562+        else:
563+            result = {}
564+            output_file = open(output_filename, 'w')
565+            output_file.write(FORMATS_FILE_HEADER)
566+            for format in FORMATS_MAP:
567+                if 'file' in format:
568+                    cldr_file = os.path.join(cldr_dir, format['file'] % dict(locale=locale))
569+                    tree = etree.parse(cldr_file) # TODO: error control
570+                    try:
571+                        original_value = tree.xpath(format['pattern'])
572+                    except IndexError:
573+                        output_file.write('# %s = \n' % (format['name']))
574+                    else:
575+                        value = format['conversion'](original_value, format['name'], locale, result)
576+                        if value:
577+                            output_file.write('%s = %s\n' % (format['name'], value.encode('utf8')))
578+                            result[format['name']] = value
579+                        else:
580+                            output_file.write('# %s = \n' % (format['name']))
581+                else:
582+                    output_file.write('# %s = \n' % (format['name']))
583+            output_file.close()
584+
585+            init_filename = os.path.join(django_locale_dir, locale, '__init__.py')
586+            open(init_filename, 'a').close()
587+
588+class Command(LabelCommand):
589+    option_list = LabelCommand.option_list + (
590+        make_option('--locale', '-l', dest='locale',
591+            help='The locale to process. Default is to process all.'),
592+    ) + (
593+        make_option('--overwite', '-o', action='store_true', dest='overwrite',
594+            help='Wheter to overwrite format definitions of locales that already have one.'),
595+    )
596+    help = 'Creates format definition files for locales, importing data from the CLDR.'
597+    args = '[cldrpath]'
598+    label = 'CLDR path'
599+    requires_model_validation = False
600+    can_import_settings = False
601+
602+    def handle_label(self, cldrpath, **options):
603+        locale = options.get('locale')
604+        overwrite = options.get('overwrite')
605+        import_cldr(cldrpath, locale, overwrite)
606+
607Index: django/views/i18n.py
608===================================================================
609--- django/views/i18n.py        (.../trunk)     (revision 11464)
610+++ django/views/i18n.py        (.../branches/soc2009/i18n-improvements)        (revision 11464)
611@@ -3,6 +3,7 @@
612 from django.utils import importlib
613 from django.utils.translation import check_for_language, activate, to_locale, get_language
614 from django.utils.text import javascript_quote
615+from django.utils.formats import project_formats_module, django_formats_module
616 import os
617 import gettext as gettext_module
618 
619@@ -32,6 +33,25 @@
620                 response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code)
621     return response
622 
623+def get_formats():
624+    """
625+    Returns an iterator over all formats in formats file
626+    """
627+    FORMAT_SETTINGS = ('DATE_FORMAT', 'DATETIME_FORMAT', 'TIME_FORMAT',
628+        'YEAR_MONTH_FORMAT', 'MONTH_DAY_FORMAT', 'SHORT_DATE_FORMAT',
629+        'SHORT_DATETIME_FORMAT', 'FIRST_DAY_OF_WEEK', 'DECIMAL_SEPARATOR',
630+        'THOUSAND_SEPARATOR', 'NUMBER_GROUPING')
631+
632+    result = {}
633+    for module in (settings, django_formats_module(), project_formats_module()):
634+        if module:
635+            for attr in FORMAT_SETTINGS:
636+                try:
637+                    result[attr] = getattr(module, attr)
638+                except AttributeError:
639+                    pass
640+    return result
641+   
642 NullSource = """
643 /* gettext identity library */
644 
645@@ -185,10 +205,13 @@
646         else:
647             raise TypeError, k
648     csrc.sort()
649-    for k,v in pdict.items():
650+    for k, v in pdict.items():
651         src.append("catalog['%s'] = [%s];\n" % (javascript_quote(k), ','.join(["''"]*(v+1))))
652+    for k, v in get_formats().items():
653+        src.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(unicode(v))))
654     src.extend(csrc)
655     src.append(LibFoot)
656     src.append(InterPolate)
657     src = ''.join(src)
658     return http.HttpResponse(src, 'text/javascript')
659+
660Index: django/utils/numberformat.py
661===================================================================
662--- django/utils/numberformat.py        (.../trunk)     (revision 0)
663+++ django/utils/numberformat.py        (.../branches/soc2009/i18n-improvements)        (revision 11464)
664@@ -0,0 +1,40 @@
665+from django.conf import settings
666+
667+def format(number, decimal_sep, decimal_pos, grouping=0, thousand_sep=''):
668+    """
669+    Gets a number (as a number or string), and returns it as a string,
670+    using formats definied as arguments:
671+     * decimal_sep: Decimal separator symbol (for example ".")
672+     * decimal_pos: Number of decimal positions
673+     * grouping: Number of digits in every group limited by thousand separator
674+     * thousand_sep: Thousand separator symbol (for example ",")
675+    """
676+    # sign
677+    if number < 0:
678+        sign = '-'
679+    else:
680+        sign = ''
681+    # decimal part
682+    str_number = unicode(number)
683+    if str_number[0] == '-':
684+        str_number = str_number[1:]
685+    if '.' in str_number:
686+        int_part, dec_part = str_number.split('.')
687+        if decimal_pos:
688+            dec_part = dec_part[:decimal_pos]
689+    else:
690+        int_part, dec_part = str_number, ''
691+    if decimal_pos:
692+        dec_part = dec_part + ('0' * (decimal_pos - len(dec_part)))
693+    if dec_part: dec_part = decimal_sep + dec_part
694+    # grouping
695+    if settings.USE_THOUSAND_SEPARATOR and grouping:
696+        int_part_gd = ''
697+        for cnt, digit in enumerate(int_part[::-1]):
698+            if cnt and not cnt % grouping:
699+                int_part_gd += thousand_sep
700+            int_part_gd += digit
701+        int_part = int_part_gd[::-1]
702+
703+    return sign + int_part + dec_part
704+
705Index: django/utils/translation/trans_real.py
706===================================================================
707--- django/utils/translation/trans_real.py      (.../trunk)     (revision 11464)
708+++ django/utils/translation/trans_real.py      (.../branches/soc2009/i18n-improvements)        (revision 11464)
709@@ -266,15 +266,16 @@
710     translation object to use. If no current translation is activated, the
711     message will be run through the default translation object.
712     """
713+    eol_message = message.replace('\r\n', '\n').replace('\r', '\n')
714     global _default, _active
715     t = _active.get(currentThread(), None)
716     if t is not None:
717-        result = getattr(t, translation_function)(message)
718+        result = getattr(t, translation_function)(eol_message)
719     else:
720         if _default is None:
721             from django.conf import settings
722             _default = translation(settings.LANGUAGE_CODE)
723-        result = getattr(_default, translation_function)(message)
724+        result = getattr(_default, translation_function)(eol_message)
725     if isinstance(message, SafeData):
726         return mark_safe(result)
727     return result
728@@ -389,39 +390,6 @@
729 
730     return settings.LANGUAGE_CODE
731 
732-def get_date_formats():
733-    """
734-    Checks whether translation files provide a translation for some technical
735-    message ID to store date and time formats. If it doesn't contain one, the
736-    formats provided in the settings will be used.
737-    """
738-    from django.conf import settings
739-    date_format = ugettext('DATE_FORMAT')
740-    datetime_format = ugettext('DATETIME_FORMAT')
741-    time_format = ugettext('TIME_FORMAT')
742-    if date_format == 'DATE_FORMAT':
743-        date_format = settings.DATE_FORMAT
744-    if datetime_format == 'DATETIME_FORMAT':
745-        datetime_format = settings.DATETIME_FORMAT
746-    if time_format == 'TIME_FORMAT':
747-        time_format = settings.TIME_FORMAT
748-    return date_format, datetime_format, time_format
749-
750-def get_partial_date_formats():
751-    """
752-    Checks whether translation files provide a translation for some technical
753-    message ID to store partial date formats. If it doesn't contain one, the
754-    formats provided in the settings will be used.
755-    """
756-    from django.conf import settings
757-    year_month_format = ugettext('YEAR_MONTH_FORMAT')
758-    month_day_format = ugettext('MONTH_DAY_FORMAT')
759-    if year_month_format == 'YEAR_MONTH_FORMAT':
760-        year_month_format = settings.YEAR_MONTH_FORMAT
761-    if month_day_format == 'MONTH_DAY_FORMAT':
762-        month_day_format = settings.MONTH_DAY_FORMAT
763-    return year_month_format, month_day_format
764-
765 dot_re = re.compile(r'\S')
766 def blankout(src, char):
767     """
768@@ -537,3 +505,42 @@
769         result.append((lang, priority))
770     result.sort(lambda x, y: -cmp(x[1], y[1]))
771     return result
772+
773+# get_date_formats and get_partial_date_formats aren't used anymore from django
774+# itself, and are kept for backward compatibility.
775+# Note that it's also important to keep format names maked for translation, so
776+# for compatibility we still want to have formats on translation catalogs. That
777+# makes template code like {{ my_date|date:_('DATE_FORMAT') }} go on working
778+def get_date_formats():
779+    """
780+    Checks whether translation files provide a translation for some technical
781+    message ID to store date and time formats. If it doesn't contain one, the
782+    formats provided in the settings will be used.
783+    """
784+    from django.conf import settings
785+    date_format = ugettext('DATE_FORMAT')
786+    datetime_format = ugettext('DATETIME_FORMAT')
787+    time_format = ugettext('TIME_FORMAT')
788+    if date_format == 'DATE_FORMAT':
789+        date_format = settings.DATE_FORMAT
790+    if datetime_format == 'DATETIME_FORMAT':
791+        datetime_format = settings.DATETIME_FORMAT
792+    if time_format == 'TIME_FORMAT':
793+        time_format = settings.TIME_FORMAT
794+    return date_format, datetime_format, time_format
795+
796+def get_partial_date_formats():
797+    """
798+    Checks whether translation files provide a translation for some technical
799+    message ID to store partial date formats. If it doesn't contain one, the
800+    formats provided in the settings will be used.
801+    """
802+    from django.conf import settings
803+    year_month_format = ugettext('YEAR_MONTH_FORMAT')
804+    month_day_format = ugettext('MONTH_DAY_FORMAT')
805+    if year_month_format == 'YEAR_MONTH_FORMAT':
806+        year_month_format = settings.YEAR_MONTH_FORMAT
807+    if month_day_format == 'MONTH_DAY_FORMAT':
808+        month_day_format = settings.MONTH_DAY_FORMAT
809+    return year_month_format, month_day_format
810+
811Index: django/utils/translation/trans_null.py
812===================================================================
813--- django/utils/translation/trans_null.py      (.../trunk)     (revision 11464)
814+++ django/utils/translation/trans_null.py      (.../branches/soc2009/i18n-improvements)        (revision 11464)
815@@ -18,10 +18,10 @@
816 deactivate = deactivate_all = lambda: None
817 get_language = lambda: settings.LANGUAGE_CODE
818 get_language_bidi = lambda: settings.LANGUAGE_CODE in settings.LANGUAGES_BIDI
819-get_date_formats = lambda: (settings.DATE_FORMAT, settings.DATETIME_FORMAT, settings.TIME_FORMAT)
820-get_partial_date_formats = lambda: (settings.YEAR_MONTH_FORMAT, settings.MONTH_DAY_FORMAT)
821 check_for_language = lambda x: True
822 
823+# date formats shouldn't be used using gettext anymore. This
824+# is kept for backward compatibility
825 TECHNICAL_ID_MAP = {
826     "DATE_WITH_TIME_FULL": settings.DATETIME_FORMAT,
827     "DATE_FORMAT": settings.DATE_FORMAT,
828@@ -51,3 +51,8 @@
829 
830 def get_language_from_request(request):
831     return settings.LANGUAGE_CODE
832+
833+# get_date_formats and get_partial_date_formats aren't used anymore from django
834+# itself, and are kept for backward compatibility.
835+get_date_formats = lambda: (settings.DATE_FORMAT, settings.DATETIME_FORMAT, settings.TIME_FORMAT)
836+get_partial_date_formats = lambda: (settings.YEAR_MONTH_FORMAT, settings.MONTH_DAY_FORMAT)
837Index: django/utils/formats.py
838===================================================================
839--- django/utils/formats.py     (.../trunk)     (revision 0)
840+++ django/utils/formats.py     (.../branches/soc2009/i18n-improvements)        (revision 11464)
841@@ -0,0 +1,95 @@
842+import decimal
843+import datetime
844+
845+from django.conf import settings
846+from django.utils.translation import get_language
847+from django.utils.importlib import import_module
848+from django.utils import dateformat
849+from django.utils import numberformat
850+
851+def project_formats_module():
852+    """
853+    Returns the formats module for the current locale, defined
854+    on the project
855+    """
856+    if settings.FORMAT_MODULE_PATH:
857+        try:
858+            return import_module('.formats', '%s.%s' % (settings.FORMAT_MODULE_PATH, get_language()))
859+        except ImportError:
860+            pass
861+    return None
862+
863+def django_formats_module():
864+    """
865+    Returns the formats module for the current locale, defined
866+    on Django
867+    """
868+    try:
869+        return import_module('.formats', 'django.conf.locale.%s' % get_language())
870+    except ImportError:
871+        return None
872+
873+def getformat(format_type):
874+    """
875+    For a specific format type, returns the format for the
876+    current language (locale) defaulting to the format on settings.
877+    format_type is the name of the format, for example 'DATE_FORMAT'
878+    """
879+    if settings.USE_I18N and settings.USE_FORMAT_I18N:
880+        for module in (project_formats_module(), django_formats_module()):
881+            if module:
882+                try:
883+                    return getattr(module, format_type)
884+                except AttributeError:
885+                    pass
886+    return getattr(settings, format_type)
887+
888+def date_format(value, format=None):
889+    """
890+    Formats a datetime.date or datetime.datetime object using a
891+    localizable format
892+    """
893+    return dateformat.format(value, getformat(format or 'DATE_FORMAT'))
894+
895+def number_format(value, decimal_pos=None):
896+    """
897+    Formats a numeric value using localization settings
898+    """
899+    return numberformat.format(
900+        value,
901+        getformat('DECIMAL_SEPARATOR'),
902+        decimal_pos,
903+        getformat('NUMBER_GROUPING'),
904+        getformat('THOUSAND_SEPARATOR'),
905+    )
906+
907+def localize(value, is_input=False):
908+    """
909+    Checks value, and if it has a localizable type (date,
910+    number...) it returns the value as a string using
911+    current locale format
912+    """
913+    if settings.USE_I18N and settings.USE_FORMAT_I18N:
914+        if isinstance(value, decimal.Decimal):
915+            return number_format(value)
916+        elif isinstance(value, float):
917+            return number_format(value)
918+        elif isinstance(value, int):
919+            return number_format(value)
920+        elif isinstance(value, datetime.datetime):
921+            if not is_input:
922+                return date_format(value, 'DATETIME_FORMAT')
923+            else:
924+                return value.strftime(getformat('DATETIME_INPUT_FORMATS')[0])
925+        elif isinstance(value, datetime.date):
926+            if not is_input:
927+                return date_format(value)
928+            else:
929+                return value.strftime(getformat('DATE_INPUT_FORMATS')[0])
930+        elif isinstance(value, datetime.time):
931+            if not is_input:
932+                return date_format(value, 'TIME_FORMAT')
933+            else:
934+                return value.strftime(getformat('TIME_INPUT_FORMATS')[0])
935+    return value
936+
937Index: django/contrib/admin/media/js/calendar.js
938===================================================================
939--- django/contrib/admin/media/js/calendar.js   (.../trunk)     (revision 11464)
940+++ django/contrib/admin/media/js/calendar.js   (.../branches/soc2009/i18n-improvements)        (revision 11464)
941@@ -25,6 +25,7 @@
942 var CalendarNamespace = {
943     monthsOfYear: gettext('January February March April May June July August September October November December').split(' '),
944     daysOfWeek: gettext('S M T W T F S').split(' '),
945+    firstDayOfWeek: parseInt(gettext('FIRST_DAY_OF_WEEK')),
946     isLeapYear: function(year) {
947         return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0));
948     },
949@@ -56,10 +57,10 @@
950         // Draw days-of-week header
951         var tableRow = quickElement('tr', tableBody);
952         for (var i = 0; i < 7; i++) {
953-            quickElement('th', tableRow, CalendarNamespace.daysOfWeek[i]);
954+            quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]);
955         }
956 
957-        var startingPos = new Date(year, month-1, 1).getDay();
958+        var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
959         var days = CalendarNamespace.getDaysInMonth(month, year);
960 
961         // Draw blanks before first of month
962Index: django/contrib/admin/templatetags/admin_list.py
963===================================================================
964--- django/contrib/admin/templatetags/admin_list.py     (.../trunk)     (revision 11464)
965+++ django/contrib/admin/templatetags/admin_list.py     (.../branches/soc2009/i18n-improvements)        (revision 11464)
966@@ -3,11 +3,11 @@
967 from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR
968 from django.core.exceptions import ObjectDoesNotExist
969 from django.db import models
970-from django.utils import dateformat
971+from django.utils import formats
972 from django.utils.html import escape, conditional_escape
973 from django.utils.text import capfirst
974 from django.utils.safestring import mark_safe
975-from django.utils.translation import get_date_formats, get_partial_date_formats, ugettext as _
976+from django.utils.translation import ugettext as _
977 from django.utils.encoding import smart_unicode, smart_str, force_unicode
978 from django.template import Library
979 import datetime
980@@ -184,25 +184,24 @@
981             # Dates and times are special: They're formatted in a certain way.
982             elif isinstance(f, models.DateField) or isinstance(f, models.TimeField):
983                 if field_val:
984-                    (date_format, datetime_format, time_format) = get_date_formats()
985-                    if isinstance(f, models.DateTimeField):
986-                        result_repr = capfirst(dateformat.format(field_val, datetime_format))
987-                    elif isinstance(f, models.TimeField):
988-                        result_repr = capfirst(dateformat.time_format(field_val, time_format))
989-                    else:
990-                        result_repr = capfirst(dateformat.format(field_val, date_format))
991+                    result_repr = formats.localize(field_val)
992                 else:
993                     result_repr = EMPTY_CHANGELIST_VALUE
994+            elif isinstance(f, models.DecimalField):
995+                if field_val:
996+                    result_repr = formats.number_format(field_val, f.decimal_places)
997+                else:
998+                    result_repr = EMPTY_CHANGELIST_VALUE
999                 row_class = ' class="nowrap"'
1000+            elif isinstance(f, models.FloatField):
1001+                if field_val:
1002+                    result_repr = formats.number_format(field_val)
1003+                else:
1004+                    result_repr = EMPTY_CHANGELIST_VALUE
1005+                row_class = ' class="nowrap"'
1006             # Booleans are special: We use images.
1007             elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
1008                 result_repr = _boolean_icon(field_val)
1009-            # DecimalFields are special: Zero-pad the decimals.
1010-            elif isinstance(f, models.DecimalField):
1011-                if field_val is not None:
1012-                    result_repr = ('%%.%sf' % f.decimal_places) % field_val
1013-                else:
1014-                    result_repr = EMPTY_CHANGELIST_VALUE
1015             # Fields with choices are special: Use the representation
1016             # of the choice.
1017             elif f.flatchoices:
1018@@ -263,7 +262,6 @@
1019         year_lookup = cl.params.get(year_field)
1020         month_lookup = cl.params.get(month_field)
1021         day_lookup = cl.params.get(day_field)
1022-        year_month_format, month_day_format = get_partial_date_formats()
1023 
1024         link = lambda d: mark_safe(cl.get_query_string(d, [field_generic]))
1025 
1026@@ -273,9 +271,9 @@
1027                 'show': True,
1028                 'back': {
1029                     'link': link({year_field: year_lookup, month_field: month_lookup}),
1030-                    'title': dateformat.format(day, year_month_format)
1031+                    'title': capfirst(formats.date_format(day, 'YEAR_MONTH_FORMAT'))
1032                 },
1033-                'choices': [{'title': dateformat.format(day, month_day_format)}]
1034+                'choices': [{'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))}]
1035             }
1036         elif year_lookup and month_lookup:
1037             days = cl.query_set.filter(**{year_field: year_lookup, month_field: month_lookup}).dates(field_name, 'day')
1038@@ -287,7 +285,7 @@
1039                 },
1040                 'choices': [{
1041                     'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}),
1042-                    'title': dateformat.format(day, month_day_format)
1043+                    'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))
1044                 } for day in days]
1045             }
1046         elif year_lookup:
1047@@ -300,7 +298,7 @@
1048                 },
1049                 'choices': [{
1050                     'link': link({year_field: year_lookup, month_field: month.month}),
1051-                    'title': dateformat.format(month, year_month_format)
1052+                    'title': capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT'))
1053                 } for month in months]
1054             }
1055         else:
1056Index: django/contrib/admin/templates/admin/object_history.html
1057===================================================================
1058--- django/contrib/admin/templates/admin/object_history.html    (.../trunk)     (revision 11464)
1059+++ django/contrib/admin/templates/admin/object_history.html    (.../branches/soc2009/i18n-improvements)        (revision 11464)
1060@@ -27,7 +27,7 @@
1061         <tbody>
1062         {% for action in action_list %}
1063         <tr>
1064-            <th scope="row">{{ action.action_time|date:_("DATETIME_FORMAT") }}</th>
1065+            <th scope="row">{{ action.action_time|date }}</th>
1066             <td>{{ action.user.username }}{% if action.user.get_full_name %} ({{ action.user.get_full_name }}){% endif %}</td>
1067             <td>{{ action.change_message }}</td>
1068         </tr>
1069Index: django/contrib/databrowse/datastructures.py
1070===================================================================
1071--- django/contrib/databrowse/datastructures.py (.../trunk)     (revision 11464)
1072+++ django/contrib/databrowse/datastructures.py (.../branches/soc2009/i18n-improvements)        (revision 11464)
1073@@ -4,9 +4,8 @@
1074 """
1075 
1076 from django.db import models
1077-from django.utils import dateformat
1078+from django.utils import formats
1079 from django.utils.text import capfirst
1080-from django.utils.translation import get_date_formats
1081 from django.utils.encoding import smart_unicode, smart_str, iri_to_uri
1082 from django.utils.safestring import mark_safe
1083 from django.db.models.query import QuerySet
1084@@ -156,13 +155,12 @@
1085             objs = dict(self.field.choices).get(self.raw_value, EMPTY_VALUE)
1086         elif isinstance(self.field, models.DateField) or isinstance(self.field, models.TimeField):
1087             if self.raw_value:
1088-                date_format, datetime_format, time_format = get_date_formats()
1089                 if isinstance(self.field, models.DateTimeField):
1090-                    objs = capfirst(dateformat.format(self.raw_value, datetime_format))
1091+                    objs = capfirst(formats.date_format(self.raw_value, 'DATETIME_FORMAT'))
1092                 elif isinstance(self.field, models.TimeField):
1093-                    objs = capfirst(dateformat.time_format(self.raw_value, time_format))
1094+                    objs = capfirst(formats.date_format(self.raw_value, 'TIME_FORMAT'))
1095                 else:
1096-                    objs = capfirst(dateformat.format(self.raw_value, date_format))
1097+                    objs = capfirst(formats.date_format(self.raw_value, 'DATE_FORMAT'))
1098             else:
1099                 objs = EMPTY_VALUE
1100         elif isinstance(self.field, models.BooleanField) or isinstance(self.field, models.NullBooleanField):
1101Index: django/template/defaultfilters.py
1102===================================================================
1103--- django/template/defaultfilters.py   (.../trunk)     (revision 11464)
1104+++ django/template/defaultfilters.py   (.../branches/soc2009/i18n-improvements)        (revision 11464)
1105@@ -18,6 +18,7 @@
1106 from django.utils.translation import ugettext, ungettext
1107 from django.utils.encoding import force_unicode, iri_to_uri
1108 from django.utils.safestring import mark_safe, SafeData
1109+from django.utils.formats import date_format, number_format
1110 
1111 register = Library()
1112 
1113@@ -166,14 +167,14 @@
1114         return input_val
1115 
1116     if not m and p < 0:
1117-        return mark_safe(u'%d' % (int(d)))
1118+        return mark_safe(number_format(u'%d' % (int(d)), 0))
1119 
1120     if p == 0:
1121         exp = Decimal(1)
1122     else:
1123         exp = Decimal('1.0') / (Decimal(10) ** abs(p))
1124     try:
1125-        return mark_safe(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)))
1126+        return mark_safe(number_format(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)), abs(p)))
1127     except InvalidOperation:
1128         return input_val
1129 floatformat.is_safe = True
1130@@ -684,9 +685,12 @@
1131     if arg is None:
1132         arg = settings.DATE_FORMAT
1133     try:
1134-        return format(value, arg)
1135+        return date_format(value, arg)
1136     except AttributeError:
1137-        return ''
1138+        try:
1139+            return format(value, arg)
1140+        except AttributeError:
1141+            return ''
1142 date.is_safe = False
1143 
1144 def time(value, arg=None):
1145@@ -697,9 +701,12 @@
1146     if arg is None:
1147         arg = settings.TIME_FORMAT
1148     try:
1149-        return time_format(value, arg)
1150+        return date_format(value, arg)
1151     except AttributeError:
1152-        return ''
1153+        try:
1154+            return time_format(value, arg)
1155+        except AttributeError:
1156+            return ''
1157 time.is_safe = False
1158 
1159 def timesince(value, arg=None):
1160Index: django/template/__init__.py
1161===================================================================
1162--- django/template/__init__.py (.../trunk)     (revision 11464)
1163+++ django/template/__init__.py (.../branches/soc2009/i18n-improvements)        (revision 11464)
1164@@ -60,6 +60,7 @@
1165 from django.utils.encoding import smart_unicode, force_unicode, smart_str
1166 from django.utils.translation import ugettext as _
1167 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
1168+from django.utils.formats import localize
1169 from django.utils.html import escape
1170 
1171 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
1172@@ -808,6 +809,7 @@
1173     means escaping, if required, and conversion to a unicode object. If value
1174     is a string, it is expected to have already been translated.
1175     """
1176+    value = localize(value)
1177     value = force_unicode(value)
1178     if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData):
1179         return escape(value)
1180Index: django/template/debug.py
1181===================================================================
1182--- django/template/debug.py    (.../trunk)     (revision 11464)
1183+++ django/template/debug.py    (.../branches/soc2009/i18n-improvements)        (revision 11464)
1184@@ -2,6 +2,7 @@
1185 from django.utils.encoding import force_unicode
1186 from django.utils.html import escape
1187 from django.utils.safestring import SafeData, EscapeData
1188+from django.utils.formats import localize
1189 
1190 class DebugLexer(Lexer):
1191     def __init__(self, template_string, origin):
1192@@ -84,7 +85,9 @@
1193 class DebugVariableNode(VariableNode):
1194     def render(self, context):
1195         try:
1196-            output = force_unicode(self.filter_expression.resolve(context))
1197+            output = self.filter_expression.resolve(context)
1198+            output = localize(output)
1199+            output = force_unicode(output)
1200         except TemplateSyntaxError, e:
1201             if not hasattr(e, 'source'):
1202                 e.source = self.source
1203Index: tests/regressiontests/i18n/tests.py
1204===================================================================
1205--- tests/regressiontests/i18n/tests.py (.../trunk)     (revision 11464)
1206+++ tests/regressiontests/i18n/tests.py (.../branches/soc2009/i18n-improvements)        (revision 11464)
1207@@ -64,6 +64,283 @@
1208 'as'
1209 >>> print s
1210 Password
1211+
1212+Translations on files with mac or dos end of lines will be converted
1213+to unix eof in .po catalogs, and they have to match when retrieved
1214+
1215+>>> from django.utils.translation.trans_real import translation
1216+>>> ca_translation = translation('ca')
1217+>>> ca_translation._catalog[u'Mac\nEOF\n'] = u'Catalan Mac\nEOF\n'
1218+>>> ca_translation._catalog[u'Win\nEOF\n'] = u'Catalan Win\nEOF\n'
1219+>>> activate('ca')
1220+>>> ugettext(u'Mac\rEOF\r')
1221+u'Catalan Mac\nEOF\n'
1222+>>> ugettext(u'Win\r\nEOF\r\n')
1223+u'Catalan Win\nEOF\n'
1224+>>> deactivate()
1225+
1226+Localization of dates and numbers
1227+
1228+>>> import datetime
1229+>>> import decimal
1230+>>> from django.conf import settings
1231+>>> from django.utils.formats import getformat, date_format, number_format, localize
1232+>>> from django.utils.numberformat import format
1233+>>> from django import template
1234+>>> from django import forms
1235+>>> from django.forms.extras import SelectDateWidget
1236+
1237+>>> old_use_i18n = settings.USE_I18N
1238+>>> old_use_format_i18n = settings.USE_FORMAT_I18N
1239+>>> old_use_thousand_separator = settings.USE_THOUSAND_SEPARATOR
1240+
1241+>>> n = decimal.Decimal('66666.666')
1242+>>> f = 99999.999
1243+>>> d = datetime.date(2009, 12, 31)
1244+>>> dt = datetime.datetime(2009, 12, 31, 20, 50)
1245+>>> ctxt = template.Context({'n': n, 'd': d, 'dt': dt, 'f': f})
1246+>>> class I18nForm(forms.Form):
1247+...     decimal_field = forms.DecimalField()
1248+...     float_field = forms.FloatField()
1249+...     date_field = forms.DateField()
1250+...     datetime_field = forms.DateTimeField()
1251+...     time_field = forms.TimeField()
1252+>>> class SelectDateForm(forms.Form):
1253+...     date_field = forms.DateField(widget=SelectDateWidget)
1254+
1255+Locale independent
1256+
1257+>>> settings.USE_FORMAT_I18N = True
1258+>>> settings.USE_THOUSAND_SEPARATOR = False
1259+>>> format(n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=',')
1260+u'66666.66'
1261+>>> format(n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B')
1262+u'66666A6'
1263+>>> settings.USE_THOUSAND_SEPARATOR = True
1264+>>> format(n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=',')
1265+u'66,666.66'
1266+>>> format(n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B')
1267+u'6B6B6B6B6A6'
1268+
1269+Catalan locale with format i18n disabled
1270+translations will be used, but not formats
1271+
1272+>>> settings.USE_FORMAT_I18N = False
1273+>>> activate('ca')
1274+>>> getformat('DATE_FORMAT')
1275+'N j, Y'
1276+>>> getformat('FIRST_DAY_OF_WEEK')
1277+0
1278+>>> getformat('DECIMAL_SEPARATOR')
1279+'.'
1280+>>> date_format(d)
1281+u'des. 31, 2009'
1282+>>> date_format(d, 'YEAR_MONTH_FORMAT')
1283+u'desembre 2009'
1284+>>> date_format(dt, 'SHORT_DATETIME_FORMAT')
1285+u'12/31/2009 8:50 p.m.'
1286+>>> localize('No localizable')
1287+'No localizable'
1288+>>> localize(n)
1289+Decimal('66666.666')
1290+>>> localize(f)
1291+99999.999
1292+>>> localize(d)
1293+datetime.date(2009, 12, 31)
1294+>>> localize(dt)
1295+datetime.datetime(2009, 12, 31, 20, 50)
1296+>>> template.Template('{{ n }}').render(ctxt)
1297+u'66666.666'
1298+>>> template.Template('{{ f }}').render(ctxt)
1299+u'99999.999'
1300+>>> template.Template('{{ d }}').render(ctxt)
1301+u'2009-12-31'
1302+>>> template.Template('{{ dt }}').render(ctxt)
1303+u'2009-12-31 20:50:00'
1304+>>> template.Template('{{ n|floatformat:2 }}').render(ctxt)
1305+u'66666.67'
1306+>>> template.Template('{{ f|floatformat }}').render(ctxt)
1307+u'100000.0'
1308+>>> template.Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(ctxt)
1309+u'12/31/2009'
1310+>>> template.Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(ctxt)
1311+u'12/31/2009 8:50 p.m.'
1312+>>> form = I18nForm({'decimal_field': u'66666,666', 'float_field': u'99999,999', 'date_field': u'31/12/2009', 'datetime_field': u'31/12/2009 20:50', 'time_field': u'20:50'})
1313+>>> form.is_valid()
1314+False
1315+>>> form.errors['float_field']
1316+[u'Introdu\xefu un n\xfamero.']
1317+>>> form.errors['decimal_field']
1318+[u'Introdu\xefu un n\xfamero.']
1319+>>> form.errors['date_field']
1320+[u'Introdu\xefu una data v\xe0lida.']
1321+>>> form.errors['datetime_field']
1322+[u'Introdu\xefu una data/hora v\xe0lides.']
1323+>>> form2 = SelectDateForm({'date_field_month': u'12', 'date_field_day': u'31', 'date_field_year': u'2009'})
1324+>>> form2.is_valid()
1325+True
1326+>>> form2.cleaned_data['date_field']
1327+datetime.date(2009, 12, 31)
1328+>>> SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
1329+u'<select name="mydate_month" id="id_mydate_month">\n<option value="1">gener</option>\n<option value="2">febrer</option>\n<option value="3">mar\xe7</option>\n<option value="4">abril</option>\n<option value="5">maig</option>\n<option value="6">juny</option>\n<option value="7">juliol</option>\n<option value="8">agost</option>\n<option value="9">setembre</option>\n<option value="10">octubre</option>\n<option value="11">novembre</option>\n<option value="12" selected="selected">desembre</option>\n</select>\n<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>'
1330+
1331+Catalan locale
1332+
1333+>>> settings.USE_FORMAT_I18N = True
1334+>>> activate('ca')
1335+>>> getformat('DATE_FORMAT')
1336+'j \\de F \\de Y'
1337+>>> getformat('FIRST_DAY_OF_WEEK')
1338+1
1339+>>> getformat('DECIMAL_SEPARATOR')
1340+','
1341+>>> date_format(d)
1342+u'31 de desembre de 2009'
1343+>>> date_format(d, 'YEAR_MONTH_FORMAT')
1344+u'desembre del 2009'
1345+>>> date_format(dt, 'SHORT_DATETIME_FORMAT')
1346+u'31/12/2009 20:50'
1347+>>> localize('No localizable')
1348+'No localizable'
1349+>>> settings.USE_THOUSAND_SEPARATOR = True
1350+>>> localize(n)
1351+u'66.666,666'
1352+>>> localize(f)
1353+u'99.999,999'
1354+>>> settings.USE_THOUSAND_SEPARATOR = False
1355+>>> localize(n)
1356+u'66666,666'
1357+>>> localize(f)
1358+u'99999,999'
1359+>>> localize(d)
1360+u'31 de desembre de 2009'
1361+>>> localize(dt)
1362+u'31 de desembre de 2009 a les 20:50'
1363+>>> settings.USE_THOUSAND_SEPARATOR = True
1364+>>> template.Template('{{ n }}').render(ctxt)
1365+u'66.666,666'
1366+>>> template.Template('{{ f }}').render(ctxt)
1367+u'99.999,999'
1368+>>> settings.USE_THOUSAND_SEPARATOR = False
1369+>>> template.Template('{{ n }}').render(ctxt)
1370+u'66666,666'
1371+>>> template.Template('{{ f }}').render(ctxt)
1372+u'99999,999'
1373+>>> template.Template('{{ d }}').render(ctxt)
1374+u'31 de desembre de 2009'
1375+>>> template.Template('{{ dt }}').render(ctxt)
1376+u'31 de desembre de 2009 a les 20:50'
1377+>>> template.Template('{{ n|floatformat:2 }}').render(ctxt)
1378+u'66666,67'
1379+>>> template.Template('{{ f|floatformat }}').render(ctxt)
1380+u'100000,0'
1381+>>> template.Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(ctxt)
1382+u'31/12/2009'
1383+>>> template.Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(ctxt)
1384+u'31/12/2009 20:50'
1385+>>> form = I18nForm({'decimal_field': u'66666,666', 'float_field': u'99999,999', 'date_field': u'31/12/2009', 'datetime_field': u'31/12/2009 20:50', 'time_field': u'20:50'})
1386+>>> form.is_valid()
1387+True
1388+>>> form.cleaned_data['decimal_field']
1389+Decimal('66666.666')
1390+>>> form.cleaned_data['float_field']
1391+99999.999
1392+>>> form.cleaned_data['date_field']
1393+datetime.date(2009, 12, 31)
1394+>>> form.cleaned_data['datetime_field']
1395+datetime.datetime(2009, 12, 31, 20, 50)
1396+>>> form.cleaned_data['time_field']
1397+datetime.time(20, 50)
1398+>>> form2 = SelectDateForm({'date_field_month': u'12', 'date_field_day': u'31', 'date_field_year': u'2009'})
1399+>>> form2.is_valid()
1400+True
1401+>>> form2.cleaned_data['date_field']
1402+datetime.date(2009, 12, 31)
1403+>>> SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
1404+u'<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_month" id="id_mydate_month">\n<option value="1">gener</option>\n<option value="2">febrer</option>\n<option value="3">mar\xe7</option>\n<option value="4">abril</option>\n<option value="5">maig</option>\n<option value="6">juny</option>\n<option value="7">juliol</option>\n<option value="8">agost</option>\n<option value="9">setembre</option>\n<option value="10">octubre</option>\n<option value="11">novembre</option>\n<option value="12" selected="selected">desembre</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>'
1405+
1406+English locale
1407+
1408+>>> settings.USE_FORMAT_I18N = True
1409+>>> activate('en')
1410+>>> getformat('DATE_FORMAT')
1411+'N j, Y'
1412+>>> getformat('FIRST_DAY_OF_WEEK')
1413+0
1414+>>> getformat('DECIMAL_SEPARATOR')
1415+'.'
1416+>>> date_format(d)
1417+u'Dec. 31, 2009'
1418+>>> date_format(d, 'YEAR_MONTH_FORMAT')
1419+u'December 2009'
1420+>>> date_format(dt, 'SHORT_DATETIME_FORMAT')
1421+u'12/31/2009 8:50 p.m.'
1422+>>> localize('No localizable')
1423+'No localizable'
1424+>>> settings.USE_THOUSAND_SEPARATOR = True
1425+>>> localize(n)
1426+u'66,666.666'
1427+>>> localize(f)
1428+u'99,999.999'
1429+>>> settings.USE_THOUSAND_SEPARATOR = False
1430+>>> localize(n)
1431+u'66666.666'
1432+>>> localize(f)
1433+u'99999.999'
1434+>>> localize(d)
1435+u'Dec. 31, 2009'
1436+>>> localize(dt)
1437+u'Dec. 31, 2009, 8:50 p.m.'
1438+>>> settings.USE_THOUSAND_SEPARATOR = True
1439+>>> template.Template('{{ n }}').render(ctxt)
1440+u'66,666.666'
1441+>>> template.Template('{{ f }}').render(ctxt)
1442+u'99,999.999'
1443+>>> settings.USE_THOUSAND_SEPARATOR = False
1444+>>> template.Template('{{ n }}').render(ctxt)
1445+u'66666.666'
1446+>>> template.Template('{{ f }}').render(ctxt)
1447+u'99999.999'
1448+>>> template.Template('{{ d }}').render(ctxt)
1449+u'Dec. 31, 2009'
1450+>>> template.Template('{{ dt }}').render(ctxt)
1451+u'Dec. 31, 2009, 8:50 p.m.'
1452+>>> template.Template('{{ n|floatformat:2 }}').render(ctxt)
1453+u'66666.67'
1454+>>> template.Template('{{ f|floatformat }}').render(ctxt)
1455+u'100000.0'
1456+>>> template.Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(ctxt)
1457+u'12/31/2009'
1458+>>> template.Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(ctxt)
1459+u'12/31/2009 8:50 p.m.'
1460+>>> form = I18nForm({'decimal_field': u'66666.666', 'float_field': u'99999.999', 'date_field': u'12/31/2009', 'datetime_field': u'12/31/2009 20:50', 'time_field': u'20:50'})
1461+>>> form.is_valid()
1462+True
1463+>>> form.cleaned_data['decimal_field']
1464+Decimal('66666.666')
1465+>>> form.cleaned_data['float_field']
1466+99999.999
1467+>>> form.cleaned_data['date_field']
1468+datetime.date(2009, 12, 31)
1469+>>> form.cleaned_data['datetime_field']
1470+datetime.datetime(2009, 12, 31, 20, 50)
1471+>>> form.cleaned_data['time_field']
1472+datetime.time(20, 50)
1473+>>> form2 = SelectDateForm({'date_field_month': u'12', 'date_field_day': u'31', 'date_field_year': u'2009'})
1474+>>> form2.is_valid()
1475+True
1476+>>> form2.cleaned_data['date_field']
1477+datetime.date(2009, 12, 31)
1478+>>> SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
1479+u'<select name="mydate_month" id="id_mydate_month">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12" selected="selected">December</option>\n</select>\n<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>'
1480+
1481+Restore defaults
1482+
1483+>>> settings.USE_I18N = old_use_i18n
1484+>>> settings.USE_FORMAT_I18N = old_use_format_i18n
1485+>>> settings.USE_THOUSAND_SEPARATOR = old_use_thousand_separator
1486+>>> deactivate()
1487+
1488 """
1489 
1490 __test__ = {
1491Index: docs/topics/i18n.txt
1492===================================================================
1493--- docs/topics/i18n.txt        (.../trunk)     (revision 11464)
1494+++ docs/topics/i18n.txt        (.../branches/soc2009/i18n-improvements)        (revision 11464)
1495@@ -4,20 +4,21 @@
1496 Internationalization
1497 ====================
1498 
1499-Django has full support for internationalization of text in code and templates.
1500-Here's how it works.
1501+Django has full support for internationalization, including translation
1502+capabilities of text in code and templates, and format localization for
1503+dates and numbers. Here's how it works.
1504 
1505 Overview
1506 ========
1507 
1508 The goal of internationalization is to allow a single Web application to offer
1509-its content and functionality in multiple languages.
1510+its content and functionality in multiple languages and locales.
1511 
1512-You, the Django developer, can accomplish this goal by adding a minimal amount
1513-of hooks to your Python code and templates. These hooks are called
1514-**translation strings**. They tell Django: "This text should be translated into
1515-the end user's language, if a translation for this text is available in that
1516-language."
1517+For text translation, you, the Django developer, can accomplish this goal by
1518+adding a minimal amount of hooks to your Python code and templates. These hooks
1519+are called **translation strings**. They tell Django: "This text should be
1520+translated into the end user's language, if a translation for this text is
1521+available in that language."
1522 
1523 Django takes care of using these hooks to translate Web apps, on the fly,
1524 according to users' language preferences.
1525@@ -29,6 +30,12 @@
1526     * It uses these hooks to translate Web apps for particular users according
1527       to their language preferences.
1528 
1529+For format localization, it's just necessary to set
1530+:setting:`USE_FORMAT_I18N = True <USE_FORMAT_I18N>` in your settings file. If
1531+:settings:`USE_FORMAT_I18N` is set to ``True``, then Django will display
1532+numbers and dates in the format of the current locale. That includes field
1533+representation on templates, and allowed input formats on the admin.
1534+
1535 If you don't need internationalization in your app
1536 ==================================================
1537 
1538@@ -1074,3 +1081,53 @@
1539 translation utilities with a ``gettext`` package if the command ``xgettext
1540 --version`` entered at a Windows command prompt causes a popup window saying
1541 "xgettext.exe has generated errors and will be closed by Windows".
1542+
1543+Format localization
1544+===================
1545+
1546+Django's formatting system is disabled by default. To enable it, it's necessay
1547+to set :setting:`USE_FORMAT_I18N = True <USE_FORMAT_I18N>` in your settings
1548+file.  Note that :setting:`USE_FORMAT_I18N` requires `USE_I18N` to be ``True``.
1549+
1550+When using Django's formatting system, dates and numbers on templates will be
1551+displayed using the format specified for the current locale. That means, two
1552+users accessing the same content, but in different language, will see date and
1553+number fields formatted in different ways, depending on the format for their
1554+current locale.
1555+
1556+Django will also use localized formats when parsing data in forms. That means
1557+Django uses different formats for different locales when guessing the format
1558+used by the user when inputting data on forms. Note that Django uses different
1559+formats for displaying data, and for parsing it.
1560+
1561+Creating custom format files
1562+----------------------------
1563+
1564+Django provides format definitions for many locales, but sometimes you could
1565+want to create your own ones, because a format files doesn't exist for your
1566+locale, or because you want to overwrite some of the values.
1567+
1568+To use custom formats, first thing to do, is to specify the path where you'll
1569+place format files. To do that, just set :setting:`FORMAT_MODULE_PATH` setting
1570+to the the path (in the format ``'foo.bar.baz``) where format files will
1571+exists.
1572+
1573+Files are not placed directly in this directory, but in a directory named as
1574+the locale. File must be named ``formats.py``.
1575+
1576+For customazing English formats, a structure like this would be needed::
1577+
1578+    mysite/
1579+        formats/
1580+            __init__.py
1581+            en/
1582+                __init__.py
1583+                formats.py
1584+
1585+where :file:`formats.py` contains custom format definitions. For example::
1586+
1587+    THOUSAND_SEPARATOR = ' '
1588+
1589+to use a space as thousand separator, instead of the default for English,
1590+comma.
1591+
1592Index: docs/ref/settings.txt
1593===================================================================
1594--- docs/ref/settings.txt       (.../trunk)     (revision 11464)
1595+++ docs/ref/settings.txt       (.../branches/soc2009/i18n-improvements)        (revision 11464)
1596@@ -243,13 +243,33 @@
1597 
1598 Default: ``'N j, Y'`` (e.g. ``Feb. 4, 2003``)
1599 
1600-The default formatting to use for date fields on Django admin change-list
1601-pages -- and, possibly, by other parts of the system. See
1602-:ttag:`allowed date format strings <now>`.
1603+The default formatting to use for date fields in any part of the system.
1604+Note that if ``USE_FORMAT_I18N`` is set to ``True``, then locale format will
1605+be applied. See :ttag:`allowed date format strings <now>`.
1606 
1607-See also ``DATETIME_FORMAT``, ``TIME_FORMAT``, ``YEAR_MONTH_FORMAT``
1608-and ``MONTH_DAY_FORMAT``.
1609+See also ``DATETIME_FORMAT``, ``TIME_FORMAT`` and ``SHORT_DATE_FORMAT``.
1610 
1611+.. setting:: DATE_INPUT_FORMATS
1612+
1613+DATE_INPUT_FORMATS
1614+------------------
1615+
1616+Default::
1617+
1618+    ('%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', '%b %d %Y',
1619+    '%b %d, %Y', '%d %b %Y', '%d %b, %Y', '%B %d %Y',
1620+    '%B %d, %Y', '%d %B %Y', '%d %B, %Y')
1621+
1622+A tuple of formats that will be accepted when inputting data on a date
1623+field. Formats will be tried in order, using the first valid.
1624+Note that these format strings are specified in Python's datetime_ module
1625+syntax, that is different from the one used by Django for formatting dates
1626+to be displayed.
1627+
1628+See also ``DATETIME_INPUT_FORMATS`` and ``TIME_INPUT_FORMATS``.
1629+
1630+.. _datetime: http://docs.python.org/library/datetime.html#strftime-behavior
1631+
1632 .. setting:: DATETIME_FORMAT
1633 
1634 DATETIME_FORMAT
1635@@ -257,13 +277,33 @@
1636 
1637 Default: ``'N j, Y, P'`` (e.g. ``Feb. 4, 2003, 4 p.m.``)
1638 
1639-The default formatting to use for datetime fields on Django admin change-list
1640-pages -- and, possibly, by other parts of the system. See
1641-:ttag:`allowed date format strings <now>`.
1642+The default formatting to use for datetime fields in any part of the system.
1643+Note that if ``USE_FORMAT_I18N`` is set to ``True``, then locale format will
1644+be applied. See :ttag:`allowed date format strings <now>`.
1645 
1646-See also ``DATE_FORMAT``, ``DATETIME_FORMAT``, ``TIME_FORMAT``,
1647-``YEAR_MONTH_FORMAT`` and ``MONTH_DAY_FORMAT``.
1648+See also ``DATE_FORMAT``, ``TIME_FORMAT`` and ``SHORT_DATETIME_FORMAT``.
1649 
1650+.. setting:: DATETIME_INPUT_FORMATS
1651+
1652+DATETIME_INPUT_FORMATS
1653+----------------------
1654+
1655+Default::
1656+
1657+    ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', '%Y-%m-%d',
1658+    '%m/%d/%Y %H:%M:%S', '%m/%d/%Y %H:%M', '%m/%d/%Y',
1659+    '%m/%d/%y %H:%M:%S', '%m/%d/%y %H:%M', '%m/%d/%y')
1660+
1661+A tuple of formats that will be accepted when inputting data on a datetime
1662+field. Formats will be tried in order, using the first valid.
1663+Note that these format strings are specified in Python's datetime_ module
1664+syntax, that is different from the one used by Django for formatting dates
1665+to be displayed.
1666+
1667+See also ``DATE_INPUT_FORMATS`` and ``TIME_INPUT_FORMATS``.
1668+
1669+.. _datetime: http://docs.python.org/library/datetime.html#strftime-behavior
1670+
1671 .. setting:: DEBUG
1672 
1673 DEBUG
1674@@ -302,7 +342,15 @@
1675 be useful for some test setups, and should never be used on a live
1676 site.
1677 
1678+.. setting:: DECIMAL_SEPARATOR
1679 
1680+DECIMAL_SEPARATOR
1681+-----------------
1682+
1683+Default: ``'.'`` (Dot)
1684+
1685+Default decimal separator used when formatting decimal numbers.
1686+
1687 .. setting:: DEFAULT_CHARSET
1688 
1689 DEFAULT_CHARSET
1690@@ -528,6 +576,21 @@
1691 
1692 .. _documentation for os.chmod: http://docs.python.org/lib/os-file-dir.html
1693 
1694+.. setting:: FIRST_DAY_OF_WEEK
1695+
1696+FIRST_DAY_OF_WEEK
1697+-----------------
1698+
1699+Default: ``0`` (Sunday)
1700+
1701+Number representing the first day of the week. This is specially useful
1702+when displaying a calendar. This value is only used when not using
1703+format internationalization, or when a format cannot be found for the
1704+current locale.
1705+
1706+The value must be an integer from 0 to 6, where 0 means Sunday, 1 means
1707+Monday and so on.
1708+
1709 .. setting:: FIXTURE_DIRS
1710 
1711 FIXTURE_DIRS
1712@@ -549,6 +612,34 @@
1713 the server-provided value of ``SCRIPT_NAME``, which may be a rewritten version
1714 of the preferred value or not supplied at all.
1715 
1716+.. setting:: FORMAT_MODULE_PATH
1717+
1718+FORMAT_MODULE_PATH
1719+------------------
1720+
1721+Default: ``None``
1722+
1723+A full Python path to a Python package that contains format definitions for
1724+project locales. If not ``None``, Django will check for a ``formats.py``
1725+file, under the directory named as the current locale, and will use the
1726+formats defined on this file.
1727+
1728+For example, if ``FORMAT_MODULE_PATH`` is set to ``mysite.formats``, and
1729+current language is ``en`` (English), Django will expect a directory tree
1730+like::
1731+
1732+    mysite/
1733+        formats/
1734+            __init__.py
1735+            en/
1736+                __init__.py
1737+                formats.py
1738+
1739+Available formats are ``DATE_FORMAT``, ``TIME_FORMAT``, ``DATETIME_FORMAT``,
1740+``YEAR_MONTH_FORMAT``, ``MONTH_DAY_FORMAT``, ``SHORT_DATE_FORMAT``,
1741+``SHORT_DATETIME_FORMAT``, ``FIRST_DAY_OF_WEEK``, ``DECIMAL_SEPARATOR``,
1742+``THOUSAND_SEPARATOR`` and ``NUMBER_GROUPING``.
1743+
1744 .. setting:: IGNORABLE_404_ENDS
1745 
1746 IGNORABLE_404_ENDS
1747@@ -774,6 +865,21 @@
1748 See :ttag:`allowed date format strings <now>`. See also ``DATE_FORMAT``,
1749 ``DATETIME_FORMAT``, ``TIME_FORMAT`` and ``YEAR_MONTH_FORMAT``.
1750 
1751+.. setting:: NUMBER_GROUPING
1752+
1753+NUMBER_GROUPING
1754+----------------
1755+
1756+Default: ``0``
1757+
1758+Number of digits grouped together on the integer part of a number. Common use
1759+is to display a thousand separator. If this setting is ``0``, then, no grouping
1760+will be applied to the number. If this setting is greater than ``0`` then the
1761+setting ``THOUSAND_SEPARATOR`` will be used as the separator between those
1762+groups.
1763+
1764+See also ``THOUSAND_SEPARATOR``
1765+
1766 .. setting:: PREPEND_WWW
1767 
1768 PREPEND_WWW
1769@@ -965,6 +1071,32 @@
1770 Whether to save the session data on every request. See
1771 :ref:`topics-http-sessions`.
1772 
1773+.. setting:: SHORT_DATE_FORMAT
1774+
1775+SHORT_DATE_FORMAT
1776+-----------------
1777+
1778+Default: ``m/d/Y`` (e.g. ``12/31/2003``)
1779+
1780+An available formatting that can be used for date fields on templates.
1781+Note that if ``USE_FORMAT_I18N`` is set to ``True``, then locale format will
1782+be applied. See :ttag:`allowed date format strings <now>`.
1783+
1784+See also ``DATE_FORMAT`` and ``SHORT_DATETIME_FORMAT``.
1785+
1786+.. setting:: SHORT_DATETIME_FORMAT
1787+
1788+SHORT_DATETIME_FORMAT
1789+---------------------
1790+
1791+Default: ``m/d/Y P`` (e.g. ``12/31/2003 4 p.m.``)
1792+
1793+An available formatting that can be used for datetime fields on templates.
1794+Note that if ``USE_FORMAT_I18N`` is set to ``True``, then locale format will
1795+be applied. See :ttag:`allowed date format strings <now>`.
1796+
1797+See also ``DATE_FORMAT`` and ``SHORT_DATETIME_FORMAT``.
1798+
1799 .. setting:: SITE_ID
1800 
1801 SITE_ID
1802@@ -1110,6 +1242,18 @@
1803 
1804 .. _Testing Django Applications: ../testing/
1805 
1806+.. setting:: THOUSAND_SEPARATOR
1807+
1808+THOUSAND_SEPARATOR
1809+------------------
1810+
1811+Default ``,`` (Comma)
1812+
1813+Default thousand separator used when formatting numbers. This setting is
1814+used only when ``NUMBER_GROUPPING`` is set.
1815+
1816+See also ``NUMBER_GROUPPING``, ``DECIMAL_SEPARATOR``
1817+
1818 .. setting:: TIME_FORMAT
1819 
1820 TIME_FORMAT
1821@@ -1117,13 +1261,29 @@
1822 
1823 Default: ``'P'`` (e.g. ``4 p.m.``)
1824 
1825-The default formatting to use for time fields on Django admin change-list
1826-pages -- and, possibly, by other parts of the system. See
1827-:ttag:`allowed date format strings <now>`.
1828+The default formatting to use for time fields in any part of the system.
1829+Note that if ``USE_FORMAT_I18N`` is set to ``True``, then locale format will
1830+be applied. See :ttag:`allowed date format strings <now>`.
1831 
1832-See also ``DATE_FORMAT``, ``DATETIME_FORMAT``, ``TIME_FORMAT``,
1833-``YEAR_MONTH_FORMAT`` and ``MONTH_DAY_FORMAT``.
1834+See also ``DATE_FORMAT`` and ``DATETIME_FORMAT``.
1835 
1836+.. setting:: TIME_INPUT_FORMATS
1837+
1838+TIME_INPUT_FORMATS
1839+------------------
1840+
1841+Default: ``('%H:%M:%S', '%H:%M')``
1842+
1843+A tuple of formats that will be accepted when inputting data on a time
1844+field. Formats will be tried in order, using the first valid.
1845+Note that these format strings are specified in Python's datetime_ module
1846+syntax, that is different from the one used by Django for formatting dates
1847+to be displayed.
1848+
1849+See also ``DATE_INPUT_FORMATS`` and ``DATETIME_INPUT_FORMATS``.
1850+
1851+.. _datetime: http://docs.python.org/library/datetime.html#strftime-behavior
1852+
1853 .. setting:: TIME_ZONE
1854 
1855 TIME_ZONE
1856@@ -1177,6 +1337,20 @@
1857 bandwidth but slows down performance. This is only used if ``CommonMiddleware``
1858 is installed (see :ref:`topics-http-middleware`).
1859 
1860+.. setting:: USE_FORMAT_I18N
1861+
1862+USE_FORMAT_I18N
1863+---------------
1864+
1865+Default ``False``
1866+
1867+A boolean that specifies if data will be localized by default or not. If this is
1868+set to ``True``, Django will display numbers and dates using the format of the
1869+current locale. It is required to set ``USE_I18N`` to ``True`` to allow data
1870+format localization.
1871+
1872+See also ``USE_I18N``
1873+
1874 .. setting:: USE_I18N
1875 
1876 USE_I18N
1877@@ -1189,6 +1363,22 @@
1878 set to ``False``, Django will make some optimizations so as not to load the
1879 internationalization machinery.
1880 
1881+See also ``USE_FORMAT_I18N``
1882+
1883+.. setting:: USE_THOUSAND_SEPARATOR
1884+
1885+USE_THOUSAND_SEPARATOR
1886+----------------------
1887+
1888+Default ``False``
1889+
1890+A boolean that specifies wheter to display numbers using a thousand separator.
1891+If this is set to ``True``, Django will use values from ``THOUSAND_SEPARATOR``
1892+and ``NUMBER_GROUPING`` from current locale, to format the number.
1893+``USE_FORMAT_I18N`` must be set to ``True``, in order to format numbers.
1894+
1895+See also ``THOUSAND_SEPARATOR`` and ``NUMBER_GROUPING``.
1896+
1897 .. setting:: YEAR_MONTH_FORMAT
1898 
1899 YEAR_MONTH_FORMAT
1900Index: docs/ref/templates/builtins.txt
1901===================================================================
1902--- docs/ref/templates/builtins.txt     (.../trunk)     (revision 11464)
1903+++ docs/ref/templates/builtins.txt     (.../branches/soc2009/i18n-improvements)        (revision 11464)
1904@@ -897,8 +897,12 @@
1905 date
1906 ~~~~
1907 
1908-Formats a date according to the given format (same as the `now`_ tag).
1909+Formats a date according to the given format.
1910 
1911+Given format can be one of the predefined ones ``DATE_FORMAT``, ``DATETIME_FORMAT``,
1912+``SHORT_DATE_FORMAT`` or ``SHORT_DATETIME_FORMAT``, or a custom format, same as the
1913+`now`_ tag. Note that prefedined formats vary depending on the current locale.
1914+
1915 For example::
1916 
1917     {{ value|date:"D d M Y" }}
1918@@ -912,7 +916,7 @@
1919     {{ value|date }}
1920 
1921 ...the formatting string defined in the :setting:`DATE_FORMAT` setting will be
1922-used.
1923+used, without applying any localization.
1924 
1925 .. templatefilter:: default
1926 
1927@@ -1460,7 +1464,10 @@
1928 time
1929 ~~~~
1930 
1931-Formats a time according to the given format (same as the `now`_ tag).
1932+Formats a time according to the given format.
1933+
1934+Given format can be the predefined one ``TIME_FORMAT``, or a custom format, same as the `now`_ tag. Note that the predefined format is locale depending.
1935+
1936 The time filter will only accept parameters in the format string that relate
1937 to the time of day, not the date (for obvious reasons). If you need to
1938 format a date, use the `date`_ filter.
1939@@ -1477,7 +1484,7 @@
1940     {{ value|time }}
1941 
1942 ...the formatting string defined in the :setting:`TIME_FORMAT` setting will be
1943-used.
1944+used, without aplying any localization.
1945 
1946 .. templatefilter:: timesince
1947