FreeCAD

Форк
0
/
updatets.py 
451 строка · 15.1 Кб
1
#!/usr/bin/python3
2

3
# SPDX-License-Identifier: LGPL-2.1-or-later
4
# ***************************************************************************
5
# *                                                                         *
6
# *   Copyright (c) 2010 Werner Mayer <wmayer@users.sourceforge.net>        *
7
# *                                                                         *
8
# *   This file is part of FreeCAD.                                         *
9
# *                                                                         *
10
# *   FreeCAD is free software: you can redistribute it and/or modify it    *
11
# *   under the terms of the GNU Lesser General Public License as           *
12
# *   published by the Free Software Foundation, either version 2.1 of the  *
13
# *   License, or (at your option) any later version.                       *
14
# *                                                                         *
15
# *   FreeCAD is distributed in the hope that it will be useful, but        *
16
# *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
17
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
18
# *   Lesser General Public License for more details.                       *
19
# *                                                                         *
20
# *   You should have received a copy of the GNU Lesser General Public      *
21
# *   License along with FreeCAD. If not, see                               *
22
# *   <https://www.gnu.org/licenses/>.                                      *
23
# *                                                                         *
24
# ***************************************************************************
25

26
# Changelog:
27
# 0.5 Add support for Qt 6 lupdate (which fixes MANY bugs and should be preferred)
28
# 0.4 Refactor to always try both C++ and Python translations
29
# 0.3 User-friendly output
30
#     Corrections
31
#     Added Changelog
32
# 0.2 Add Qt5 support
33
#     Add "no obsolete" flags in order to fix 'ghost strings' in Crowdin
34
# 0.1 Initial Release
35

36

37
Usage = """updatets - update all .ts files found in the source directories
38

39
Usage:
40
   updatets
41

42
Authors:
43
  (c) 2010 Werner Mayer
44
  (c) 2019 FreeCAD Volunteers
45
  Licence: LGPL
46

47
Version:
48
  0.5
49
"""
50

51
import os, sys
52
import subprocess
53
import re
54
import pathlib
55

56
directories = [
57
    {"tsname": "App", "workingdir": "./src/App", "tsdir": "Resources/translations"},
58
    {"tsname": "Base", "workingdir": "./src/Base", "tsdir": "Resources/translations"},
59
    {"tsname": "FreeCAD", "workingdir": "./src/Gui", "tsdir": "Language"},
60
    {
61
        "tsname": "AddonManager",
62
        "workingdir": "./src/Mod/AddonManager/",
63
        "tsdir": "Resources/translations",
64
    },
65
    {
66
        "tsname": "Arch",
67
        "workingdir": "./src/Mod/Arch/",
68
        "tsdir": "Resources/translations",
69
    },
70
    {
71
        "tsname": "Assembly",
72
        "workingdir": "./src/Mod/Assembly/",
73
        "tsdir": "Gui/Resources/translations",
74
    },
75
    {
76
        "tsname": "Draft",
77
        "workingdir": "./src/Mod/Draft/",
78
        "tsdir": "Resources/translations",
79
    },
80
    {
81
        "tsname": "Drawing",
82
        "workingdir": "./src/Mod/Drawing/",
83
        "tsdir": "Gui/Resources/translations",
84
    },
85
    {
86
        "tsname": "Fem",
87
        "workingdir": "./src/Mod/Fem/",
88
        "tsdir": "Gui/Resources/translations",
89
    },
90
    {
91
        "tsname": "Inspection",
92
        "workingdir": "./src/Mod/Inspection/",
93
        "tsdir": "Gui/Resources/translations",
94
    },
95
    {
96
        "tsname": "Material",
97
        "workingdir": "./src/Mod/Material/",
98
        "tsdir": "Resources/translations",
99
    },
100
    {
101
        "tsname": "Mesh",
102
        "workingdir": "./src/Mod/Mesh/",
103
        "tsdir": "Gui/Resources/translations",
104
    },
105
    {
106
        "tsname": "MeshPart",
107
        "workingdir": "./src/Mod/MeshPart/",
108
        "tsdir": "Gui/Resources/translations",
109
    },
110
    {
111
        "tsname": "OpenSCAD",
112
        "workingdir": "./src/Mod/OpenSCAD/",
113
        "tsdir": "Resources/translations",
114
    },
115
    {
116
        "tsname": "PartDesign",
117
        "workingdir": "./src/Mod/PartDesign/",
118
        "tsdir": "Gui/Resources/translations",
119
    },
120
    {
121
        "tsname": "Part",
122
        "workingdir": "./src/Mod/Part/",
123
        "tsdir": "Gui/Resources/translations",
124
    },
125
    {
126
        "tsname": "CAM",
127
        "workingdir": "./src/Mod/CAM/",
128
        "tsdir": "Gui/Resources/translations",
129
    },
130
    {
131
        "tsname": "Points",
132
        "workingdir": "./src/Mod/Points/",
133
        "tsdir": "Gui/Resources/translations",
134
    },
135
    {
136
        "tsname": "ReverseEngineering",
137
        "workingdir": "./src/Mod/ReverseEngineering/",
138
        "tsdir": "Gui/Resources/translations",
139
    },
140
    {
141
        "tsname": "Robot",
142
        "workingdir": "./src/Mod/Robot/",
143
        "tsdir": "Gui/Resources/translations",
144
    },
145
    {
146
        "tsname": "Sketcher",
147
        "workingdir": "./src/Mod/Sketcher/",
148
        "tsdir": "Gui/Resources/translations",
149
    },
150
    {
151
        "tsname": "Spreadsheet",
152
        "workingdir": "./src/Mod/Spreadsheet/",
153
        "tsdir": "Gui/Resources/translations",
154
    },
155
    {
156
        "tsname": "StartPage",
157
        "workingdir": "./src/Mod/Start/",
158
        "tsdir": "Gui/Resources/translations",
159
    },
160
    {
161
        "tsname": "TechDraw",
162
        "workingdir": "./src/Mod/TechDraw/",
163
        "tsdir": "Gui/Resources/translations",
164
    },
165
    {
166
        "tsname": "Test",
167
        "workingdir": "./src/Mod/Test/",
168
        "tsdir": "Gui/Resources/translations",
169
    },
170
    {
171
        "tsname": "Tux",
172
        "workingdir": "./src/Mod/Tux/",
173
        "tsdir": "Resources/translations",
174
    },
175
    {
176
        "tsname": "Web",
177
        "workingdir": "./src/Mod/Web/",
178
        "tsdir": "Gui/Resources/translations",
179
    },
180
    {
181
        "tsname": "Help",
182
        "workingdir": "./src/Mod/Help/",
183
        "tsdir": "Resources/translations",
184
    },
185
]
186

