diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py index 8fc312c0664..519354985f4 100644 --- a/django/templatetags/i18n.py +++ b/django/templatetags/i18n.py @@ -128,7 +128,12 @@ class BlockTranslateNode(Node): result = re.sub(u'%(?!\()', u'%%', result) data = dict([(v, _render_value_in_context(context.get(v, ''), context)) for v in vars]) context.pop() - return result % data + try: + result = result % data + except KeyError: + with translation.override(None): + result = self.render(context) + return result class LanguageNode(Node): diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py index acf4d8deadb..8ad9c5d59a2 100644 --- a/django/utils/translation/__init__.py +++ b/django/utils/translation/__init__.py @@ -115,7 +115,10 @@ class override(object): self.old_language = get_language() def __enter__(self): - activate(self.language) + if self.language is not None: + activate(self.language) + else: + deactivate_all() def __exit__(self, exc_type, exc_value, traceback): if self.deactivate: diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 0b2e386268a..1755ee77b32 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -532,15 +532,18 @@ For a complete discussion on the usage of the following see the useful when we want delayed translations to appear as the original string for some reason. -.. function:: override(language) +.. function:: override(language, deactivate=False) .. versionadded:: 1.4 A Python context manager that uses :func:`django.utils.translation.activate` to fetch the translation object for a given language, installing it as the translation object for the - current thread and deinstalls it again on exit with - :func:`django.utils.translation.deactivate`. + current thread and reinstall the previous active language on exit. + Optionally it can simply deinstall the temporary translation on exit with + :func:`django.utils.translation.deactivate` if the deactivate argument is + True. If you pass None as the language argument, a NullTranslations() + instance is installed while the context is active. .. function:: get_language() diff --git a/docs/topics/i18n/internationalization.txt b/docs/topics/i18n/internationalization.txt index 176ad7fb43e..dbeb77c8796 100644 --- a/docs/topics/i18n/internationalization.txt +++ b/docs/topics/i18n/internationalization.txt @@ -512,6 +512,13 @@ You can use multiple expressions inside a single ``blocktrans`` tag:: .. note:: The previous more verbose format is still supported: ``{% blocktrans with book|title as book_t and author|title as author_t %}`` +.. versionchanged:: 1.4 + +If resolving one of the block arguments fails, blocktrans will fall back to +the default language by deactivating the currently active language +temporarily with the :func:`~django.utils.translation.deactivate_all` +function. + This tag also provides for pluralization. To use it: * Designate and bind a counter value with the name ``count``. This value will diff --git a/tests/regressiontests/i18n/other/locale/fr/LC_MESSAGES/django.mo b/tests/regressiontests/i18n/other/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 00000000000..f0a21797676 Binary files /dev/null and b/tests/regressiontests/i18n/other/locale/fr/LC_MESSAGES/django.mo differ diff --git a/tests/regressiontests/i18n/other/locale/fr/LC_MESSAGES/django.po b/tests/regressiontests/i18n/other/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000000..8e9b9e10344 --- /dev/null +++ b/tests/regressiontests/i18n/other/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,21 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: django tests\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-02-14 17:33+0100\n" +"PO-Revision-Date: 2011-01-21 21:37-0300\n" +"Last-Translator: Claude\n" +"Language-Team: fr \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: template.html:3 +msgid "My name is %(person)s." +msgstr "Mon nom est %(personne)s." diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 0b955ad3da9..f230e595560 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -9,6 +9,7 @@ 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 @@ -27,6 +28,8 @@ 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): @@ -34,6 +37,9 @@ class TranslationTests(TestCase): 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): @@ -67,7 +73,7 @@ class TranslationTests(TestCase): def test_pgettext(self): # Reset translation catalog to include other/locale/de extended_locale_paths = settings.LOCALE_PATHS + ( - os.path.join(os.path.dirname(os.path.abspath(__file__)), 'other', 'locale'), + os.path.join(here, 'other', 'locale'), ) with self.settings(LOCALE_PATHS=extended_locale_paths): from django.utils.translation import trans_real @@ -129,6 +135,18 @@ class TranslationTests(TestCase): 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): @@ -636,7 +654,7 @@ class LocalePathsResolutionOrderI18NTests(ResolutionOrderI18NTests): def setUp(self): self.old_locale_paths = settings.LOCALE_PATHS - settings.LOCALE_PATHS += (os.path.join(os.path.dirname(os.path.abspath(__file__)), 'other', 'locale'),) + settings.LOCALE_PATHS += (os.path.join(here, 'other', 'locale'),) super(LocalePathsResolutionOrderI18NTests, self).setUp() def tearDown(self):