i18n Python scripts: allow autodetection of translation files

- Add class method availableTranslations() to AbstractFormatHandler in
  i18n.py.

- Use it in fg-update-translation-files to allow autodetection of the
  available translations when no LANGUAGE_CODE is passed to
  fg-update-translation-files.

This should address James' wish at
<https://sourceforge.net/p/flightgear/mailman/message/37003967/>.
This commit is contained in:
Florent Rougon
2020-05-08 10:33:35 +02:00
parent 4e28b88c22
commit b3893f7bda
3 changed files with 62 additions and 7 deletions
+15 -3
View File
@@ -36,6 +36,10 @@ localization files in $FG_ROOT/Translations/<language_code>):
fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \ fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \
merge-new-master $languages merge-new-master $languages
Note: you may omit $languages in the fg-update-translation-files command if
you want to autodetect the FlightGear-nonQt.xlf files present in
$FG_ROOT/Translations.
Updating XLIFF files to reflect changes in the default translation Updating XLIFF files to reflect changes in the default translation
------------------------------------------------------------------ ------------------------------------------------------------------
@@ -45,6 +49,9 @@ modified or removed, or categories added or removed[3]):
fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \ fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \
merge-new-master $languages merge-new-master $languages
Note: you may omit $languages in this command if you want to autodetect the
FlightGear-nonQt.xlf files present in $FG_ROOT/Translations.
Updating XLIFF files to mark or remove obsolete translated strings Updating XLIFF files to mark or remove obsolete translated strings
------------------------------------------------------------------ ------------------------------------------------------------------
@@ -53,9 +60,14 @@ To remove unused translated strings (not to be done too often in my opinion):
fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \ fg-update-translation-files --transl-dir="$FG_ROOT/Translations" \
remove-unused $languages remove-unused $languages
(you may replace 'remove-unused' with 'mark-unused' to just mark the strings Notes:
as not-to-be-translated, however 'merge-new-master' presented above already
does that) - You may omit $languages in this command if you want to autodetect the
FlightGear-nonQt.xlf files present in $FG_ROOT/Translations.
- It is possible to replace 'remove-unused' with 'mark-unused' to just mark
the strings as not-to-be-translated; however, 'merge-new-master' presented
above already does that.
Merging contents from an XLIFF file into another one Merging contents from an XLIFF file into another one
---------------------------------------------------- ----------------------------------------------------
+20 -4
View File
@@ -27,6 +27,7 @@ import sys
import flightgear.meta.logging import flightgear.meta.logging
import flightgear.meta.i18n as fg_i18n import flightgear.meta.i18n as fg_i18n
from flightgear.meta.i18n import XliffFormatHandler
PROGNAME = os.path.basename(sys.argv[0]) PROGNAME = os.path.basename(sys.argv[0])
@@ -45,7 +46,7 @@ def processCommandLine():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
usage="""\ usage="""\
%(prog)s [OPTION ...] ACTION LANGUAGE_CODE... %(prog)s [OPTION ...] ACTION [LANGUAGE_CODE]...
Update FlightGear XLIFF localization files.""", Update FlightGear XLIFF localization files.""",
description="""\ description="""\
This program performs the following operations (actions) on FlightGear XLIFF This program performs the following operations (actions) on FlightGear XLIFF
@@ -68,6 +69,13 @@ translation files (*.xlf):
In the XLIFF localization files corresponding to the specified In the XLIFF localization files corresponding to the specified
language(s), remove all translated strings that are marked as unused. language(s), remove all translated strings that are marked as unused.
If no LANGUAGE_CODE is provided as an argument, then assuming $transl_dir
represents the value passed to --transl-dir, all directories $d such that a
file named FlightGear-nonQt.xlf is found in $transl_dir/$d will be acted on as
if they had been passed as LANGUAGE_CODE arguments (actually, the directory
$transl_dir/default is not considered as a candidate; it is simply skipped).
Typically, $transl_dir is /path/to/FGData/Translations.
A translated string that is marked as unused is still present in the XLIFF A translated string that is marked as unused is still present in the XLIFF
localization file; it is just presented in a way that tells translators they localization file; it is just presented in a way that tells translators they
don't need to worry about it. On the other hand, when a translated string is don't need to worry about it. On the other hand, when a translated string is
@@ -105,7 +113,7 @@ general on the short or mid-term: they only take some space.
remove those already marked as unused from the XLIFF remove those already marked as unused from the XLIFF
files corresponding to each given LANGUAGE_CODE (i.e., files corresponding to each given LANGUAGE_CODE (i.e.,
those that are not in the default translation)""") those that are not in the default translation)""")
parser.add_argument("lang_code", metavar="LANGUAGE_CODE", nargs="+", parser.add_argument("lang_code", metavar="LANGUAGE_CODE", nargs="*",
help="""\ help="""\
codes of languages to operate on (e.g., fr, en_GB, it, codes of languages to operate on (e.g., fr, en_GB, it,
es_ES...)""") es_ES...)""")
@@ -125,11 +133,19 @@ class MarkOrRemoveUnusedAction(enum.Enum):
mark, remove = range(2) mark, remove = range(2)
def langCodesToActOn():
"""Return an iterable of all language codes we were told to work on."""
if params.lang_code:
return params.lang_code
else:
return XliffFormatHandler.availableTranslations(params.transl_dir)
def markOrRemoveUnused(l10nResPoolMgr, action): def markOrRemoveUnused(l10nResPoolMgr, action):
formatHandler = fg_i18n.XliffFormatHandler() formatHandler = fg_i18n.XliffFormatHandler()
masterTransl = l10nResPoolMgr.readFgMasterTranslation().transl masterTransl = l10nResPoolMgr.readFgMasterTranslation().transl
for langCode in params.lang_code: for langCode in langCodesToActOn():
xliffPath = formatHandler.defaultFilePath(params.transl_dir, langCode) xliffPath = formatHandler.defaultFilePath(params.transl_dir, langCode)
transl = formatHandler.readTranslation(xliffPath) transl = formatHandler.readTranslation(xliffPath)
@@ -148,7 +164,7 @@ def mergeNewMaster(l10nResPoolMgr):
formatHandler = fg_i18n.XliffFormatHandler() formatHandler = fg_i18n.XliffFormatHandler()
masterTransl = l10nResPoolMgr.readFgMasterTranslation().transl masterTransl = l10nResPoolMgr.readFgMasterTranslation().transl
for langCode in params.lang_code: for langCode in langCodesToActOn():
xliffPath = formatHandler.defaultFilePath(params.transl_dir, langCode) xliffPath = formatHandler.defaultFilePath(params.transl_dir, langCode)
transl = formatHandler.readTranslation(xliffPath) transl = formatHandler.readTranslation(xliffPath)
transl.mergeMasterTranslation(masterTransl, logger=logger) transl.mergeMasterTranslation(masterTransl, logger=logger)
@@ -1001,6 +1001,33 @@ class AbstractFormatHandler(metaclass=abc.ABCMeta):
baseName = cls.defaultFileBaseName(targetLanguage) baseName = cls.defaultFileBaseName(targetLanguage)
return os.path.join(translationsDir, targetLanguage, baseName) return os.path.join(translationsDir, targetLanguage, baseName)
@classmethod
def availableTranslations(cls, translationsDir):
"""Return a list of all available translations in translationsDir.
This method expects a particular layout for translation files:
the one used in $FG_ROOT/Translations. More precisely, it looks
for all files named LANG_CODE/NAME in translationsDir, where
NAME is cls.defaultFileBaseName(LANG_CODE). The special
directory translationsDir/DEFAULT_LANG_DIR is not explored;
thus, the result cannot contain DEFAULT_LANG_DIR.
Return a list of language codes, sorted with list.sort().
"""
res = []
with os.scandir(translationsDir) as it:
for entry in it:
if (entry.name != DEFAULT_LANG_DIR and entry.is_dir() and
os.path.isfile(
os.path.join(
translationsDir, entry.name,
cls.defaultFileBaseName(entry.name)))):
res.append(entry.name)
res.sort()
return res
@abc.abstractmethod @abc.abstractmethod
def writeTranslation(self, transl, filePath): def writeTranslation(self, transl, filePath):
"""Write a Translation instance to a file.""" """Write a Translation instance to a file."""