Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/build_config/i18n.py
169674 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2011-2025 German Aerospace Center (DLR) and others.
4
# This program and the accompanying materials are made available under the
5
# terms of the Eclipse Public License 2.0 which is available at
6
# https://www.eclipse.org/legal/epl-2.0/
7
# This Source Code may also be made available under the following Secondary
8
# Licenses when the conditions for such availability set forth in the Eclipse
9
# Public License 2.0 are satisfied: GNU General Public License, version 2
10
# or later which is available at
11
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
14
# @file i18n.py
15
# @author Michael Behrisch
16
# @date 2022-10-08
17
18
"""
19
Prepare gettext pot and po files for all languages.
20
"""
21
from __future__ import absolute_import
22
from __future__ import print_function
23
import os
24
import io
25
import subprocess
26
import difflib
27
from glob import glob
28
from argparse import ArgumentParser
29
30
31
SUMO_HOME = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
32
SUMO_LIBRARIES = os.environ.get("SUMO_LIBRARIES", os.path.join(os.path.dirname(SUMO_HOME), "SUMOLibraries"))
33
34
35
def get_args(args=None):
36
arg_parser = ArgumentParser()
37
arg_parser.add_argument("-l", "--lang", nargs='*', help="languages to process")
38
arg_parser.add_argument("-m", "--mo-only", action="store_true", default=False,
39
help="only generate mo files, do not update po and pot")
40
arg_parser.add_argument("-f", "--fuzzy", action="store_true", default=False,
41
help="use fuzzy matching to prefill new message ids")
42
arg_parser.add_argument("--sumo-home", default=SUMO_HOME, help="SUMO root directory to use")
43
arg_parser.add_argument("-o", "--out-dir", help="output directory to use")
44
return arg_parser.parse_args(args)
45
46
47
def generate_po(sumo_home, path, languages, pot_file, gui_pot_file, py_pot_file, fuzzy):
48
pots = {pot_file: open(pot_file + ".txt", "w"),
49
gui_pot_file: open(gui_pot_file + ".txt", "w"),
50
py_pot_file: open(py_pot_file + ".txt", "w")}
51
for f in sorted(glob(sumo_home + "/src/*.cpp") +
52
glob(sumo_home + "/src/*/*.cpp") +
53
glob(sumo_home + "/src/*/*/*.cpp") +
54
glob(sumo_home + "/src/*/*/*/*.cpp") +
55
glob(sumo_home + "/src/*.h") +
56
glob(sumo_home + "/src/*/*.h") +
57
glob(sumo_home + "/src/*/*/*.h") +
58
glob(sumo_home + "/src/*/*/*/*.h") +
59
glob(sumo_home + "/tools/game/*.py")):
60
if f[-3:] == ".py":
61
print(f, file=pots[py_pot_file])
62
elif "gui" in f[len(sumo_home):] or "netedit" in f[len(sumo_home):]:
63
print(f, file=pots[gui_pot_file])
64
else:
65
print(f, file=pots[pot_file])
66
for pot, sources in pots.items():
67
sources.close()
68
arguments = [path + "xgettext", "--files-from=" + sources.name, "--from-code=UTF-8",
69
"--keyword=TL", "--keyword=TLC:1c,2", "--keyword=TLF", "--output=" + pot + ".new",
70
"--package-name=sumo", "[email protected]"]
71
if pot == py_pot_file:
72
arguments.append("--language=Python")
73
subprocess.check_call(arguments)
74
os.remove(sources.name)
75
with io.open(pot + ".new", encoding="utf-8") as new, io.open(pot + ".fixed", "w", encoding="utf-8") as fixed:
76
for s in new.readlines():
77
if s.startswith("#") and sumo_home in s:
78
fixed.write(s.replace(sumo_home, "").replace("\\", "/").replace("#: /", "#: "))
79
else:
80
fixed.write(s)
81
os.remove(new.name)
82
has_diff = True
83
if os.path.exists(pot):
84
with io.open(pot, encoding="utf-8") as old, io.open(pot + ".fixed", encoding="utf-8") as fixed:
85
a = [s for s in old.readlines() if not s.startswith(("#", '"POT-Creation-Date:'))]
86
b = [s for s in fixed.readlines() if not s.startswith(("#", '"POT-Creation-Date:'))]
87
has_diff = list(difflib.unified_diff(a, b))
88
if has_diff:
89
os.remove(pot)
90
if has_diff:
91
os.rename(fixed.name, pot)
92
else:
93
os.remove(fixed.name)
94
for lang in languages:
95
po_file = "%s/data/po/%s_%s" % (sumo_home, lang, os.path.basename(pot)[:-1])
96
if os.path.exists(po_file):
97
subprocess.check_call([path + "msgmerge", po_file, pot,
98
"--output-file=" + po_file] + ([] if fuzzy else ["--no-fuzzy-matching"]))
99
else:
100
subprocess.check_call([path + "msginit", "--input=" + pot, "--output=" + po_file,
101
"--no-translator", "--locale=" + lang])
102
103
104
def main(args=None):
105
path = ""
106
if os.name == "nt":
107
paths = glob(os.path.join(SUMO_LIBRARIES, "gettext-*", "tools", "gettext", "bin"))
108
if paths:
109
path = paths[0] + os.path.sep
110
options = get_args(args)
111
if options.lang is None:
112
options.lang = [os.path.basename(p)[:-8] for p in glob(options.sumo_home + "/data/po/*_sumo.po")]
113
if options.out_dir is None:
114
options.out_dir = options.sumo_home + "/data"
115
pot_file = options.sumo_home + "/data/po/sumo.pot"
116
gui_pot_file = options.sumo_home + "/data/po/gui.pot"
117
py_pot_file = options.sumo_home + "/data/po/py.pot"
118
if not options.mo_only:
119
generate_po(options.sumo_home, path, options.lang, pot_file, gui_pot_file, py_pot_file, options.fuzzy)
120
for lang in options.lang:
121
po_files = ["%s/data/po/%s_%s" % (options.sumo_home, lang, os.path.basename(pot)[:-1])
122
for pot in (pot_file, gui_pot_file, py_pot_file)]
123
if not os.path.exists("%s/po" % options.out_dir):
124
os.makedirs("%s/po" % options.out_dir)
125
merged_po_file = "%s/po/%s.po" % (options.out_dir, lang)
126
subprocess.check_call([path + "msgcat"] + po_files + ["--output-file=" + merged_po_file])
127
d = "%s/locale/%s/LC_MESSAGES" % (options.out_dir, lang)
128
try:
129
os.makedirs(d)
130
except OSError:
131
pass
132
subprocess.check_call([path + "msgfmt", merged_po_file, "--output-file=%s/sumo.mo" % d])
133
os.remove(merged_po_file)
134
135
136
if __name__ == "__main__":
137
main()
138
139