[PATCH] Prevent incomplete translations from making the TUI unusable (#1235617)

Martin Kolman mkolman at redhat.com
Mon Jul 27 18:42:44 UTC 2015


What happens is that we have the following translatable strings:
- "Please make your choice from above ['q' to quit | 'c' to continue | 'r' to refresh]"
- "q"
- "c"
- "r"
All of these strings can be translated *separately* and in many partial
translations *some of them are translated while some are not*.

The end result is that we might be telling the user to press say "p"
for "pokračovat" but me might actually expect "c" for continue,
because the "c" string was not translated to "p".

This affects both the Anaconda and Initial Setup TUIs and can make the
UI totally unusable.

The fix for this is to remove the keystrokes from the long  strings and replace
them by string formatting operators & do the keystroke transformation separately,
simplified example:

_("Please make your choice from above ['%s' to quit | '%s' to continue | '%s' to refresh]") % \
 (_("q"), _("c"), _("r"))

This makes sure that we will always ask the user to type the same
keystroke that is actually displayed.

Resolves: rhbz#1235617
Signed-off-by: Martin Kolman <mkolman at redhat.com>
---
 pyanaconda/ui/tui/hubs/__init__.py      | 11 +++++++++--
 pyanaconda/ui/tui/hubs/summary.py       | 16 ++++++++++++----
 pyanaconda/ui/tui/simpleline/base.py    | 11 ++++++++++-
 pyanaconda/ui/tui/spokes/langsupport.py | 15 ++++++++++++---
 pyanaconda/ui/tui/spokes/time_spoke.py  | 12 +++++++++---
 pyanaconda/ui/tui/tuiobject.py          | 15 +++++++++++----
 6 files changed, 63 insertions(+), 17 deletions(-)

diff --git a/pyanaconda/ui/tui/hubs/__init__.py b/pyanaconda/ui/tui/hubs/__init__.py
index 82b49fe..b67ed0b 100644
--- a/pyanaconda/ui/tui/hubs/__init__.py
+++ b/pyanaconda/ui/tui/hubs/__init__.py
@@ -129,7 +129,14 @@ class TUIHub(TUIObject, common.Hub):
         """
         if self._spoke_count == 1:
             single_spoke_title = self._spokes.values()[0].title
-            return _(u"  Please make your choice from [ '1' to enter the %s spoke | 'q' to quit |\n"
-                     "  'c' to continue | 'r' to refresh]: ") % single_spoke_title
+            # TRANSLATORS: 'q' to quit
+            quit = C_('TUI|Spoke Navigation', 'q'),
+            # TRANSLATORS:'c' to continue
+            forward = C_('TUI|Spoke Navigation', 'c'),
+            # TRANSLATORS:'r' to refresh
+            refresh = C_('TUI|Spoke Navigation', 'r')
+
+            return _(u"  Please make your choice from [ '1' to enter the %s spoke | '%s' to quit |\n"
+                     "  '%s' to continue | '%s' to refresh]: ") % (single_spoke_title, quit, forward, refresh)
         else:
             return super(TUIHub, self).prompt(args)
diff --git a/pyanaconda/ui/tui/hubs/summary.py b/pyanaconda/ui/tui/hubs/summary.py
index 5d414ee..866794a 100644
--- a/pyanaconda/ui/tui/hubs/summary.py
+++ b/pyanaconda/ui/tui/hubs/summary.py
@@ -24,7 +24,7 @@ from pyanaconda.ui.lib.space import FileSystemSpaceChecker, DirInstallSpaceCheck
 from pyanaconda.ui.tui.hubs import TUIHub
 from pyanaconda.flags import flags
 from pyanaconda.errors import CmdlineError
-from pyanaconda.i18n import N_, _
+from pyanaconda.i18n import N_, _, C_
 import sys
 import time
 
@@ -87,8 +87,16 @@ class SummaryHub(TUIHub):
 
         # override the default prompt since we want to offer the 'b' to begin
         # installation option here
-        return _("  Please make your choice from above ['q' to quit | 'b' to begin installation |\n  'r' to refresh]: ")
 
+        # TRANSLATORS: 'q' to quit
+        quit = C_('TUI|Spoke Navigation', 'q')
+        # TRANSLATORS: 'b' to begin installation
+        begin = C_('TUI|Spoke Navigation', 'b')
+        # TRANSLATORS: 'r' to refresh
+        refresh = C_('TUI|Spoke Navigation', 'r')
+
+        return _("  Please make your choice from above ['%s' to quit | '%s' to begin installation |\n  '%s' to refresh]: ") % \
+                (quit, begin, refresh)
     def input(self, args, key):
         """Handle user input. Numbers are used to show a spoke, the rest is passed
         to the higher level for processing."""
@@ -101,7 +109,7 @@ class SummaryHub(TUIHub):
             # If we get a continue, check for unfinished spokes.  If unfinished
             # don't continue
             # TRANSLATORS: 'b' to begin installation
-            if key == _('b'):
+            if key == C_('TUI|Spoke Navigation', 'b'):
                 for spoke in self._spokes.values():
                     if not spoke.completed and spoke.mandatory:
                         print(_("Please complete all spokes before continuing"))
@@ -115,7 +123,7 @@ class SummaryHub(TUIHub):
                     self.app.close_screen()
                     return True
             # TRANSLATORS: 'c' to continue
-            elif key == _('c'):
+            elif key == C_('TUI|Spoke Navigation', 'c'):
                 # Kind of a hack, but we want to ignore if anyone presses 'c'
                 # which is the global TUI key to close the current screen
                 return False
diff --git a/pyanaconda/ui/tui/simpleline/base.py b/pyanaconda/ui/tui/simpleline/base.py
index c4fe736..b09afab 100644
--- a/pyanaconda/ui/tui/simpleline/base.py
+++ b/pyanaconda/ui/tui/simpleline/base.py
@@ -627,7 +627,16 @@ class UIScreen(object):
                  to skip further input processing
         :rtype: unicode|None
         """
-        return _(u"  Please make your choice from above ['q' to quit | 'c' to continue |\n  'r' to refresh]: ")
+
+        # TRANSLATORS: 'q' to quit
+        quit = C_('TUI|Spoke Navigation', 'q'),
+        # TRANSLATORS:'c' to continue
+        forward = C_('TUI|Spoke Navigation', 'c'),
+        # TRANSLATORS:'r' to refresh
+        refresh = C_('TUI|Spoke Navigation', 'r')
+
+        return _(u"  Please make your choice from above ['%s' to quit | '%s' to continue |\n  '%s' to refresh]: ") % \
+                (quit, forward, refresh)
 
     @property
     def app(self):
diff --git a/pyanaconda/ui/tui/spokes/langsupport.py b/pyanaconda/ui/tui/spokes/langsupport.py
index 0b1e836..6685bf3 100644
--- a/pyanaconda/ui/tui/spokes/langsupport.py
+++ b/pyanaconda/ui/tui/spokes/langsupport.py
@@ -24,7 +24,7 @@ from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
 from pyanaconda.ui.common import FirstbootSpokeMixIn
 from pyanaconda import localization
-from pyanaconda.i18n import N_, _
+from pyanaconda.i18n import N_, _, C_
 
 class LangSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
     """
@@ -109,7 +109,7 @@ class LangSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
             pass
 
         # TRANSLATORS: 'b' to go back
-        if key.lower() == _("b"):
+        if key.lower() == C_('TUI|Spoke Navigation|Language Support', 'b'):
             self.app.switch_screen(self, None)
             return True
         else:
@@ -117,7 +117,16 @@ class LangSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
 
     def prompt(self, args=None):
         """ Override default prompt with a custom prompt. """
-        return _("Please select language support to install.\n[b to language list, c to continue, q to quit]: ")
+
+        # TRANSLATORS: 'b' to go back
+        back = C_('TUI|Spoke Navigation|Language Support', 'b'),
+        # TRANSLATORS:'c' to continue
+        forward = C_('TUI|Spoke Navigation|Language Support', 'c'),
+        # TRANSLATORS:'q' to quit
+        quit = C_('TUI|Spoke Navigation|Language Support', 'q')
+
+        return _("Please select language support to install.\n[%s to return to language list, %s to continue, %s to quit]: ") % \
+                (back, forward, quit)
 
     def apply(self):
         """ Store the selected langsupport locales """
diff --git a/pyanaconda/ui/tui/spokes/time_spoke.py b/pyanaconda/ui/tui/spokes/time_spoke.py
index a6e59e3..b1c74d8 100644
--- a/pyanaconda/ui/tui/spokes/time_spoke.py
+++ b/pyanaconda/ui/tui/spokes/time_spoke.py
@@ -24,7 +24,7 @@ from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget
 from pyanaconda.ui.common import FirstbootSpokeMixIn
 from pyanaconda import timezone
-from pyanaconda.i18n import N_, _
+from pyanaconda.i18n import N_, _, C_
 from pyanaconda.constants_text import INPUT_PROCESSED
 
 class TimeZoneSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
@@ -110,7 +110,8 @@ class TimeZoneSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
                 else:
                     self.app.switch_screen(self, self._regions[index])
                 return INPUT_PROCESSED
-            elif key.lower() == "b":
+            # TRANSLATORS: 'b' to go back
+            elif key.lower() == C_('TUI|Spoke Navigation|Time Settings', 'b'):
                 self.app.switch_screen(self, None)
                 return INPUT_PROCESSED
             else:
@@ -131,7 +132,12 @@ class TimeZoneSpoke(FirstbootSpokeMixIn, NormalTUISpoke):
             return INPUT_PROCESSED
 
     def prompt(self, args = None):
-        return _("Please select the timezone.\nUse numbers or type names directly [b to region list, q to quit]: ")
+        # TRANSLATORS: 'b' to go back
+        back = C_('TUI|Spoke Navigation|Time Settings', 'b'),
+        # TRANSLATORS:'q' to quit
+        quit = C_('TUI|Spoke Navigation|Time Settings', 'q')
+
+        return _("Please select the timezone.\nUse numbers or type names directly [%s to region list, %s to quit]: ") % (back, quit)
 
     def apply(self):
         self.data.timezone.timezone = self._selection
diff --git a/pyanaconda/ui/tui/tuiobject.py b/pyanaconda/ui/tui/tuiobject.py
index 9fbb726..7ff00ee 100644
--- a/pyanaconda/ui/tui/tuiobject.py
+++ b/pyanaconda/ui/tui/tuiobject.py
@@ -19,7 +19,7 @@
 # Red Hat Author(s): Martin Sivak <msivak at redhat.com>
 #
 
-from pyanaconda.i18n import N_, _
+from pyanaconda.i18n import N_, _, C_
 from pyanaconda.ui import common
 from pyanaconda.ui.tui import simpleline as tui
 from pyanaconda.constants_text import INPUT_PROCESSED
@@ -128,15 +128,22 @@ class YesNoDialog(tui.UIScreen):
         return True
 
     def prompt(self, args = None):
-        return _("Please respond 'yes' or 'no': ")
+        return _("Please respond '%(yes)s' or '%(no)s': ") % {
+            # TRANSLATORS: 'yes' as positive reply
+            "yes": C_('TUI|Spoke Navigation', 'yes'),
+            # TRANSLATORS: 'no' as negative reply
+            "no": C_('TUI|Spoke Navigation', 'no')
+        }
 
     def input(self, args, key):
-        if key == _("yes"):
+        # TRANSLATORS: 'yes' as positive reply
+        if key == C_('TUI|Spoke Navigation', 'yes'):
             self._response = True
             self.close()
             return None
 
-        elif key == _("no"):
+        # TRANSLATORS: 'no' as negative reply
+        elif key == C_('TUI|Spoke Navigation', 'no'):
             self._response = False
             self.close()
             return None
-- 
2.4.3



More information about the anaconda-patches mailing list