get_gettext_tool_output()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 17
rs 9.85
c 0
b 0
f 0
cc 4
nop 1
1
import itertools
2
import sys
3
4
if sys.version_info >= (3, 9):
5
  List = list
6
else:
7
  from typing import List
8
9
from pathlib import Path
10
11
from .text import normalize_eols
12
from .text import stringify_path
13
14
from .utils import find_executable
15
from .utils import popen_wrapper
16
from .utils import print_err
17
18
19
GETTEXT_TOOLS_STATUS_OK = 0
20
GETTEXT_TOOLS_EXECUTABLES = [
21
  "xgettext",
22
  "msguniq",
23
  "msgmerge",
24
  "msgattrib",
25
  "msgfmt",
26
]
27
28
29
def validate_gettext_tools_exist() -> None:
30
  for executable_name in GETTEXT_TOOLS_EXECUTABLES:
31
    if find_executable(executable_name) is None:
32
      raise OSError(
33
        f"cannot find executable '{executable_name}': make sure you have GNU "
34
        f"gettext tools 0.15 or newer installed"
35
      )
36
37
38
def get_gettext_tool_output(args: List[str]) -> str:
39
  content, errors, status = popen_wrapper(args)
40
41
  if errors:
42
    if status != GETTEXT_TOOLS_STATUS_OK:
43
      tool_name = args[0]
44
      raise RuntimeError(
45
        f"failed to run gettext tool '{tool_name}' (status={status}): "
46
        f"{errors}"
47
      )
48
    else:
49
      print_err(errors)
50
51
  if content:
52
    content = normalize_eols(content)
53
54
  return content
55
56
57
def _make_xgettext_args(
58
  source_file_path: Path,
59
  domain: str,
60
  keywords: List[str],
61
  no_wrap: bool,
62
  no_location: bool,
63
  extra_args: List[str],
64
) -> List[str]:
65
66
  args = [
67
    "xgettext",
68
    "-d", domain,
69
    "--from-code=UTF-8",
70
    "--add-comments=Translators",
71
    "--output=-",
72
  ]
73
74
  args.extend([f"--keyword={x}" for x in keywords])
75
76
  if no_wrap:
77
    args.append("--no-wrap")
78
79
  if no_location:
80
    args.append("--no-location")
81
82
  if extra_args:
83
    args.extend(extra_args)
84
85
  args.append(stringify_path(source_file_path))
86
87
  return args
88
89
90
def extract_translations(
91
  source_file_path: Path,
92
  domain: str,
93
  keywords: List[str],
94
  no_wrap: bool,
95
  no_location: bool,
96
  xgettext_extra_args: List[str],
97
) -> str:
98
99
  args = _make_xgettext_args(
100
    source_file_path=source_file_path,
101
    domain=domain,
102
    keywords=keywords,
103
    no_wrap=no_wrap,
104
    no_location=no_location,
105
    extra_args=xgettext_extra_args,
106
  )
107
  return get_gettext_tool_output(args)
108
109
110
def strip_translations_header(translations: str) -> str:
111
  """
112
  Strip header from translations generated by ``xgettext``.
113
114
  Header consists of multiple lines separated from the body by an empty line.
115
116
  """
117
  return "\n".join(itertools.dropwhile(len, translations.splitlines()))
118
119
120
def _make_msguniq_args(
121
  pot_file_path: Path,
122
  no_wrap: bool,
123
  no_location: bool,
124
  extra_args: List[str],
125
) -> List[str]:
126
127
  args = [
128
    "msguniq",
129
    "--to-code=utf-8",
130
  ]
131
132
  if no_wrap:
133
    args.append("--no-wrap")
134
135
  if no_location:
136
    args.append("--no-location")
137
138
  if extra_args:
139
    args.extend(extra_args)
140
141
  args.append(stringify_path(pot_file_path))
142
143
  return args
