1
1
import os
2
+ import re
2
3
import shutil
3
4
import tempfile
4
5
import urllib
7
8
from django.core.management import call_command
8
9
from django.core.management.commands import makemessages
9
10
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"),)
10
15
11
16
# This makes makemessages create both translations for Python and JavaScript
12
17
# code in one go.
18
23
# [1] https://savannah.gnu.org/bugs/?50920
19
24
20
25
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
+
21
104
class Command(makemessages.Command, BaseCommand):
105
+ build_file_class = BuildFile
106
+
22
107
def handle(self, *args, **options):
23
- call_command("transpile")
108
+ # call_command("transpile")
24
109
options["ignore_patterns"] += [
25
110
"venv",
26
111
".direnv",
@@ -36,40 +121,40 @@ def handle(self, *args, **options):
36
121
shutil.rmtree(self.temp_dir_in)
37
122
shutil.rmtree(self.temp_dir_out)
38
123
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