Skip to content

Commit 88d9608

Browse files
committed
modify JSLexer to read JS templates
1 parent 8d5342f commit 88d9608

File tree

1 file changed

+123
-38
lines changed

1 file changed

+123
-38
lines changed
Lines changed: 123 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import re
23
import shutil
34
import tempfile
45
import urllib
@@ -7,6 +8,10 @@
78
from django.core.management import call_command
89
from django.core.management.commands import makemessages
910
from django.core.management.utils import popen_wrapper
11+
from django.utils.translation import templatize
12+
from django.utils.functional import cached_property
13+
from django.utils.jslex import JsLexer, Tok
14+
JsLexer.both_before.append(Tok("template", r"`([^\\]|(\\(.|\n)))*?`", next="div"),)
1015

1116
# This makes makemessages create both translations for Python and JavaScript
1217
# code in one go.
@@ -18,9 +23,89 @@
1823
# [1] https://savannah.gnu.org/bugs/?50920
1924

2025

26+
def prepare_js_for_gettext(js):
27+
"""
28+
Convert the JavaScript source `js` into something resembling C for
29+
xgettext.
30+
What actually happens is that all the regex literals are replaced with
31+
"REGEX".
32+
"""
33+
34+
def escape_quotes(m):
35+
"""Used in a regex to properly escape double quotes."""
36+
s = m[0]
37+
if s == '"':
38+
return r"\""
39+
else:
40+
return s
41+
42+
lexer = JsLexer()
43+
c = []
44+
for name, tok in lexer.lex(js):
45+
if name == "regex":
46+
# C doesn't grok regexes, and they aren't needed for gettext,
47+
# so just output a string instead.
48+
tok = '"REGEX"'
49+
elif name == "string":
50+
# C doesn't have single-quoted strings, so make all strings
51+
# double-quoted.
52+
if tok.startswith("'"):
53+
guts = re.sub(r"\\.|.", escape_quotes, tok[1:-1])
54+
tok = '"' + guts + '"'
55+
elif name == "template":
56+
# Split the template string into chunks using the ${...} pattern
57+
chunks = re.findall(r'\${(.*?)}', tok)
58+
# Concatenate the chunks with "+"
59+
for chunk in chunks:
60+
tok = tok.replace('${' + chunk + '}', '" + ' + chunk + ' + "', 1)
61+
# Replace backtick-quotes with double-quotes.
62+
tok = '"' + tok[1:-1] +'"'
63+
elif name == "id":
64+
# C can't deal with Unicode escapes in identifiers. We don't
65+
# need them for gettext anyway, so replace them with something
66+
# innocuous
67+
tok = tok.replace("\\", "U")
68+
c.append(tok)
69+
return "".join(c)
70+
71+
72+
class BuildFile(makemessages.BuildFile):
73+
74+
@cached_property
75+
def is_templatized(self):
76+
if self.domain == "djangojs":
77+
return True
78+
elif self.domain == "django":
79+
file_ext = os.path.splitext(self.translatable.file)[1]
80+
return file_ext != ".py"
81+
return False
82+
83+
def preprocess(self):
84+
"""
85+
Preprocess (if necessary) a translatable file before passing it to
86+
xgettext GNU gettext utility.
87+
"""
88+
if not self.is_templatized:
89+
return
90+
with open(self.path, encoding="utf-8") as fp:
91+
src_data = fp.read()
92+
93+
if self.domain == "djangojs":
94+
content = prepare_js_for_gettext(src_data)
95+
elif self.domain == "django":
96+
content = templatize(src_data, origin=self.path[2:])
97+
98+
with open(self.work_path, "w", encoding="utf-8") as fp:
99+
fp.write(content)
100+
101+
def cleanup(self):
102+
pass
103+
21104
class Command(makemessages.Command, BaseCommand):
105+
build_file_class = BuildFile
106+
22107
def handle(self, *args, **options):
23-
call_command("transpile")
108+
#call_command("transpile")
24109
options["ignore_patterns"] += [
25110
"venv",
26111
".direnv",
@@ -36,40 +121,40 @@ def handle(self, *args, **options):
36121
shutil.rmtree(self.temp_dir_in)
37122
shutil.rmtree(self.temp_dir_out)
38123

39-
def process_locale_dir(self, locale_dir, files):
40-
if self.domain == "djangojs":
41-
for file in files:
42-
# We need to copy the JS files first, as otherwise babel will
43-
# attempt to read package.json files in subdirs, such as
44-
# base/package.json
45-
in_path = urllib.parse.urljoin(self.temp_dir_in + "/", file.dirpath)
46-
os.makedirs(in_path, exist_ok=True)
47-
in_file = urllib.parse.urljoin(in_path + "/", file.file)
48-
shutil.copy2(file.path, in_file)
49-
out_path = urllib.parse.urljoin(self.temp_dir_out + "/", file.dirpath)
50-
file.dirpath = out_path
51-
os.chdir(".transpile/")
52-
out, err, status = popen_wrapper(
53-
[
54-
"npm",
55-
"run",
56-
"babel-transform-template-literals",
57-
"--",
58-
"--out-dir",
59-
self.temp_dir_out,
60-
self.temp_dir_in,
61-
],
62-
)
63-
os.chdir("../")
64-
65-
super().process_locale_dir(locale_dir, files)
66-
67-
def write_po_file(self, potfile, locale):
68-
if self.domain == "djangojs":
69-
with open(potfile, encoding="utf-8") as fp:
70-
msgs = fp.read()
71-
# Remove temp dir path info
72-
msgs = msgs.replace(self.temp_dir_out + "/", "")
73-
with open(potfile, "w", encoding="utf-8") as fp:
74-
fp.write(msgs)
75-
super().write_po_file(potfile, locale)
124+
# def process_locale_dir(self, locale_dir, files):
125+
# if self.domain == "djangojs":
126+
# for file in files:
127+
# # We need to copy the JS files first, as otherwise babel will
128+
# # attempt to read package.json files in subdirs, such as
129+
# # base/package.json
130+
# in_path = urllib.parse.urljoin(self.temp_dir_in + "/", file.dirpath)
131+
# os.makedirs(in_path, exist_ok=True)
132+
# in_file = urllib.parse.urljoin(in_path + "/", file.file)
133+
# shutil.copy2(file.path, in_file)
134+
# out_path = urllib.parse.urljoin(self.temp_dir_out + "/", file.dirpath)
135+
# file.dirpath = out_path
136+
# os.chdir(".transpile/")
137+
# out, err, status = popen_wrapper(
138+
# [
139+
# "npm",
140+
# "run",
141+
# "babel-transform-template-literals",
142+
# "--",
143+
# "--out-dir",
144+
# self.temp_dir_out,
145+
# self.temp_dir_in,
146+
# ],
147+
# )
148+
# os.chdir("../")
149+
#
150+
# super().process_locale_dir(locale_dir, files)
151+
#
152+
# def write_po_file(self, potfile, locale):
153+
# if self.domain == "djangojs":
154+
# with open(potfile, encoding="utf-8") as fp:
155+
# msgs = fp.read()
156+
# # Remove temp dir path info
157+
# msgs = msgs.replace(self.temp_dir_out + "/", "")
158+
# with open(potfile, "w", encoding="utf-8") as fp:
159+
# fp.write(msgs)
160+
# super().write_po_file(potfile, locale)

0 commit comments

Comments
 (0)