144
145
146
def extract_unique_messages(
147
  pot_file_path: Path,
148
  no_wrap: bool,
149
  no_location: bool,
150
  msguniq_extra_args: List[str],
151
) -> str:
152
153
  args = _make_msguniq_args(
154
    pot_file_path=pot_file_path,
155
    no_wrap=no_wrap,
156
    no_location=no_location,
157
    extra_args=msguniq_extra_args,
158
  )
159
  return get_gettext_tool_output(args)
160
161
162
def _make_msgmerge_args(
163
  po_file_path: Path,
164
  pot_file_path: Path,
165
  no_wrap: bool,
166
  no_location: bool,
167
  extra_args: List[str],
168
) -> List[str]:
169
170
  args = [
171
    "msgmerge",
172
    "-q",
173
    "--previous",
174
  ]
175
176
  if no_wrap:
177
    args.append("--no-wrap")
178
179
  if no_location:
180
    args.append("--no-location")
181
182
  if extra_args:
183
    args.extend(extra_args)
184
185
  args.append(stringify_path(po_file_path))
186
  args.append(stringify_path(pot_file_path))
187
188
  return args
189
190
191
def merge_new_and_existing_translations(
192
  po_file_path: Path,
193
  pot_file_path: Path,
194
  no_wrap: bool,
195
  no_location: bool,
196
  msgmerge_extra_args: List[str],
197
) -> str:
198
199
  args = _make_msgmerge_args(
200
    po_file_path=po_file_path,
201
    pot_file_path=pot_file_path,
202
    no_wrap=no_wrap,
203
    no_location=no_location,
204
    extra_args=msgmerge_extra_args,
205
  )
206
  return get_gettext_tool_output(args)
207
208
209
def _make_msgattrib_args(
210
  po_file_path: Path,
211
  no_wrap: bool,
212
  no_location: bool,
213
  extra_args: List[str],
214
) -> List[str]:
215
216
  args = [
217
    "msgattrib",
218
    "--no-obsolete",
219
  ]
220
221
  if no_wrap:
222
    args.append("--no-wrap")
223
224
  if no_location:
225
    args.append("--no-location")
226
227
  if extra_args:
228
    args.extend(extra_args)
229
230
  po_file_path_str = stringify_path(po_file_path)
231
232
  args.extend([
233
    "-o",
234
    po_file_path_str,
235
    po_file_path_str,
236
  ])
237
238
  return args
239
240
241
def remove_obsolete_translations(
242
  po_file_path: Path,
243
  no_wrap: bool,
244
  no_location: bool,
245
  msgattrib_extra_args: List[str],
246
) -> str:
247
248
  args = _make_msgattrib_args(
249
    po_file_path=po_file_path,
250
    no_wrap=no_wrap,
251
    no_location=no_location,
252
    extra_args=msgattrib_extra_args,
253
  )
254
  _ = get_gettext_tool_output(args)
255
256
257
def _make_msgfmt_args(
258
  mo_file_path: Path,
259
  po_file_path: Path,
260
  fuzzy: bool,
261
  extra_args: List[str],
262
) -> List[str]:
263
264
  args = [
265
    "msgfmt",
266
    "--check-format",
267
  ]
268
269
  if fuzzy:
270
    args.append("-f")
271
272
  if extra_args:
273
    args.extend(extra_args)
274
275
  args.extend([
276
    "-o",
277
    stringify_path(mo_file_path),
278
    stringify_path(po_file_path),
279
  ])
280
281
  return args
282
283
284
def compile_translations(
285
  mo_file_path: Path,
286
  po_file_path: Path,
287
  fuzzy: bool,
288
  msgfmt_extra_args: List[str],
289
) -> None:
290
291
  args = _make_msgfmt_args(
292
    mo_file_path=mo_file_path,
293
    po_file_path=po_file_path,
294
    fuzzy=fuzzy,
295
    extra_args=msgfmt_extra_args,
296
  )
297
  _ = get_gettext_tool_output(args)
298