diff options
| author | Jannis Leidel <jannis@leidel.info> | 2009-12-22 17:58:49 +0000 |
|---|---|---|
| committer | Jannis Leidel <jannis@leidel.info> | 2009-12-22 17:58:49 +0000 |
| commit | 9233d0426537615e06b78d28010d17d5a66adf44 (patch) | |
| tree | 5c731977b3ef9bd1e660b63c4edf6caa7b6491ad /django/utils | |
| parent | 6632739e94c6c38b4c5a86cf5c80c48ae50ac49f (diff) | |
Fixed #7980 - Improved i18n framework to support locale aware formatting (dates and numbers) and form processing.
Thanks to Marc Garcia for working on this during his Google Summer of Code 2009!
Additionally fixes #1061, #2203, #3940, #5526, #6449, #6231, #6693, #6783, #9366 and #10891.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@11964 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/utils')
| -rw-r--r-- | django/utils/formats.py | 97 | ||||
| -rw-r--r-- | django/utils/numberformat.py | 42 | ||||
| -rw-r--r-- | django/utils/translation/trans_null.py | 23 | ||||
| -rw-r--r-- | django/utils/translation/trans_real.py | 88 |
4 files changed, 213 insertions, 37 deletions
diff --git a/django/utils/formats.py b/django/utils/formats.py new file mode 100644 index 0000000000..e18e120b36 --- /dev/null +++ b/django/utils/formats.py @@ -0,0 +1,97 @@ +import decimal +import datetime + +from django.conf import settings +from django.utils.translation import get_language, to_locale, check_for_language +from django.utils.importlib import import_module +from django.utils import dateformat +from django.utils import numberformat + +def get_format_modules(): + """ + Returns an iterator over the format modules found in the project and Django + """ + modules = [] + if not check_for_language(get_language()): + return modules + locale = to_locale(get_language()) + if settings.FORMAT_MODULE_PATH: + format_locations = [settings.FORMAT_MODULE_PATH + '.%s'] + else: + format_locations = [] + format_locations.append('django.conf.locale.%s') + for location in format_locations: + for l in (locale, locale.split('_')[0]): + try: + mod = import_module('.formats', location % l) + except ImportError: + pass + else: + # Don't return duplicates + if mod not in modules: + modules.append(mod) + return modules + +def get_format(format_type): + """ + For a specific format type, returns the format for the current + language (locale), defaults to the format in the settings. + format_type is the name of the format, e.g. 'DATE_FORMAT' + """ + if settings.USE_L10N: + for module in get_format_modules(): + try: + return getattr(module, format_type) + except AttributeError: + pass + return getattr(settings, format_type) + +def date_format(value, format=None): + """ + Formats a datetime.date or datetime.datetime object using a + localizable format + """ + return dateformat.format(value, get_format(format or 'DATE_FORMAT')) + +def number_format(value, decimal_pos=None): + """ + Formats a numeric value using localization settings + """ + return numberformat.format( + value, + get_format('DECIMAL_SEPARATOR'), + decimal_pos, + get_format('NUMBER_GROUPING'), + get_format('THOUSAND_SEPARATOR'), + ) + +def localize(value, is_input=False): + """ + 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_L10N: + 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): + if not is_input: + return date_format(value, 'DATETIME_FORMAT') + else: + return value.strftime(get_format('DATETIME_INPUT_FORMATS')[0]) + elif isinstance(value, datetime.date): + if not is_input: + return date_format(value) + else: + return value.strftime(get_format('DATE_INPUT_FORMATS')[0]) + elif isinstance(value, datetime.time): + if not is_input: + return date_format(value, 'TIME_FORMAT') + else: + return value.strftime(get_format('TIME_INPUT_FORMATS')[0]) + return value + diff --git a/django/utils/numberformat.py b/django/utils/numberformat.py new file mode 100644 index 0000000000..78ecb2fbdb --- /dev/null +++ b/django/utils/numberformat.py @@ -0,0 +1,42 @@ +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 float(number) < 0: + sign = '-' + else: + sign = '' + # decimal part + str_number = unicode(number) + if str_number[0] == '-': + str_number = str_number[1:] + if '.' in str_number: + int_part, dec_part = str_number.split('.') + if decimal_pos: + dec_part = dec_part[:decimal_pos] + else: + int_part, dec_part = str_number, '' + if decimal_pos: + dec_part = dec_part + ('0' * (decimal_pos - len(dec_part))) + if dec_part: dec_part = decimal_sep + dec_part + # grouping + if settings.USE_THOUSAND_SEPARATOR and grouping: + int_part_gd = '' + for cnt, digit in enumerate(int_part[::-1]): + 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 + diff --git a/django/utils/translation/trans_null.py b/django/utils/translation/trans_null.py index 98c6de6197..50f41a2c23 100644 --- a/django/utils/translation/trans_null.py +++ b/django/utils/translation/trans_null.py @@ -2,6 +2,7 @@ # that don't actually do anything. This is purely for performance, so that # settings.USE_I18N = False can use this module rather than trans_real.py. +import warnings from django.conf import settings from django.utils.encoding import force_unicode from django.utils.safestring import mark_safe, SafeData @@ -18,10 +19,10 @@ activate = lambda x: None deactivate = deactivate_all = lambda: None get_language = lambda: settings.LANGUAGE_CODE get_language_bidi = lambda: settings.LANGUAGE_CODE in settings.LANGUAGES_BIDI -get_date_formats = lambda: (settings.DATE_FORMAT, settings.DATETIME_FORMAT, settings.TIME_FORMAT) -get_partial_date_formats = lambda: (settings.YEAR_MONTH_FORMAT, settings.MONTH_DAY_FORMAT) check_for_language = lambda x: True +# date formats shouldn't be used using gettext anymore. This +# is kept for backward compatibility TECHNICAL_ID_MAP = { "DATE_WITH_TIME_FULL": settings.DATETIME_FORMAT, "DATE_FORMAT": settings.DATE_FORMAT, @@ -51,3 +52,21 @@ def to_locale(language): def get_language_from_request(request): return settings.LANGUAGE_CODE + +# get_date_formats and get_partial_date_formats aren't used anymore by Django +# but are kept for backward compatibility. +def get_date_formats(): + warnings.warn( + '`django.utils.translation.get_date_formats` is deprecated. ' + 'Please update your code to use the new i18n aware formatting.', + PendingDeprecationWarning + ) + return settings.DATE_FORMAT, settings.DATETIME_FORMAT, settings.TIME_FORMAT + +def get_partial_date_formats(): + warnings.warn( + '`django.utils.translation.get_partial_date_formats` is deprecated. ' + 'Please update your code to use the new i18n aware formatting.', + PendingDeprecationWarning + ) + return settings.YEAR_MONTH_FORMAT, settings.MONTH_DAY_FORMAT diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 48ed7cc885..8b7db0f123 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -4,6 +4,7 @@ import locale import os import re import sys +import warnings import gettext as gettext_module from cStringIO import StringIO @@ -266,15 +267,16 @@ def do_translate(message, translation_function): translation object to use. If no current translation is activated, the message will be run through the default translation object. """ + eol_message = message.replace('\r\n', '\n').replace('\r', '\n') global _default, _active t = _active.get(currentThread(), None) if t is not None: - result = getattr(t, translation_function)(message) + result = getattr(t, translation_function)(eol_message) else: if _default is None: from django.conf import settings _default = translation(settings.LANGUAGE_CODE) - result = getattr(_default, translation_function)(message) + result = getattr(_default, translation_function)(eol_message) if isinstance(message, SafeData): return mark_safe(result) return result @@ -389,39 +391,6 @@ def get_language_from_request(request): return settings.LANGUAGE_CODE -def get_date_formats(): - """ - Checks whether translation files provide a translation for some technical - message ID to store date and time formats. If it doesn't contain one, the - formats provided in the settings will be used. - """ - from django.conf import settings - date_format = ugettext('DATE_FORMAT') - datetime_format = ugettext('DATETIME_FORMAT') - time_format = ugettext('TIME_FORMAT') - if date_format == 'DATE_FORMAT': - date_format = settings.DATE_FORMAT - if datetime_format == 'DATETIME_FORMAT': - datetime_format = settings.DATETIME_FORMAT - if time_format == 'TIME_FORMAT': - time_format = settings.TIME_FORMAT - return date_format, datetime_format, time_format - -def get_partial_date_formats(): - """ - Checks whether translation files provide a translation for some technical - message ID to store partial date formats. If it doesn't contain one, the - formats provided in the settings will be used. - """ - from django.conf import settings - year_month_format = ugettext('YEAR_MONTH_FORMAT') - month_day_format = ugettext('MONTH_DAY_FORMAT') - if year_month_format == 'YEAR_MONTH_FORMAT': - year_month_format = settings.YEAR_MONTH_FORMAT - if month_day_format == 'MONTH_DAY_FORMAT': - month_day_format = settings.MONTH_DAY_FORMAT - return year_month_format, month_day_format - dot_re = re.compile(r'\S') def blankout(src, char): """ @@ -537,3 +506,52 @@ def parse_accept_lang_header(lang_string): result.append((lang, priority)) result.sort(lambda x, y: -cmp(x[1], y[1])) return result + +# get_date_formats and get_partial_date_formats aren't used anymore by Django +# and are kept for backward compatibility. +# Note, it's also important to keep format names marked for translation. +# For compatibility we still want to have formats on translation catalogs. +# That makes template code like {{ my_date|date:_('DATE_FORMAT') }} still work +def get_date_formats(): + """ + Checks whether translation files provide a translation for some technical + message ID to store date and time formats. If it doesn't contain one, the + formats provided in the settings will be used. + """ + warnings.warn( + '`django.utils.translation.get_date_formats` is deprecated. ' + 'Please update your code to use the new i18n aware formatting.', + PendingDeprecationWarning + ) + from django.conf import settings + date_format = ugettext('DATE_FORMAT') + datetime_format = ugettext('DATETIME_FORMAT') + time_format = ugettext('TIME_FORMAT') + if date_format == 'DATE_FORMAT': + date_format = settings.DATE_FORMAT + if datetime_format == 'DATETIME_FORMAT': + datetime_format = settings.DATETIME_FORMAT + if time_format == 'TIME_FORMAT': + time_format = settings.TIME_FORMAT + return date_format, datetime_format, time_format + +def get_partial_date_formats(): + """ + Checks whether translation files provide a translation for some technical + message ID to store partial date formats. If it doesn't contain one, the + formats provided in the settings will be used. + """ + warnings.warn( + '`django.utils.translation.get_partial_date_formats` is deprecated. ' + 'Please update your code to use the new i18n aware formatting.', + PendingDeprecationWarning + ) + from django.conf import settings + year_month_format = ugettext('YEAR_MONTH_FORMAT') + month_day_format = ugettext('MONTH_DAY_FORMAT') + if year_month_format == 'YEAR_MONTH_FORMAT': + year_month_format = settings.YEAR_MONTH_FORMAT + if month_day_format == 'MONTH_DAY_FORMAT': + month_day_format = settings.MONTH_DAY_FORMAT + return year_month_format, month_day_format + |