187
# Exclude these files from consideration
188
excluded_files = [
189
    ("CAM", "UtilsArguments.py"),  # Causes lupdate to hang
190
    ("CAM", "refactored_centroid_post.py"),  # lupdate bug causes failure on line 245
191
    ("CAM", "refactored_grbl_post.py"),  # lupdate bug causes failure on line 212
192
    ("CAM", "refactored_linuxcnc_post.py"),  # lupdate bug causes failure on line 178
193
    ("CAM", "refactored_mach3_mach4_post.py"),  # lupdate bug causes failure on line 186
194
    ("CAM", "refactored_test_post.py"),  # lupdate bug causes failure on lines 42 and 179
195
]
196

197
QMAKE = ""
198
LUPDATE = ""
199
PYLUPDATE = ""
200
LCONVERT = ""
201
QT_VERSION_MAJOR = ""
202

203

204
def find_tools(noobsolete=True):
205

206
    print(Usage + "\nFirst, lets find all necessary tools on your system")
207
    global QMAKE, LUPDATE, PYLUPDATE, LCONVERT, QT_VERSION_MAJOR
208

209
    p = subprocess.run(["lupdate", "-version"], check=True, stdout=subprocess.PIPE)
210
    lupdate_version = p.stdout.decode()
211
    result = re.search(r".* ([456])\.([\d]+)\.([\d]+)", lupdate_version)
212
    if not result:
213
        print(f"Failed to parse version from {lupdate_version}")
214
    QT_VERSION_MAJOR = int(result.group(1))
215
    QT_VERSION_MINOR = int(result.group(2))
216
    QT_VERSION_PATCH = int(result.group(3))
217
    QT_VERSION = f"{QT_VERSION_MAJOR}.{QT_VERSION_MINOR}.{QT_VERSION_PATCH}"
218
    print(f"Found Qt {QT_VERSION}")
219

220
    if QT_VERSION_MAJOR < 6:
221
        if os.system("lupdate -version") == 0:
222
            LUPDATE = "lupdate"
223
            # TODO: we suppose lupdate is a symlink to lupdate-qt4 for now
224
            if noobsolete:
225
                LUPDATE += " -no-obsolete"
226
        elif os.system("lupdate-qt5 -version") == 0:
