From 23f69af45409a29146f215058974898baa7e2207 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Sat, 4 Dec 2010 17:42:54 +0000 Subject: [PATCH] Fixed #12201 -- Added a lineno attibute to template Token so e.g. we can report line numbers in errors during i18n literals extraction. Thanks madewulf for the report and Claude Paroz for the patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@14813 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/management/commands/makemessages.py | 17 +++++++---------- django/template/base.py | 4 ++++ django/utils/translation/__init__.py | 4 ++-- django/utils/translation/trans_real.py | 9 ++++++--- .../regressiontests/i18n/commands/extraction.py | 12 ++++++++++++ .../regressiontests/i18n/commands/locale/dummy | 0 .../commands/templates/template_with_error.txt | 3 +++ 7 files changed, 34 insertions(+), 15 deletions(-) delete mode 100644 tests/regressiontests/i18n/commands/locale/dummy create mode 100644 tests/regressiontests/i18n/commands/templates/template_with_error.txt diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py index 8c73d760af..f02a3c8649 100644 --- a/django/core/management/commands/makemessages.py +++ b/django/core/management/commands/makemessages.py @@ -220,18 +220,15 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False, os.unlink(os.path.join(dirpath, thefile)) elif domain == 'django' and (file_ext == '.py' or file_ext in extensions): thefile = file + orig_file = os.path.join(dirpath, file) if file_ext in extensions: - src = open(os.path.join(dirpath, file), "rU").read() + src = open(orig_file, "rU").read() thefile = '%s.py' % file + f = open(os.path.join(dirpath, thefile), "w") try: - f = open(os.path.join(dirpath, thefile), "w") - try: - f.write(templatize(src)) - finally: - f.close() - except SyntaxError, msg: - msg = "%s (file: %s)" % (msg, os.path.join(dirpath, file)) - raise SyntaxError(msg) + f.write(templatize(src, orig_file[2:])) + finally: + f.close() if verbosity > 1: sys.stdout.write('processing file %s in %s\n' % (file, dirpath)) cmd = ( @@ -250,7 +247,7 @@ def make_messages(locale=None, domain='django', verbosity='1', all=False, if thefile != file: old = '#: '+os.path.join(dirpath, thefile)[2:] - new = '#: '+os.path.join(dirpath, file)[2:] + new = '#: '+orig_file[2:] msgs = msgs.replace(old, new) if os.path.exists(potfile): # Strip the header diff --git a/django/template/base.py b/django/template/base.py index c6d646eebf..d934e050ee 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -139,6 +139,7 @@ class Token(object): def __init__(self, token_type, contents): # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT. self.token_type, self.contents = token_type, contents + self.lineno = None def __str__(self): return '<%s token: "%s...">' % \ @@ -164,6 +165,7 @@ class Lexer(object): def __init__(self, template_string, origin): self.template_string = template_string self.origin = origin + self.lineno = 1 def tokenize(self): "Return a list of tokens from a given template_string." @@ -193,6 +195,8 @@ class Lexer(object): token = Token(TOKEN_COMMENT, content) else: token = Token(TOKEN_TEXT, token_string) + token.lineno = self.lineno + self.lineno += token_string.count('\n') return token class Parser(object): diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py index 0e1b4f8d67..e3edb652f6 100644 --- a/django/utils/translation/__init__.py +++ b/django/utils/translation/__init__.py @@ -104,8 +104,8 @@ def to_locale(language): def get_language_from_request(request): return _trans.get_language_from_request(request) -def templatize(src): - return _trans.templatize(src) +def templatize(src, origin=None): + return _trans.templatize(src, origin) def deactivate_all(): return _trans.deactivate_all() diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 832acc76e7..a5c612bfe0 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -421,7 +421,7 @@ endblock_re = re.compile(r"""^\s*endblocktrans$""") plural_re = re.compile(r"""^\s*plural$""") constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""") -def templatize(src): +def templatize(src, origin=None): """ Turns a Django template into something that is understood by xgettext. It does so by translating the Django translation tags into standard gettext @@ -435,7 +435,7 @@ def templatize(src): plural = [] incomment = False comment = [] - for t in Lexer(src, None).tokenize(): + for t in Lexer(src, origin).tokenize(): if incomment: if t.token_type == TOKEN_BLOCK and t.contents == 'endcomment': out.write(' # %s' % ''.join(comment)) @@ -465,7 +465,10 @@ def templatize(src): elif pluralmatch: inplural = True else: - raise SyntaxError("Translation blocks must not include other block tags: %s" % t.contents) + filemsg = '' + if origin: + filemsg = 'file %s, ' % origin + raise SyntaxError("Translation blocks must not include other block tags: %s (%sline %d)" % (t.contents, filemsg, t.lineno)) elif t.token_type == TOKEN_VAR: if inplural: plural.append('%%(%s)s' % t.contents) diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py index 3e8d2d6228..9061863e67 100644 --- a/tests/regressiontests/i18n/commands/extraction.py +++ b/tests/regressiontests/i18n/commands/extraction.py @@ -59,6 +59,18 @@ class BasicExtractorTests(ExtractorTests): self.assertMsgId('I think that 100%% is more that 50%% of anything.', po_contents) self.assertMsgId('I think that 100%% is more that 50%% of %\(obj\)s.', po_contents) + def test_extraction_error(self): + os.chdir(self.test_dir) + shutil.copyfile('./templates/template_with_error.txt', './templates/template_with_error.html') + self.assertRaises(SyntaxError, management.call_command, 'makemessages', locale=LOCALE, verbosity=0) + try: + management.call_command('makemessages', locale=LOCALE, verbosity=0) + except SyntaxError, e: + self.assertEqual(str(e), 'Translation blocks must not include other block tags: blocktrans (file templates/template_with_error.html, line 3)') + finally: + os.remove('./templates/template_with_error.html') + os.remove('./templates/template_with_error.html.py') # Waiting for #8536 to be fixed + class JavascriptExtractorTests(ExtractorTests): diff --git a/tests/regressiontests/i18n/commands/locale/dummy b/tests/regressiontests/i18n/commands/locale/dummy deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/regressiontests/i18n/commands/templates/template_with_error.txt b/tests/regressiontests/i18n/commands/templates/template_with_error.txt new file mode 100644 index 0000000000..c2b93bdd06 --- /dev/null +++ b/tests/regressiontests/i18n/commands/templates/template_with_error.txt @@ -0,0 +1,3 @@ +{% load i18n %} +

This template contains an error (no endblocktrans)

+

{% blocktrans %}This should fail{% blocktrans %}