Skip to content

Commit

Permalink
ENH: Make translations in Python scripts more robust
Browse files Browse the repository at this point in the history
If a Python string with placeholders (e.g., Loading {filename}) is translated incorrectly so that the placeholder is not in the translated string (e.g., Carga {nombre de archivo}) then currently an exception is raised and execution of the Python code stops.

Fixed by only using the translated string if the placeholders match. Otherwise a warning is logged and the translation is not used.

fixes Slicer#7235

Co-authored-by: Jean-Christophe Fillion-Robin <jchris.fillionr@kitware.com>
  • Loading branch information
lassoan and jcfr committed Feb 29, 2024
1 parent 37d06b5 commit 50446f2
Showing 1 changed file with 30 additions and 3 deletions.
33 changes: 30 additions & 3 deletions Base/Python/slicer/i18n.py
@@ -1,6 +1,7 @@
import os
import inspect

import logging
import os
import re

def translate(context, text):
"""Translate message to the current application language.
Expand Down Expand Up @@ -51,6 +52,32 @@ def tr(text):
statusText = _("Idle") if idle else _("Running")
"""

def findBracedStrings(text):
"""Get all placeholders (replacement fields) in the input format string text.
All placeholders delimited by curly braces are returned except the ones enclosed in
double-braces.
See https://docs.python.org/3/library/string.html#formatstrings
"""
pattern = r"(?<!\{)\{([^\{\}]+)\}(?!\})"
matches = re.findall(pattern, text)
return matches

filename = inspect.stack()[1][1]
contextName = getContext(filename)
return translate(contextName, text)
translatedText = translate(contextName, text)

# Accept the translation only if all placeholders are present in the translated text to prevent runtime errors.
# For example:
# text = "delete {count} files"
# translatedText = "supprimer {compter} fichiers" (incorrect, because `count` should not have been translated)
# would fail at runtime with a KeyError when `_("delete {count} files").format(count=numberOfSomeItems)` is called,
# as after translation it turns into `"supprimer {compter} fichiers".format(count=numberOfSomeItems)`.
# The check prevents the runtime error: only a warning is logged and the incorrect translation is ignored.
if set(findBracedStrings(text)) != set(findBracedStrings(translatedText)):
logging.warning(f"In context '{contextName}', translation of '{text}' to '{translatedText}' is incorrect: placeholders do not match")
return text

return translatedText

0 comments on commit 50446f2

Please sign in to comment.