Path: blob/main/tools/build_config/obsoleteTranslations.py
169674 views
#!/usr/bin/env python1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2# Copyright (C) 2011-2025 German Aerospace Center (DLR) and others.3# This program and the accompanying materials are made available under the4# terms of the Eclipse Public License 2.0 which is available at5# https://www.eclipse.org/legal/epl-2.0/6# This Source Code may also be made available under the following Secondary7# Licenses when the conditions for such availability set forth in the Eclipse8# Public License 2.0 are satisfied: GNU General Public License, version 29# or later which is available at10# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later1213# @file obsoleteTranslations.py14# @author Mirko Barthauer15# @date 2023-07-041617"""18Collect information about lost translations due to changes in the original gettext msgid.19"""20from __future__ import absolute_import21from __future__ import print_function22import os23import io24import polib25import i18n26from glob import glob27from argparse import ArgumentParser2829SUMO_HOME = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))30SUMO_LIBRARIES = os.environ.get("SUMO_LIBRARIES", os.path.join(os.path.dirname(SUMO_HOME), "SUMOLibraries"))313233def getOptions(args=None):34ap = ArgumentParser()35ap.add_argument("-l", "--lang", nargs='*', help="languages to process (using the short codes like fr, de)")36ap.add_argument("--sumo-home", default=SUMO_HOME, help="SUMO root directory to use")37ap.add_argument("-o", "--output", type=str, help="path to output file (protocol of obsolete translations)")38ap.add_argument("--clear", default=False, action="store_true", help="remove obsolete entries from .po files")39ap.add_argument("--patch", nargs="*", type=str,40help="restore obsolete (but still present) translations with sequence of the original \41(odd position) and then the new string (even position) from the source code (= gettext msgid)")42options = ap.parse_args(args)43if options.lang is not None and "en" in options.lang:44options.lang.remove("en")45return options464748def main(args=None):49options = getOptions(args)50if options.lang is None:51options.lang = [os.path.basename(p)[:-8] for p in glob(options.sumo_home + "/data/po/*_sumo.po")]52if options.patch is not None and len(options.patch) > 0 and len(options.patch) % 2 == 1:53print("The new string replacing the old msgid '%s' has not been given - the translation \54cannot be patched." % options.patch[-1])55# run i18n.py to update the translation files56args = ['--sumo-home', options.sumo_home, '--lang']57args.extend(options.lang)58print("Run i18n.py ...")59i18n.main(args=args)60pot_file = options.sumo_home + "/data/po/sumo.pot"61gui_pot_file = options.sumo_home + "/data/po/gui.pot"62py_pot_file = options.sumo_home + "/data/po/py.pot"63potFiles = [pot_file, gui_pot_file, py_pot_file]64if options.output is not None:65if os.path.exists(options.output):66os.remove(options.output) # clear output file67for potFile in potFiles:68print("Check pot file '%s'..." % potFile)69checkPotFile(potFile, options)707172def checkPotFile(potFile, options):73# loop through translated po files and collect the obsolete translations74result = {}75for langCode in options.lang:76poFilePath = os.path.join(os.path.dirname(potFile), "%s_" % langCode + os.path.basename(potFile)[:-1])77if not os.path.exists(poFilePath):78continue79po = polib.pofile(poFilePath)80obsoleteEntries = po.obsolete_entries()81for entry in obsoleteEntries:82if entry.msgid not in result:83result[entry.msgid] = []84result[entry.msgid].append(langCode)85# optionally patch obsolete entry with updated msgid86patched = False87if options.patch is not None:88entriesToRemove = []89for i in range(0, len(options.patch), 2):90if options.patch[i] in result:91updatedEntry = po.find(options.patch[i+1])92translatedEntry = po.find(options.patch[i], include_obsolete_entries=True)93if updatedEntry is not None and translatedEntry is not None:94if len(updatedEntry.msgstr) == 0:95updatedEntry.msgstr = translatedEntry.msgstr96patched = True97entriesToRemove.append(translatedEntry)98print("Patched '%s' for %s" % (options.patch[i], langCode))99else:100print("Has already been translated again: '%s' > '%s'"101% (options.patch[i+1], updatedEntry.msgstr))102for entry in entriesToRemove:103po.remove(entry)104# optionally overwrite obsolete entries completely105if options.clear:106print("Removing obsolete entries from %s..." % poFilePath)107for entry in obsoleteEntries:108po.remove(entry)109if options.clear or patched:110po.save(poFilePath)111if options.output is not None: # write protocol112with io.open(options.output, "a", encoding="utf-8") as f:113for msgid, langCodes in result.items():114f.write("msgid \"%s\" has obsolete translations: %s\n" % (msgid, ', '.join(langCodes)))115116117if __name__ == "__main__":118main()119120121