diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py
index 8c73d76..f02a3c8 100644
a
|
b
|
def make_messages(locale=None, domain='django', verbosity='1', all=False,
|
220 | 220 | os.unlink(os.path.join(dirpath, thefile)) |
221 | 221 | elif domain == 'django' and (file_ext == '.py' or file_ext in extensions): |
222 | 222 | thefile = file |
| 223 | orig_file = os.path.join(dirpath, file) |
223 | 224 | if file_ext in extensions: |
224 | | src = open(os.path.join(dirpath, file), "rU").read() |
| 225 | src = open(orig_file, "rU").read() |
225 | 226 | thefile = '%s.py' % file |
| 227 | f = open(os.path.join(dirpath, thefile), "w") |
226 | 228 | try: |
227 | | f = open(os.path.join(dirpath, thefile), "w") |
228 | | try: |
229 | | f.write(templatize(src)) |
230 | | finally: |
231 | | f.close() |
232 | | except SyntaxError, msg: |
233 | | msg = "%s (file: %s)" % (msg, os.path.join(dirpath, file)) |
234 | | raise SyntaxError(msg) |
| 229 | f.write(templatize(src, orig_file[2:])) |
| 230 | finally: |
| 231 | f.close() |
235 | 232 | if verbosity > 1: |
236 | 233 | sys.stdout.write('processing file %s in %s\n' % (file, dirpath)) |
237 | 234 | cmd = ( |
… |
… |
def make_messages(locale=None, domain='django', verbosity='1', all=False,
|
250 | 247 | |
251 | 248 | if thefile != file: |
252 | 249 | old = '#: '+os.path.join(dirpath, thefile)[2:] |
253 | | new = '#: '+os.path.join(dirpath, file)[2:] |
| 250 | new = '#: '+orig_file[2:] |
254 | 251 | msgs = msgs.replace(old, new) |
255 | 252 | if os.path.exists(potfile): |
256 | 253 | # Strip the header |
diff --git a/django/template/base.py b/django/template/base.py
index c6d646e..bd500d7 100644
a
|
b
|
def compile_string(template_string, origin):
|
132 | 132 | else: |
133 | 133 | lexer_class, parser_class = Lexer, Parser |
134 | 134 | lexer = lexer_class(template_string, origin) |
135 | | parser = parser_class(lexer.tokenize()) |
| 135 | tokens = lexer.tokenize() |
| 136 | tokens = zip(*tokens) |
| 137 | if tokens: |
| 138 | tokens = tokens[0] |
| 139 | parser = parser_class(list(tokens)) |
136 | 140 | return parser.parse() |
137 | 141 | |
138 | 142 | class Token(object): |
… |
… |
class Lexer(object):
|
164 | 168 | def __init__(self, template_string, origin): |
165 | 169 | self.template_string = template_string |
166 | 170 | self.origin = origin |
| 171 | self.lineno = 1 |
167 | 172 | |
168 | 173 | def tokenize(self): |
169 | 174 | "Return a list of tokens from a given template_string." |
… |
… |
class Lexer(object):
|
171 | 176 | result = [] |
172 | 177 | for bit in tag_re.split(self.template_string): |
173 | 178 | if bit: |
174 | | result.append(self.create_token(bit, in_tag)) |
| 179 | result.append((self.create_token(bit, in_tag), self.lineno)) |
175 | 180 | in_tag = not in_tag |
176 | 181 | return result |
177 | 182 | |
… |
… |
class Lexer(object):
|
193 | 198 | token = Token(TOKEN_COMMENT, content) |
194 | 199 | else: |
195 | 200 | token = Token(TOKEN_TEXT, token_string) |
| 201 | self.lineno += token_string.count('\n') |
196 | 202 | return token |
197 | 203 | |
198 | 204 | class Parser(object): |
diff --git a/django/template/debug.py b/django/template/debug.py
index a9e3c4f..21ff65e 100644
a
|
b
|
class DebugLexer(Lexer):
|
15 | 15 | for match in tag_re.finditer(self.template_string): |
16 | 16 | start, end = match.span() |
17 | 17 | if start > upto: |
18 | | result.append(self.create_token(self.template_string[upto:start], (upto, start), False)) |
| 18 | result.append((self.create_token(self.template_string[upto:start], (upto, start), False), None)) |
19 | 19 | upto = start |
20 | | result.append(self.create_token(self.template_string[start:end], (start, end), True)) |
| 20 | result.append((self.create_token(self.template_string[start:end], (start, end), True), None)) |
21 | 21 | upto = end |
22 | 22 | last_bit = self.template_string[upto:] |
23 | 23 | if last_bit: |
24 | | result.append(self.create_token(last_bit, (upto, upto + len(last_bit)), False)) |
| 24 | result.append((self.create_token(last_bit, (upto, upto + len(last_bit)), False), None)) |
25 | 25 | return result |
26 | 26 | |
27 | 27 | def create_token(self, token_string, source, in_tag): |
diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py
index 0e1b4f8..e3edb65 100644
a
|
b
|
def to_locale(language):
|
104 | 104 | def get_language_from_request(request): |
105 | 105 | return _trans.get_language_from_request(request) |
106 | 106 | |
107 | | def templatize(src): |
108 | | return _trans.templatize(src) |
| 107 | def templatize(src, origin=None): |
| 108 | return _trans.templatize(src, origin) |
109 | 109 | |
110 | 110 | def deactivate_all(): |
111 | 111 | return _trans.deactivate_all() |
diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
index 832acc7..6dbe8e3 100644
a
|
b
|
endblock_re = re.compile(r"""^\s*endblocktrans$""")
|
421 | 421 | plural_re = re.compile(r"""^\s*plural$""") |
422 | 422 | constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""") |
423 | 423 | |
424 | | def templatize(src): |
| 424 | def templatize(src, origin=None): |
425 | 425 | """ |
426 | 426 | Turns a Django template into something that is understood by xgettext. It |
427 | 427 | does so by translating the Django translation tags into standard gettext |
… |
… |
def templatize(src):
|
435 | 435 | plural = [] |
436 | 436 | incomment = False |
437 | 437 | comment = [] |
438 | | for t in Lexer(src, None).tokenize(): |
| 438 | for t, lineno in Lexer(src, origin).tokenize(): |
439 | 439 | if incomment: |
440 | 440 | if t.token_type == TOKEN_BLOCK and t.contents == 'endcomment': |
441 | 441 | out.write(' # %s' % ''.join(comment)) |
… |
… |
def templatize(src):
|
465 | 465 | elif pluralmatch: |
466 | 466 | inplural = True |
467 | 467 | else: |
468 | | raise SyntaxError("Translation blocks must not include other block tags: %s" % t.contents) |
| 468 | filemsg = '' |
| 469 | if origin: |
| 470 | filemsg = 'file %s, ' % origin |
| 471 | raise SyntaxError("Translation blocks must not include other block tags: %s (%sline %d)" % (t.contents, filemsg, lineno)) |
469 | 472 | elif t.token_type == TOKEN_VAR: |
470 | 473 | if inplural: |
471 | 474 | plural.append('%%(%s)s' % t.contents) |
diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py
index 3e8d2d6..9061863 100644
a
|
b
|
class BasicExtractorTests(ExtractorTests):
|
59 | 59 | self.assertMsgId('I think that 100%% is more that 50%% of anything.', po_contents) |
60 | 60 | self.assertMsgId('I think that 100%% is more that 50%% of %\(obj\)s.', po_contents) |
61 | 61 | |
| 62 | def test_extraction_error(self): |
| 63 | os.chdir(self.test_dir) |
| 64 | shutil.copyfile('./templates/template_with_error.txt', './templates/template_with_error.html') |
| 65 | self.assertRaises(SyntaxError, management.call_command, 'makemessages', locale=LOCALE, verbosity=0) |
| 66 | try: |
| 67 | management.call_command('makemessages', locale=LOCALE, verbosity=0) |
| 68 | except SyntaxError, e: |
| 69 | self.assertEqual(str(e), 'Translation blocks must not include other block tags: blocktrans (file templates/template_with_error.html, line 3)') |
| 70 | finally: |
| 71 | os.remove('./templates/template_with_error.html') |
| 72 | os.remove('./templates/template_with_error.html.py') # Waiting for #8536 to be fixed |
| 73 | |
62 | 74 | |
63 | 75 | class JavascriptExtractorTests(ExtractorTests): |
64 | 76 | |
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 0000000..c2b93bd
-
|
+
|
|
| 1 | {% load i18n %} |
| 2 | <p>This template contains an error (no endblocktrans)</p> |
| 3 | <p>{% blocktrans %}This should fail{% blocktrans %}</p> |