fix(gettext): extract template-literal translations with correct line numbers (#38063)

This commit is contained in:
Raffael Meyer 2026-03-16 19:52:04 +01:00 committed by GitHub
parent b60a980fd4
commit 163094c898
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 17 deletions

View file

@ -185,27 +185,40 @@ def parse_template_string(
:param options: a dictionary of additional options (optional) :param options: a dictionary of additional options (optional)
:param lineno: starting line number (optional) :param lineno: starting line number (optional)
""" """
from babel.messages.jslexer import line_re
prev_character = None prev_character = None
current_lineno = lineno
level = 0 level = 0
inside_str = False inside_expression_str = False
expression_lineno = lineno
expression_contents = "" expression_contents = ""
for character in template_string[1:-1]: for character in template_string[1:-1]:
if not inside_str and character in ('"', "'", "`"): if not level:
inside_str = character
elif inside_str == character and prev_character != r"\\":
inside_str = False
if level:
expression_contents += character
if not inside_str:
if character == "{" and prev_character == "$": if character == "{" and prev_character == "$":
expression_lineno = current_lineno
level += 1 level += 1
elif level and character == "}": else:
level -= 1 expression_contents += character
if level == 0 and expression_contents:
expression_contents = expression_contents[:-1] if inside_expression_str:
yield from extract_javascript(expression_contents, keywords, options, lineno) if inside_expression_str == character and prev_character != r"\\":
lineno += len(line_re.findall(expression_contents)) inside_expression_str = False
expression_contents = "" else:
if character in ('"', "'", "`"):
inside_expression_str = character
elif character == "{":
level += 1
elif character == "}":
level -= 1
if level == 0 and expression_contents:
expression_contents = expression_contents[:-1]
yield from extract_javascript(
expression_contents,
keywords,
options,
expression_lineno,
)
expression_contents = ""
inside_expression_str = False
if character == "\n":
current_lineno += 1
prev_character = character prev_character = character

View file

@ -15,3 +15,17 @@ class TestJavaScript(IntegrationTestCase):
next(extract_javascript(code)), next(extract_javascript(code)),
(1, "__", ("Test", None, "Context")), (1, "__", ("Test", None, "Context")),
) )
def test_extract_javascript_from_template_literal_attribute(self):
code = "let test = `<button title=\"${__('In attribute')}\">${__('In text')}</button>`;"
self.assertEqual(
list(extract_javascript(code)),
[(1, "__", "In attribute"), (1, "__", "In text")],
)
def test_extract_javascript_template_literal_multiline_line_numbers(self):
code = "let test = `\n<button title=\"${__('In attribute')}\">\n ${__('In text')}\n</button>\n`;"
self.assertEqual(
list(extract_javascript(code)),
[(2, "__", "In attribute"), (3, "__", "In text")],
)