# -*- encoding: utf-8 -*- from __future__ import with_statement import datetime import decimal import os import pickle from threading import local from django.conf import settings from django.template import Template, Context from django.test import TestCase, RequestFactory from django.test.utils import override_settings from django.utils.formats import (get_format, date_format, time_format, localize, localize_input, iter_format_modules, get_format_modules) from django.utils.importlib import import_module from django.utils.numberformat import format as nformat from django.utils.safestring import mark_safe, SafeString, SafeUnicode from django.utils import translation from django.utils.translation import (ugettext, ugettext_lazy, activate, deactivate, gettext_lazy, pgettext, npgettext, to_locale, get_language_info, get_language, get_language_from_request) from forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm from models import Company, TestModel from commands.tests import * from patterns.tests import * from test_warnings import DeprecationWarningTests here = os.path.dirname(os.path.abspath(__file__)) class TranslationTests(TestCase): def test_override(self): activate('de') with translation.override('pl'): self.assertEqual(get_language(), 'pl') self.assertEqual(get_language(), 'de') with translation.override(None): self.assertEqual(get_language(), settings.LANGUAGE_CODE) self.assertEqual(get_language(), 'de') deactivate() def test_lazy_objects(self): """ Format string interpolation should work with *_lazy objects. """ s = ugettext_lazy('Add %(name)s') d = {'name': 'Ringo'} self.assertEqual(u'Add Ringo', s % d) with translation.override('de', deactivate=True): self.assertEqual(u'Ringo hinzuf\xfcgen', s % d) with translation.override('pl'): self.assertEqual(u'Dodaj Ringo', s % d) # It should be possible to compare *_lazy objects. s1 = ugettext_lazy('Add %(name)s') self.assertEqual(True, s == s1) s2 = gettext_lazy('Add %(name)s') s3 = gettext_lazy('Add %(name)s') self.assertEqual(True, s2 == s3) self.assertEqual(True, s == s2) s4 = ugettext_lazy('Some other string') self.assertEqual(False, s == s4) def test_lazy_pickle(self): s1 = ugettext_lazy("test") self.assertEqual(unicode(s1), "test") s2 = pickle.loads(pickle.dumps(s1)) self.assertEqual(unicode(s2), "test") def test_pgettext(self): # Reset translation catalog to include other/locale/de extended_locale_paths = settings.LOCALE_PATHS + ( os.path.join(here, 'other', 'locale'), ) with self.settings(LOCALE_PATHS=extended_locale_paths): from django.utils.translation import trans_real trans_real._active = local() trans_real._translations = {} with translation.override('de'): self.assertEqual(pgettext("unexisting", "May"), u"May") self.assertEqual(pgettext("month name", "May"), u"Mai") self.assertEqual(pgettext("verb", "May"), u"Kann") self.assertEqual(npgettext("search", "%d result", "%d results", 4) % 4, u"4 Resultate") def test_string_concat(self): """ unicode(string_concat(...)) should not raise a TypeError - #4796 """ import django.utils.translation self.assertEqual(u'django', unicode(django.utils.translation.string_concat("dja", "ngo"))) def test_safe_status(self): """ Translating a string requiring no auto-escaping shouldn't change the "safe" status. """ s = mark_safe('Password') self.assertEqual(SafeString, type(s)) with translation.override('de', deactivate=True): self.assertEqual(SafeUnicode, type(ugettext(s))) self.assertEqual('aPassword', SafeString('a') + s) self.assertEqual('Passworda', s + SafeString('a')) self.assertEqual('Passworda', s + mark_safe('a')) self.assertEqual('aPassword', mark_safe('a') + s) self.assertEqual('as', mark_safe('a') + mark_safe('s')) def test_maclines(self): """ Translations on files with mac or dos end of lines will be converted to unix eof in .po catalogs, and they have to match when retrieved """ from django.utils.translation.trans_real import translation as Trans ca_translation = Trans('ca') ca_translation._catalog[u'Mac\nEOF\n'] = u'Catalan Mac\nEOF\n' ca_translation._catalog[u'Win\nEOF\n'] = u'Catalan Win\nEOF\n' with translation.override('ca', deactivate=True): self.assertEqual(u'Catalan Mac\nEOF\n', ugettext(u'Mac\rEOF\r')) self.assertEqual(u'Catalan Win\nEOF\n', ugettext(u'Win\r\nEOF\r\n')) def test_to_locale(self): """ Tests the to_locale function and the special case of Serbian Latin (refs #12230 and r11299) """ self.assertEqual(to_locale('en-us'), 'en_US') self.assertEqual(to_locale('sr-lat'), 'sr_Lat') def test_to_language(self): """ Test the to_language function """ from django.utils.translation.trans_real import to_language self.assertEqual(to_language('en_US'), 'en-us') self.assertEqual(to_language('sr_Lat'), 'sr-lat') @override_settings(LOCALE_PATHS=(os.path.join(here, 'other', 'locale'),)) def test_bad_placeholder(self): """ Error in translation file should not crash template rendering (%(person)s is translated as %(personne)s in fr.po) """ from django.template import Template, Context with translation.override('fr'): t = Template('{% load i18n %}{% blocktrans %}My name is {{ person }}.{% endblocktrans %}') rendered = t.render(Context({'person': 'James'})) self.assertEqual(rendered, 'My name is James.') class FormattingTests(TestCase): def setUp(self): self.use_i18n = settings.USE_I18N self.use_l10n = settings.USE_L10N self.use_thousand_separator = settings.USE_THOUSAND_SEPARATOR self.thousand_separator = settings.THOUSAND_SEPARATOR self.number_grouping = settings.NUMBER_GROUPING self.n = decimal.Decimal('66666.666') self.f = 99999.999 self.d = datetime.date(2009, 12, 31) self.dt = datetime.datetime(2009, 12, 31, 20, 50) self.t = datetime.time(10, 15, 48) self.l = 10000L self.ctxt = Context({ 'n': self.n, 't': self.t, 'd': self.d, 'dt': self.dt, 'f': self.f, 'l': self.l, }) def tearDown(self): # Restore defaults settings.USE_I18N = self.use_i18n settings.USE_L10N = self.use_l10n settings.USE_THOUSAND_SEPARATOR = self.use_thousand_separator settings.THOUSAND_SEPARATOR = self.thousand_separator settings.NUMBER_GROUPING = self.number_grouping def test_locale_independent(self): """ Localization of numbers """ with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=False): self.assertEqual(u'66666.66', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=',')) self.assertEqual(u'66666A6', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B')) self.assertEqual(u'66666', nformat(self.n, decimal_sep='X', decimal_pos=0, grouping=1, thousand_sep='Y')) with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True): self.assertEqual(u'66,666.66', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=',')) self.assertEqual(u'6B6B6B6B6A6', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B')) self.assertEqual(u'-66666.6', nformat(-66666.666, decimal_sep='.', decimal_pos=1)) self.assertEqual(u'-66666.0', nformat(int('-66666'), decimal_sep='.', decimal_pos=1)) self.assertEqual(u'10000.0', nformat(self.l, decimal_sep='.', decimal_pos=1)) # date filter self.assertEqual(u'31.12.2009 в 20:50', Template('{{ dt|date:"d.m.Y в H:i" }}').render(self.ctxt)) self.assertEqual(u'⌚ 10:15', Template('{{ t|time:"⌚ H:i" }}').render(self.ctxt)) def test_l10n_disabled(self): """ Catalan locale with format i18n disabled translations will be used, but not formats """ settings.USE_L10N = False with translation.override('ca', deactivate=True): self.assertEqual(u'N j, Y', get_format('DATE_FORMAT')) self.assertEqual(0, get_format('FIRST_DAY_OF_WEEK')) self.assertEqual(u'.', get_format('DECIMAL_SEPARATOR')) self.assertEqual(u'10:15 a.m.', time_format(self.t)) self.assertEqual(u'des. 31, 2009', date_format(self.d)) self.assertEqual(u'desembre 2009', date_format(self.d, 'YEAR_MONTH_FORMAT')) self.assertEqual(u'12/31/2009 8:50 p.m.', date_format(self.dt, 'SHORT_DATETIME_FORMAT')) self.assertEqual(u'No localizable', localize('No localizable')) self.assertEqual(u'66666.666', localize(self.n)) self.assertEqual(u'99999.999', localize(self.f)) self.assertEqual(u'10000', localize(self.l)) self.assertEqual(u'des. 31, 2009', localize(self.d)) self.assertEqual(u'des. 31, 2009, 8:50 p.m.', localize(self.dt)) self.assertEqual(u'66666.666', Template('{{ n }}').render(self.ctxt)) self.assertEqual(u'99999.999', Template('{{ f }}').render(self.ctxt)) self.assertEqual(u'des. 31, 2009', Template('{{ d }}').render(self.ctxt)) self.assertEqual(u'des. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt)) self.assertEqual(u'66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual(u'100000.0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual(u'10:15 a.m.', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt)) self.assertEqual(u'12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt)) self.assertEqual(u'12/31/2009 8:50 p.m.', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt)) 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', 'integer_field': u'1.234', }) self.assertEqual(False, form.is_valid()) self.assertEqual([u'Introdu\xefu un n\xfamero.'], form.errors['float_field']) self.assertEqual([u'Introdu\xefu un n\xfamero.'], form.errors['decimal_field']) self.assertEqual([u'Introdu\xefu una data v\xe0lida.'], form.errors['date_field']) self.assertEqual([u'Introdu\xefu una data/hora v\xe0lides.'], form.errors['datetime_field']) self.assertEqual([u'Introdu\xefu un n\xfamero sencer.'], form.errors['integer_field']) form2 = SelectDateForm({ 'date_field_month': u'12', 'date_field_day': u'31', 'date_field_year': u'2009' }) self.assertEqual(True, form2.is_valid()) self.assertEqual(datetime.date(2009, 12, 31), form2.cleaned_data['date_field']) self.assertEqual( u'\n\n', SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31)) ) # We shouldn't change the behavior of the floatformat filter re: # thousand separator and grouping when USE_L10N is False even # if the USE_THOUSAND_SEPARATOR, NUMBER_GROUPING and # THOUSAND_SEPARATOR settings are specified with self.settings(USE_THOUSAND_SEPARATOR=True, NUMBER_GROUPING=1, THOUSAND_SEPARATOR='!'): self.assertEqual(u'66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual(u'100000.0', Template('{{ f|floatformat }}').render(self.ctxt)) def test_l10n_enabled(self): settings.USE_L10N = True # Catalan locale with translation.override('ca', deactivate=True): self.assertEqual('j \de F \de Y', get_format('DATE_FORMAT')) self.assertEqual(1, get_format('FIRST_DAY_OF_WEEK')) self.assertEqual(',', get_format('DECIMAL_SEPARATOR')) self.assertEqual(u'10:15:48', time_format(self.t)) self.assertEqual(u'31 de desembre de 2009', date_format(self.d)) self.assertEqual(u'desembre del 2009', date_format(self.d, 'YEAR_MONTH_FORMAT')) self.assertEqual(u'31/12/2009 20:50', date_format(self.dt, 'SHORT_DATETIME_FORMAT')) self.assertEqual('No localizable', localize('No localizable')) settings.USE_THOUSAND_SEPARATOR = True self.assertEqual(u'66.666,666', localize(self.n)) self.assertEqual(u'99.999,999', localize(self.f)) self.assertEqual(u'10.000', localize(self.l)) self.assertEqual(u'True', localize(True)) settings.USE_THOUSAND_SEPARATOR = False self.assertEqual(u'66666,666', localize(self.n)) self.assertEqual(u'99999,999', localize(self.f)) self.assertEqual(u'10000', localize(self.l)) self.assertEqual(u'31 de desembre de 2009', localize(self.d)) self.assertEqual(u'31 de desembre de 2009 a les 20:50', localize(self.dt)) settings.USE_THOUSAND_SEPARATOR = True self.assertEqual(u'66.666,666', Template('{{ n }}').render(self.ctxt)) self.assertEqual(u'99.999,999', Template('{{ f }}').render(self.ctxt)) self.assertEqual(u'10.000', Template('{{ l }}').render(self.ctxt)) form3 = I18nForm({ 'decimal_field': u'66.666,666', 'float_field': u'99.999,999', 'date_field': u'31/12/2009', 'datetime_field': u'31/12/2009 20:50', 'time_field': u'20:50', 'integer_field': u'1.234', }) self.assertEqual(True, form3.is_valid()) self.assertEqual(decimal.Decimal('66666.666'), form3.cleaned_data['decimal_field']) self.assertEqual(99999.999, form3.cleaned_data['float_field']) self.assertEqual(datetime.date(2009, 12, 31), form3.cleaned_data['date_field']) self.assertEqual(datetime.datetime(2009, 12, 31, 20, 50), form3.cleaned_data['datetime_field']) self.assertEqual(datetime.time(20, 50), form3.cleaned_data['time_field']) self.assertEqual(1234, form3.cleaned_data['integer_field']) settings.USE_THOUSAND_SEPARATOR = False self.assertEqual(u'66666,666', Template('{{ n }}').render(self.ctxt)) self.assertEqual(u'99999,999', Template('{{ f }}').render(self.ctxt)) self.assertEqual(u'31 de desembre de 2009', Template('{{ d }}').render(self.ctxt)) self.assertEqual(u'31 de desembre de 2009 a les 20:50', Template('{{ dt }}').render(self.ctxt)) self.assertEqual(u'66666,67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual(u'100000,0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual(u'10:15:48', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt)) self.assertEqual(u'31/12/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt)) self.assertEqual(u'31/12/2009 20:50', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt)) self.assertEqual(date_format(datetime.datetime.now(), "DATE_FORMAT"), Template('{% now "DATE_FORMAT" %}').render(self.ctxt)) form4 = 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', 'integer_field': u'1234', }) self.assertEqual(True, form4.is_valid()) self.assertEqual(decimal.Decimal('66666.666'), form4.cleaned_data['decimal_field']) self.assertEqual(99999.999, form4.cleaned_data['float_field']) self.assertEqual(datetime.date(2009, 12, 31), form4.cleaned_data['date_field']) self.assertEqual(datetime.datetime(2009, 12, 31, 20, 50), form4.cleaned_data['datetime_field']) self.assertEqual(datetime.time(20, 50), form4.cleaned_data['time_field']) self.assertEqual(1234, form4.cleaned_data['integer_field']) form5 = SelectDateForm({ 'date_field_month': u'12', 'date_field_day': u'31', 'date_field_year': u'2009' }) self.assertEqual(True, form5.is_valid()) self.assertEqual(datetime.date(2009, 12, 31), form5.cleaned_data['date_field']) self.assertEqual( u'\n\n', SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31)) ) # Russian locale (with E as month) with translation.override('ru', deactivate=True): self.assertEqual( u'\n\n', SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31)) ) # English locale with translation.override('en', deactivate=True): self.assertEqual('N j, Y', get_format('DATE_FORMAT')) self.assertEqual(0, get_format('FIRST_DAY_OF_WEEK')) self.assertEqual('.', get_format('DECIMAL_SEPARATOR')) self.assertEqual(u'Dec. 31, 2009', date_format(self.d)) self.assertEqual(u'December 2009', date_format(self.d, 'YEAR_MONTH_FORMAT')) self.assertEqual(u'12/31/2009 8:50 p.m.', date_format(self.dt, 'SHORT_DATETIME_FORMAT')) self.assertEqual(u'No localizable', localize('No localizable')) settings.USE_THOUSAND_SEPARATOR = True self.assertEqual(u'66,666.666', localize(self.n)) self.assertEqual(u'99,999.999', localize(self.f)) self.assertEqual(u'10,000', localize(self.l)) settings.USE_THOUSAND_SEPARATOR = False self.assertEqual(u'66666.666', localize(self.n)) self.assertEqual(u'99999.999', localize(self.f)) self.assertEqual(u'10000', localize(self.l)) self.assertEqual(u'Dec. 31, 2009', localize(self.d)) self.assertEqual(u'Dec. 31, 2009, 8:50 p.m.', localize(self.dt)) settings.USE_THOUSAND_SEPARATOR = True self.assertEqual(u'66,666.666', Template('{{ n }}').render(self.ctxt)) self.assertEqual(u'99,999.999', Template('{{ f }}').render(self.ctxt)) self.assertEqual(u'10,000', Template('{{ l }}').render(self.ctxt)) settings.USE_THOUSAND_SEPARATOR = False self.assertEqual(u'66666.666', Template('{{ n }}').render(self.ctxt)) self.assertEqual(u'99999.999', Template('{{ f }}').render(self.ctxt)) self.assertEqual(u'Dec. 31, 2009', Template('{{ d }}').render(self.ctxt)) self.assertEqual(u'Dec. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt)) self.assertEqual(u'66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual(u'100000.0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual(u'12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt)) self.assertEqual(u'12/31/2009 8:50 p.m.', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt)) form5 = 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', 'integer_field': u'1234', }) self.assertEqual(True, form5.is_valid()) self.assertEqual(decimal.Decimal('66666.666'), form5.cleaned_data['decimal_field']) self.assertEqual(99999.999, form5.cleaned_data['float_field']) self.assertEqual(datetime.date(2009, 12, 31), form5.cleaned_data['date_field']) self.assertEqual(datetime.datetime(2009, 12, 31, 20, 50), form5.cleaned_data['datetime_field']) self.assertEqual(datetime.time(20, 50), form5.cleaned_data['time_field']) self.assertEqual(1234, form5.cleaned_data['integer_field']) form6 = SelectDateForm({ 'date_field_month': u'12', 'date_field_day': u'31', 'date_field_year': u'2009' }) self.assertEqual(True, form6.is_valid()) self.assertEqual(datetime.date(2009, 12, 31), form6.cleaned_data['date_field']) self.assertEqual( u'\n\n', SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31)) ) def test_sub_locales(self): """ Check if sublocales fall back to the main locale """ with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True): with translation.override('de-at', deactivate=True): self.assertEqual(u'66.666,666', Template('{{ n }}').render(self.ctxt)) with translation.override('es-us', deactivate=True): self.assertEqual(u'31 de diciembre de 2009', date_format(self.d)) def test_localized_input(self): """ Tests if form input is correctly localized """ settings.USE_L10N = True with translation.override('de-at', deactivate=True): form6 = CompanyForm({ 'name': u'acme', 'date_added': datetime.datetime(2009, 12, 31, 6, 0, 0), 'cents_payed': decimal.Decimal('59.47'), 'products_delivered': 12000, }) self.assertEqual(True, form6.is_valid()) self.assertEqual( form6.as_ul(), u'