227
            LUPDATE = "lupdate-qt5"
228
            if noobsolete:
229
                LUPDATE += " -no-obsolete"
230
        else:
231
            raise Exception("Cannot find lupdate")
232
    else:
233
        LUPDATE = "lupdate"
234

235
    if QT_VERSION_MAJOR < 6:
236
        if os.system("qmake -version") == 0:
237
            QMAKE = "qmake"
238
        elif os.system("qmake-qt5 -version") == 0:
239
            QMAKE = "qmake-qt5"
240
        else:
241
            raise Exception("Cannot find qmake")
242
        if os.system("pylupdate -version") == 0:
243
            PYLUPDATE = "pylupdate"
244
        elif os.system("pylupdate6 --version") == 0:
245
            PYLUPDATE = "pylupdate6"
246
            if noobsolete:
247
                PYLUPDATE += " -no-obsolete"
248
        elif os.system("pylupdate5 -version") == 0:
249
            PYLUPDATE = "pylupdate5"
250
            if noobsolete:
251
                PYLUPDATE += " -noobsolete"
252
        elif os.system("pyside2-lupdate -version") == 0:
253
            PYLUPDATE = "pyside2-lupdate"
254
            raise Exception(
255
                "Please do not use pyside2-lupdate at the moment, as it shows encoding problems. Please use pylupdate5 or 6 instead."
256
            )
257
        else:
258
            raise Exception("Cannot find pylupdate")
259
    else:
260
        QMAKE = "(qmake not needed for Qt 6 and later)"
261
        PYLUPDATE = "(pylupdate not needed for Qt 6 and later)"
262
    if os.system("lconvert -h") == 0:
263
        LCONVERT = "lconvert"
264
        if noobsolete and QT_VERSION_MAJOR < 6:
265
            LCONVERT += " -no-obsolete"
266
    else:
267
        raise Exception("Cannot find lconvert")
268
    print(
269
        "\nAll Qt tools have been found!\n",
270
        "\t" + QMAKE + "\n",
271
        "\t" + LUPDATE + "\n",
272
        "\t" + PYLUPDATE + "\n",
273
        "\t" + LCONVERT + "\n",
274
    )
275
    print("==============================================\n")
276

277

278
def update_translation(entry):
279

280
    global QMAKE, LUPDATE, LCONVERT, QT_VERSION_MAJOR
281
    cur = os.getcwd()
282
    log_redirect = f" 2>> {cur}/tsupdate_stderr.log 1>> {cur}/tsupdate_stdout.log"
283
    os.chdir(entry["workingdir"])
284
    existingjsons = [f for f in os.listdir(".") if f.endswith(".json")]
285
    project_filename = entry["tsname"] + ".pro"
286
    tsBasename = os.path.join(entry["tsdir"], entry["tsname"])
287

288
    if QT_VERSION_MAJOR < 6:
289
        print("\n\n=============================================")
290
        print(f"EXTRACTING STRINGS FOR {entry['tsname']}")
291
        print("=============================================", flush=True)
292
        execline = []
293
        execline.append(
294
            f"touch dummy_cpp_file_for_lupdate.cpp"
295
        )  # lupdate 5.x requires at least one source file to process the UI files
296
        execline.append(f"touch {tsBasename}py.ts")
297
        execline.append(f'{PYLUPDATE} `find ./ -name "*.py"` -ts {tsBasename}py.ts {log_redirect}')
298
        execline.append(f"{QMAKE} -project -o {project_filename} -r")
299
        execline.append(f"{LUPDATE} {project_filename} -ts {tsBasename}.ts {log_redirect}")
300
        execline.append(
301
            f"sed 's/<translation.*>.*<\/translation>/<translation type=\"unfinished\"><\/translation>/g' {tsBasename}.ts > {tsBasename}.ts.temp"
302
        )
303
        execline.append(f"mv {tsBasename}.ts.temp {tsBasename}.ts")
304
        execline.append(
305
            f"{LCONVERT} -i {tsBasename}py.ts {tsBasename}.ts -o {tsBasename}.ts {log_redirect}"
306
        )
307
        execline.append(f"rm {tsBasename}py.ts")
308
        execline.append(f"rm dummy_cpp_file_for_lupdate.cpp")
309

310
        print(f"Executing commands in {entry['workingdir']}:")
311
        for line in execline:
312
            print(line)
313
            os.system(line)
314
        print()
315

316
        os.remove(project_filename)
317
        # lupdate creates json files since Qt5.something. Remove them here too
318
        for jsonfile in [f for f in os.listdir(".") if f.endswith(".json")]:
319
            if not jsonfile in existingjsons:
320
                os.remove(jsonfile)
321

322
    elif QT_VERSION_MAJOR == 6:
323
        # In Qt6, QMake project files are deprecated, and lupdate directly scans for source files. The same executable is
324
        # used for all supported programming languages, so it's just a single function call
325

326
        # For Windows compatibility, do most of the work in Python:
327
        extensions = [
328
            "java",
329
            "jui",
330
            "ui",
331
            "c",
332
            "c++",
333
            "cc",
334
            "cpp",
335
            "cxx",
336
            "ch",
337
            "h",
338
            "h++",
339
            "hh",
340
            "hpp",
341
            "hxx",
342
            "js",
343
            "qs",
344
            "qml",
345
            "qrc",
346
            "py",
347
        ]
348
        with open("files_to_translate.txt", "w", encoding="utf-8") as file_list:
349
            for root, dirs, files in os.walk("./"):
350
                for f in files:
351
                    skip = False
352
                    for exclusion in excluded_files:
353
                        if entry["tsname"] == exclusion[0] and f == exclusion[1]:
354
                            print(
355
                                f"    (NOTE: Excluding file {f} because it is in the excluded_files list)"
356
                            )
357
                            skip = True
358
                            break
359
                    if not skip and pathlib.Path(f).suffix[1:] in extensions:
360
                        file_list.write(os.path.join(root, f) + "\n")
361

362
        try:
363
            p = subprocess.run(
364
                [
365
                    LUPDATE,
366
                    "@files_to_translate.txt",
367
                    "-I",
368
                    "./",
369
                    "-recursive",
370
                    "-no-sort",
371
                    "-ts",
372
                    f"{tsBasename}.ts",
373
                ],
374
                capture_output=True,
375
                timeout=60,
376
                encoding="utf-8",
377
            )
378
            if not p:
379
                raise RuntimeError("No return result from lupdate")
380
            if not p.stdout:
381
                raise RuntimeError("No stdout from lupdate")
382
        except Exception as e:
383
            print("*" * 80)
384
            print("*" * 80)
385
            print("*" * 80)
386
            print(f"ERROR RUNNING lupdate -- TRANSLATIONS FOR {entry['tsname']} PROBABLY FAILED...")
387
            print(str(e))
388
            print("*" * 80)
389
            print("*" * 80)
390
            print("*" * 80)
391
            os.chdir(cur)
392
            return
393

394
        with open(f"{cur}/tsupdate_stdout.log", "a", encoding="utf-8") as f:
395
            f.write(p.stdout)
396
            print(p.stdout, flush=True)
397

398
        with open(f"{cur}/tsupdate_stderr.log", "a", encoding="utf-8") as f:
399
            f.write(p.stderr)
400

401
        # Strip out obsolete strings, and make sure there are no translations in the main *.ts file
402
        subprocess.run(
403
            [
404
                LCONVERT,
405
                "-drop-translations",
406
                "-i",
407
                f"{tsBasename}.ts",
408
                "-o",
409
                f"{tsBasename}.ts",
410
            ]
411
        )
412

413
    else:
414
        print(
415
            "ERROR: unrecognized version of lupdate -- found Qt {QT_VERSION_MAJOR}, we only support 4, 5 and 6"
416
        )
417
        exit(1)
418

419
    os.chdir(cur)
420

421

422
def main(mod=None):
423

424
    find_tools()
425
    path = os.path.realpath(__file__)
426
    path = os.path.dirname(path)
427
    os.chdir(path)
428
    os.chdir("..")
429
    os.chdir("..")
430
    print("\n\n\n BEGINNING TRANSLATION EXTRACTION \n\n")
431
    if mod:
432
        for i in directories:
433
            if i["tsname"] == mod:
434
                print("WARNING - Updating", mod, "ONLY")
435
                update_translation(i)
436
                break
437
    else:
438
        for i in directories:
439
            update_translation(i)
440
    print(
441
        "\nIf updatets.py was run successfully, the next step is to run ./src/Tools/updatecrowdin.py"
442
    )
443
    print("stderr output from lupdate can be found in tsupdate_stderr.log")
444
    print("stdout output from lupdate can be found in tsupdate_stdout.log")
445

446

447
if __name__ == "__main__":
448
    if len(sys.argv[1:]) > 0:
449
        main(sys.argv[1])
450
    else:
451
        main()
452

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.