These patches do mainly three things:
1) Rework the Welcome spoke and Langsupport spoke to work with all locales available instead of few locales we think are best for languages we have translations for.
2) Remove the cryptic checkbutton for setting only the language-default keyboard layout instead of a combination with the 'us' layout. Having the layout indicator now, we may set the language-default layout as the only one and switch to it from the default 'us' layout (active on the Welcome spoke) automatically. Exceptions are languages the default layout of which don't support typing ASCII characters. For those layouts we should append 'us' to the list of activated layouts and set up layout switching automatically.
3) Get rid of the expand_langs() function and it's usage resulting in highly-probably nondeterministic results.
I've recorded a video preview [1] demonstrating the new look and behaviour.
[1] http://vpodzime.fedorapeople.org/locales_changes.webm
Altough there are many lines of code added by those patches, the overall user experience should be much better and they resolve a lot of bugs and complaints. And I want to come up with a patch dealing with a lot of code duplicated in Welcome and Langsupport spokes' code that should result in many lines removed. However, I'd like to see those 7 patches landing in the Fedora 20 asap because it is quite a significant change for users.
Vratislav Podzimek (7): Remove an unused argument of get_available_translations Allow seting up locale without modifying ksdata Remove the cryptic "language-default keyboard" checkbutton Improve import in GUI utils a bit Rework the Welcome spoke to allow users choose from all locales Rework the Langsupport spoke to work with all locales Get rid of the non-deterministic expand_langs and its usage
pyanaconda/localization.py | 156 ++++++++----- pyanaconda/packaging/yumpayload.py | 9 +- pyanaconda/ui/gui/hubs/progress.py | 53 +++-- pyanaconda/ui/gui/spokes/langsupport.glade | 167 ++++++++++---- pyanaconda/ui/gui/spokes/langsupport.py | 244 +++++++++++--------- pyanaconda/ui/gui/spokes/welcome.glade | 336 ++++++++++++++-------------- pyanaconda/ui/gui/spokes/welcome.py | 192 +++++++++------- pyanaconda/ui/gui/utils.py | 45 +++- tests/pyanaconda_tests/localization_test.py | 61 ++++- 9 files changed, 772 insertions(+), 491 deletions(-)
Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- pyanaconda/localization.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py index 1997ac0..31ce534 100644 --- a/pyanaconda/localization.py +++ b/pyanaconda/localization.py @@ -210,19 +210,17 @@ def get_native_name(locale):
return _upcase_first_letter(name)
-def get_available_translations(domain=None, localedir=None): +def get_available_translations(localedir=None): """ Method that generates (i.e. returns a generator) available translations for the given domain and localedir.
- :type domain: str :type localedir: str :return: generator yielding available translations :rtype: generator yielding strings
"""
- domain = domain or gettext._current_domain localedir = localedir or gettext._default_localedir
messagefiles = sorted(glob.glob(localedir + "/*/LC_MESSAGES/anaconda.mo"))
This is useful for switching between locales on the welcome screen to make retranslate methods work and not modify ksdata with temporary values.
Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- pyanaconda/localization.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py index 31ce534..0ce10c5 100644 --- a/pyanaconda/localization.py +++ b/pyanaconda/localization.py @@ -147,20 +147,23 @@ def is_supported_locale(locale): en_name = get_english_name(locale) return bool(en_name)
-def setup_locale(locale, lang): +def setup_locale(locale, lang=None): """ Procedure setting the system to use the given locale and store it in to the - ksdata.lang object. DOES NOT PERFORM ANY CHECKS OF THE GIVEN LOCALE. + ksdata.lang object (if given). DOES NOT PERFORM ANY CHECKS OF THE GIVEN + LOCALE.
:param locale: locale to setup :type locale: str - :param lang: ksdata.lang object + :param lang: ksdata.lang object or None :return: None :rtype: None
"""
- lang.lang = locale + if lang: + lang.lang = locale + os.environ["LANG"] = locale
def get_english_name(locale):
People have problems to understand what that checkbutton does and expect the language-default layout to be set up and activated by default. Now, that we have the layout indicator, it should be okay to do so as users can see the change of the current layout. For language-default layouts that do not support typing ASCII characters, we should append 'us' and activate layout switching.
Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- pyanaconda/ui/gui/spokes/welcome.glade | 17 ----------------- pyanaconda/ui/gui/spokes/welcome.py | 14 ++++++++------ 2 files changed, 8 insertions(+), 23 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/welcome.glade b/pyanaconda/ui/gui/spokes/welcome.glade index 67bb449..7622035 100644 --- a/pyanaconda/ui/gui/spokes/welcome.glade +++ b/pyanaconda/ui/gui/spokes/welcome.glade @@ -511,23 +511,6 @@ <property name="position">2</property> </packing> </child> - <child> - <object class="GtkCheckButton" id="setKeyboardCheckButton"> - <property name="label" translatable="yes">Set _keyboard to default layout for selected language.</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="xalign">0</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">3</property> - </packing> - </child> </object> </child> </object> diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index b2fc293..5fab330 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -21,6 +21,7 @@
import sys import re +import langtable
from gi.repository import Gtk, Pango from pyanaconda.ui.gui.hubs.summary import SummaryHub @@ -109,15 +110,13 @@ class WelcomeLanguageSpoke(StandaloneSpoke): # take the first locale (with highest rank) from the list and # store it normalized new_layouts = [keyboard.normalize_layout_variant(layouts[0])] + if not langtable.supports_ascii(layouts[0]): + # does not support typing ASCII chars, append the 'us' layout + new_layouts.append("us") else: + log.error("Failed to get layout for chosen locale '%s'" % locale) new_layouts = ["us"]
- checkbutton = self.builder.get_object("setKeyboardCheckButton") - if not checkbutton.get_active() and "us" not in new_layouts: - #user doesn't want only the language-default layout, prepend - #'English (US)' layout - new_layouts.insert(0, "us") - self.data.keyboard.x_layouts = new_layouts if flags.can_touch_runtime_system("replace runtime X layouts"): self._xklwrapper.replace_layouts(new_layouts) @@ -128,6 +127,9 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
if flags.can_touch_runtime_system("init layout switching"): self._xklwrapper.set_switching_options(["grp:alt_shift_toggle"]) + # activate the first (language-default) layout instead of the + # 'us' one + self._xklwrapper.activate_default_layout()
@property def completed(self):
Import globally what can be imported globally.
Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- pyanaconda/ui/gui/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/pyanaconda/ui/gui/utils.py b/pyanaconda/ui/gui/utils.py index c630a16..b1dfb06 100644 --- a/pyanaconda/ui/gui/utils.py +++ b/pyanaconda/ui/gui/utils.py @@ -24,7 +24,7 @@ from pyanaconda.threads import threadMgr
from contextlib import contextmanager -from gi.repository import Gtk, GLib +from gi.repository import Gtk, GLib, AnacondaWidgets import Queue
def gtk_call_once(func, *args): @@ -94,8 +94,9 @@ def gtk_action_nowait(func):
@contextmanager def enlightbox(mainWindow, dialog): + # importing globally would cause a circular dependency from pyanaconda.ui.gui import ANACONDA_WINDOW_GROUP - from gi.repository import AnacondaWidgets + lightbox = AnacondaWidgets.lb_show_over(mainWindow) ANACONDA_WINDOW_GROUP.add_window(lightbox) dialog.set_transient_for(lightbox)
What we actually need from users is a locale, value that can be set in the $LANG environment variable. However, what we get from the files with translations are usually languages (en, cs, es, de, ...). Instead of picking up one locale for each such language, we may offer user all locales for all languages we have translations for and let them choose the exact locale they want to use. By setting it as the value of the $LANG variable, we then get the right translations.
Resolves: rhbz#951879 Related: rhbz#986155
Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- pyanaconda/localization.py | 16 +- pyanaconda/ui/gui/spokes/welcome.glade | 319 +++++++++++++++++---------------- pyanaconda/ui/gui/spokes/welcome.py | 178 ++++++++++-------- pyanaconda/ui/gui/utils.py | 40 +++++ 4 files changed, 314 insertions(+), 239 deletions(-)
diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py index 0ce10c5..b0e8a1b 100644 --- a/pyanaconda/localization.py +++ b/pyanaconda/localization.py @@ -216,34 +216,34 @@ def get_native_name(locale): def get_available_translations(localedir=None): """ Method that generates (i.e. returns a generator) available translations for - the given domain and localedir. + the installer in the given localedir.
:type localedir: str - :return: generator yielding available translations + :return: generator yielding available translations (languages) :rtype: generator yielding strings
"""
localedir = localedir or gettext._default_localedir
- messagefiles = sorted(glob.glob(localedir + "/*/LC_MESSAGES/anaconda.mo")) + # usually there are no message files for en + messagefiles = sorted(glob.glob(localedir + "/*/LC_MESSAGES/anaconda.mo") + + ["blob/en/blob/blob"]) trans_gen = (path.split(os.path.sep)[-3] for path in messagefiles)
- # usually there are no message files for en - langs = {"en"} - yield "en_US.UTF-8" + langs = set()
for trans in trans_gen: parts = parse_langcode(trans) lang = parts.get("language", "") if lang and lang not in langs: langs.add(lang) + # check if there are any locales for the language locales = get_language_locales(lang) if not locales: continue
- # take the first locale (with highest rank) for the language - yield locales[0] + yield lang
def get_language_locales(lang): """ diff --git a/pyanaconda/ui/gui/spokes/welcome.glade b/pyanaconda/ui/gui/spokes/welcome.glade index 7622035..da6d7ae 100644 --- a/pyanaconda/ui/gui/spokes/welcome.glade +++ b/pyanaconda/ui/gui/spokes/welcome.glade @@ -126,6 +126,29 @@ <action-widget response="1">uhdContinueButton</action-widget> </action-widgets> </object> + <object class="GtkListStore" id="languageStore"> + <columns> + <!-- column-name nativeName --> + <column type="gchararray"/> + <!-- column-name englishName --> + <column type="gchararray"/> + <!-- column-name langSetting --> + <column type="gchararray"/> + <!-- column-name separator --> + <column type="gboolean"/> + </columns> + </object> + <object class="GtkListStore" id="localeStore"> + <columns> + <!-- column-name nativeName --> + <column type="gchararray"/> + <!-- column-name locale --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkTreeModelFilter" id="languageStoreFilter"> + <property name="child_model">languageStore</property> + </object> <object class="GtkDialog" id="betaWarnDialog"> <property name="can_focus">False</property> <property name="border_width">6</property> @@ -250,47 +273,26 @@ <action-widget response="1">continueButton</action-widget> </action-widgets> </object> - <object class="AnacondaSpokeWindow" id="languageSpokeWindow"> + <object class="AnacondaStandaloneWindow" id="welcomeWindow"> <property name="startup_id">filler</property> <property name="can_focus">False</property> <property name="startup_id">filler</property> - <signal name="button-clicked" handler="on_back_clicked" swapped="no"/> + <property name="mnemonics_visible">False</property> + <property name="focus_visible">False</property> + <property name="window_name"></property> <child internal-child="main_box"> - <object class="GtkBox" id="AnacondaSpokeWindow-main_box1"> + <object class="GtkBox" id="AnacondaStandaloneWindow-main_box1"> <property name="can_focus">False</property> <property name="orientation">vertical</property> - <property name="spacing">6</property> <child internal-child="nav_box"> - <object class="GtkEventBox" id="AnacondaSpokeWindow-nav_box1"> - <child internal-child="nav_area"> - <object class="GtkGrid" id="AnacondaSpokeWindow-nav_area1"> - <property name="can_focus">False</property> - <property name="margin_left">6</property> - <property name="margin_right">6</property> - <property name="margin_top">6</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child internal-child="alignment"> - <object class="GtkAlignment" id="AnacondaSpokeWindow-alignment1"> + <object class="GtkEventBox" id="AnacondaStandaloneWindow-nav_box1"> <property name="can_focus">False</property> - <property name="yalign">0</property> - <property name="xscale">1</property> - <property name="yscale">1</property> - <property name="bottom_padding">48</property> - <property name="left_padding">24</property> - <property name="right_padding">24</property> - <child internal-child="action_area"> - <object class="GtkBox" id="languageSpokeWindowContentBox"> + <child internal-child="nav_area"> + <object class="GtkGrid" id="AnacondaStandaloneWindow-nav_area1"> <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> + <child> + <placeholder/> + </child> <child> <placeholder/> </child> @@ -298,68 +300,17 @@ </child> </object> <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> </packing> </child> - <child> - <placeholder/> - </child> - </object> - </child> - </object> - <object class="GtkListStore" id="languageStore"> - <columns> - <!-- column-name nativeName --> - <column type="gchararray"/> - <!-- column-name englishName --> - <column type="gchararray"/> - <!-- column-name langSetting --> - <column type="gchararray"/> - <!-- column-name separator --> - <column type="gboolean"/> - </columns> - </object> - <object class="GtkTreeModelFilter" id="languageStoreFilter"> - <property name="child_model">languageStore</property> - </object> - <object class="AnacondaStandaloneWindow" id="welcomeWindow"> - <property name="startup_id">filler</property> - <property name="can_focus">False</property> - <property name="startup_id">filler</property> - <property name="mnemonics_visible">False</property> - <property name="focus_visible">False</property> - <property name="window_name"/> - <child internal-child="main_box"> - <object class="GtkBox" id="AnacondaStandaloneWindow-main_box1"> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <child internal-child="nav_box"> - <object class="GtkEventBox" id="AnacondaStandaloneWindow-nav_box1"> - <child internal-child="nav_area"> - <object class="GtkGrid" id="AnacondaStandaloneWindow-nav_area1"> - <property name="can_focus">False</property> - <child> - <placeholder/> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> <child internal-child="alignment"> <object class="GtkAlignment" id="AnacondaStandaloneWindow-alignment1"> <property name="can_focus">False</property> <property name="hexpand">True</property> <property name="vexpand">True</property> <property name="yalign">0</property> - <property name="xscale">1</property> - <property name="yscale">1</property> <property name="bottom_padding">48</property> <property name="left_padding">24</property> <property name="right_padding">24</property> @@ -387,7 +338,7 @@ </child> <child> <object class="GtkLabel" id="pickLanguageLabel"> - <property name="height_request">45</property> + <property name="height_request">45</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="valign">start</property> @@ -404,109 +355,165 @@ </packing> </child> <child> - <object class="GtkAlignment" id="languageAlignment"> + <object class="GtkGrid" id="mainGrid"> <property name="visible">True</property> <property name="can_focus">False</property> + <property name="halign">center</property> <property name="hexpand">True</property> <property name="vexpand">True</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> <child> - <object class="GtkBox" id="box1"> + <object class="GtkScrolledWindow" id="languageWindow"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can_focus">True</property> <property name="hexpand">True</property> <property name="vexpand">True</property> - <property name="orientation">vertical</property> + <property name="hscrollbar_policy">never</property> + <property name="shadow_type">in</property> <child> - <object class="GtkScrolledWindow" id="languageWindow"> + <object class="GtkTreeView" id="languageView"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="has_focus">True</property> <property name="hexpand">True</property> <property name="vexpand">True</property> - <property name="hscrollbar_policy">never</property> - <property name="shadow_type">in</property> + <property name="model">languageStoreFilter</property> + <property name="headers_visible">False</property> + <property name="enable_search">False</property> + <property name="search_column">0</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="languageViewSelection"> + <signal name="changed" handler="on_lang_selection_changed" swapped="no"/> + </object> + </child> <child> - <object class="GtkTreeView" id="languageView"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <property name="model">languageStoreFilter</property> - <property name="headers_visible">False</property> - <property name="enable_search">False</property> - <property name="search_column">0</property> - <child internal-child="selection"> - <object class="GtkTreeSelection" id="languageViewSelection"> - <signal name="changed" handler="on_selection_changed" swapped="no"/> + <object class="GtkTreeViewColumn" id="nativeName"> + <property name="title" translatable="yes">nativeName</property> + <property name="expand">True</property> + <property name="clickable">True</property> + <property name="sort_column_id">0</property> + <child> + <object class="GtkCellRendererText" id="nativeNameRenderer"> + <property name="xalign">0.89999997615814209</property> + <property name="font">Cantarell 12</property> </object> + <attributes> + <attribute name="markup">0</attribute> + </attributes> </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="englishName"> + <property name="title" translatable="yes">englishName</property> + <property name="expand">True</property> + <property name="clickable">True</property> + <property name="sort_column_id">1</property> <child> - <object class="GtkTreeViewColumn" id="nativeName"> - <property name="title" translatable="yes">nativeName</property> - <property name="expand">True</property> - <property name="clickable">True</property> - <property name="sort_column_id">0</property> - <child> - <object class="GtkCellRendererText" id="nativeNameRenderer"> - <property name="font">Cantarell 12</property> - </object> - <attributes> - <attribute name="markup">0</attribute> - </attributes> - </child> + <object class="GtkCellRendererText" id="englishNameRenderer"> + <property name="xalign">0.10000000149011612</property> + <property name="font">Cantarell Italic 14</property> + <property name="foreground">gray</property> </object> + <attributes> + <attribute name="text">1</attribute> + </attributes> </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="langSelectedColumn"> + <property name="title" translatable="yes">selected</property> <child> - <object class="GtkTreeViewColumn" id="englishName"> - <property name="title" translatable="yes">englishName</property> - <property name="expand">True</property> - <property name="clickable">True</property> - <property name="sort_column_id">1</property> - <child> - <object class="GtkCellRendererText" id="englishNameRenderer"> - <property name="font">Cantarell Italic 14</property> - <property name="foreground">gray</property> - </object> - <attributes> - <attribute name="text">1</attribute> - </attributes> - </child> - </object> + <object class="GtkCellRendererPixbuf" id="langSelectedRenderer"/> </child> </object> </child> </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="languageEntry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="valign">start</property> + <property name="invisible_char">●</property> + <property name="invisible_char_set">True</property> + <property name="secondary_icon_name">edit-clear-symbolic</property> + <property name="placeholder_text">Type here to search.</property> + <signal name="changed" handler="on_entry_changed" swapped="no"/> + <signal name="icon-release" handler="on_clear_icon_clicked" swapped="no"/> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="localeWindow"> + <property name="height_request">150</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="hscrollbar_policy">never</property> + <property name="shadow_type">in</property> <child> - <object class="GtkEntry" id="languageEntry"> + <object class="GtkTreeView" id="localeView"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="valign">start</property> - <property name="margin_top">6</property> - <property name="invisible_char">●</property> - <property name="invisible_char_set">True</property> - <property name="secondary_icon_name">edit-clear-symbolic</property> - <property name="placeholder_text" translatable="yes">Type here to search.</property> - <signal name="changed" handler="on_entry_changed" swapped="no"/> - <signal name="icon-release" handler="on_clear_icon_clicked" swapped="no"/> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">localeStore</property> + <property name="headers_visible">False</property> + <property name="enable_search">False</property> + <property name="search_column">0</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="localeViewSelection"> + <signal name="changed" handler="on_locale_selection_changed" swapped="no"/> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="nativeName1"> + <property name="title" translatable="yes">nativeName</property> + <property name="expand">True</property> + <property name="clickable">True</property> + <property name="sort_column_id">0</property> + <child> + <object class="GtkCellRendererText" id="nativeNameRenderer1"> + <property name="font">Cantarell 12</property> + </object> + <attributes> + <attribute name="markup">0</attribute> + </attributes> + </child> + </object> + </child> </object> - - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> </child> </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <placeholder/> </child> </object> <packing> - <property name="expand">True</property> + <property name="expand">False</property> <property name="fill">True</property> <property name="position">2</property> </packing> diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 5fab330..b3aec82 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -17,6 +17,7 @@ # Red Hat, Inc. # # Red Hat Author(s): Chris Lumens clumens@redhat.com +# Vratislav Podzimek vpodzime@redhat.com #
import sys @@ -26,7 +27,7 @@ import langtable from gi.repository import Gtk, Pango from pyanaconda.ui.gui.hubs.summary import SummaryHub from pyanaconda.ui.gui.spokes import StandaloneSpoke -from pyanaconda.ui.gui.utils import enlightbox +from pyanaconda.ui.gui.utils import enlightbox, set_treeview_selection
from pyanaconda import localization from pyanaconda.product import distributionText, isFinal, productName, productVersion @@ -35,6 +36,7 @@ from pyanaconda import flags from pyanaconda import geoloc from pyanaconda.i18n import _ from pyanaconda.iutil import is_unsupported_hw, strip_accents +from pyanaconda.constants import DEFAULT_LANG
import logging log = logging.getLogger("anaconda") @@ -44,7 +46,8 @@ __all__ = ["WelcomeLanguageSpoke"] class WelcomeLanguageSpoke(StandaloneSpoke): mainWidgetName = "welcomeWindow" uiFile = "spokes/welcome.glade" - builderObjects = ["languageStore", "languageStoreFilter", "welcomeWindow", "betaWarnDialog", "unsupportedHardwareDialog"] + builderObjects = ["languageStore", "languageStoreFilter", "localeStore", + "welcomeWindow", "betaWarnDialog"]
preForHub = SummaryHub priority = 0 @@ -55,10 +58,9 @@ class WelcomeLanguageSpoke(StandaloneSpoke): self._origStrings = {}
def apply(self): - selected = self.builder.get_object("languageViewSelection") - (store, itr) = selected.get_selected() + (store, itr) = self._localeSelection.get_selected()
- locale = store[itr][2] + locale = store[itr][1] localization.setup_locale(locale, self.data.lang)
# Skip timezone and keyboard default setting for kickstart installs. @@ -141,15 +143,33 @@ class WelcomeLanguageSpoke(StandaloneSpoke): def _row_is_separator(self, model, itr, *args): return model[itr][3]
+ def _render_lang_selected(self, column, renderer, model, itr, user_data=None): + (lang_store, sel_itr) = self._langSelection.get_selected() + + if sel_itr and lang_store[sel_itr][2] == model[itr][2]: + renderer.set_property("pixbuf", self._right_arrow.get_pixbuf()) + else: + renderer.set_property("pixbuf", None) + def initialize(self): - store = self.builder.get_object("languageStore") + self._languageStore = self.builder.get_object("languageStore") self._languageStoreFilter = self.builder.get_object("languageStoreFilter") self._languageEntry = self.builder.get_object("languageEntry") - self._selection = self.builder.get_object("languageViewSelection") - self._view = self.builder.get_object("languageView") + self._langSelection = self.builder.get_object("languageViewSelection") + self._langSelectedRenderer = self.builder.get_object("langSelectedRenderer") + self._langSelectedColumn = self.builder.get_object("langSelectedColumn"); + self._langView = self.builder.get_object("languageView") + self._localeView = self.builder.get_object("localeView") + self._localeStore = self.builder.get_object("localeStore") + self._localeSelection = self.builder.get_object("localeViewSelection")
# We need to tell the view whether something is a separator or not. - self._view.set_row_separator_func(self._row_is_separator, None) + self._langView.set_row_separator_func(self._row_is_separator, None) + + # Render a right arrow for the chosen language + self._right_arrow = Gtk.Image.new_from_file("/usr/share/anaconda/pixmaps/right-arrow-icon.png") + self._langSelectedColumn.set_cell_data_func(self._langSelectedRenderer, + self._render_lang_selected)
# We can use the territory from geolocation here # to preselect the translation, when it's available. @@ -162,22 +182,28 @@ class WelcomeLanguageSpoke(StandaloneSpoke): localization.setup_locale(locales[0], self.data.lang)
# fill the list with available translations - for locale in localization.get_available_translations(): - self._addLanguage(store, localization.get_native_name(locale), - localization.get_english_name(locale), locale) + for lang in localization.get_available_translations(): + self._addLanguage(self._languageStore, + localization.get_native_name(lang), + localization.get_english_name(lang), lang)
# Move the default language (whatever was provided on the command line, # or by kickstart, or by geoip, or English if nothing else) to the top # of the list and select it by default. People find it confusing to be # dropped into the middle of a scrollable list. - (store, itr) = self._selection.get_selected() - if not itr: - itr = self._selectLanguage(self.data.lang.lang) + lang_itr, locale_itr = self._select_locale(self.data.lang.lang)
- # store is the filtered store, and itr is an iter on it. We need to + if not lang_itr or not locale_itr: + log.error("Failed to select language %s, using the default %s", + self.data.lang.lang, DEFAULT_LANG) + lang_itr, locale_itr = self._select_locale(DEFAULT_LANG) + self.data.lang.lang = DEFAULT_LANG + + filter_store = self._languageStoreFilter + # filtered store and lang_itr is an iter on it. We need to # convert to an iter on the underlying store. - itr = store.convert_iter_to_child_iter(itr) - store = store.get_model() + itr = filter_store.convert_iter_to_child_iter(lang_itr) + store = filter_store.get_model() store.move_after(itr, None)
# And then we add a separator after the default chosen language. @@ -201,7 +227,7 @@ class WelcomeLanguageSpoke(StandaloneSpoke): # Change the translations on labels and buttons that do not have # substitution text. for name in ["pickLanguageLabel", "betaWarnTitle", "betaWarnDesc", - "quitButton", "continueButton", "setKeyboardCheckButton"]: + "quitButton", "continueButton"]: self._retranslate_one(name)
# The welcome label is special - it has text that needs to be @@ -221,28 +247,17 @@ class WelcomeLanguageSpoke(StandaloneSpoke): self.window.retranslate(lang)
def refresh(self): - self._selectLanguage(self.data.lang.lang) - - # Rip the label and language selection window - # from where it is right now and add it to this - # spoke. - # This way we can share the dialog and code - # between Welcome and Language spokes - langLabel = self.builder.get_object("pickLanguageLabel") - langLabel.get_parent().remove(langLabel) - langAlign = self.builder.get_object("languageAlignment") - langAlign.get_parent().remove(langAlign) - - content = self.builder.get_object("welcomeWindowContentBox") - content.pack_start(child = langLabel, fill = True, expand = False, padding = 0) - content.pack_start(child = langAlign, fill = True, expand = True, padding = 0) - + self._select_locale(self.data.lang.lang) self._languageEntry.set_text("") self._languageStoreFilter.refilter()
- def _addLanguage(self, store, native, english, setting): - lang = '<span lang="%s">%s</span>' % (re.sub(r'..*', '', setting), native) - store.append([lang, english, setting, False]) + def _addLanguage(self, store, native, english, lang): + native_span = '<span lang="%s">%s</span>' % (lang, native) + store.append([native_span, english, lang, False]) + + def _addLocale(self, store, native, locale): + native_span = '<span lang="%s">%s</span>' % (re.sub(r'..*', '', locale), native) + store.append([native_span, locale])
def _matchesEntry(self, model, itr, *args): # Need to strip out the pango markup before attempting to match. @@ -266,51 +281,64 @@ class WelcomeLanguageSpoke(StandaloneSpoke): else: return False
- def _selectLanguage(self, language): - treeview = self.builder.get_object("languageView") - selection = treeview.get_selection() - store = treeview.get_model() - itr = store.get_iter_first() - # store[itr][3] is True if this row is a separator in the view, so we - # want to skip those. - while itr and not store[itr][3] \ - and language not in localization.expand_langs(store[itr][2]): - itr = store.iter_next(itr) - - # If we were provided with an unsupported language, just use the default. - if not itr: - return + def _select_locale(self, locale): + """ + Try to select the given locale in the language and locale + treeviews. This method tries to find the best match for the given + locale. + + :return: a pair of selected iterators (language and locale) + :rtype: a pair of GtkTreeIter or None objects + + """ + + # get lang and select it + parts = localization.parse_langcode(locale) + if "language" not in parts: + # invalid locale, cannot select + return (None, None)
- selection.select_iter(itr) - path = store.get_path(itr) - # row_align=0.5 tells GTK to move the cell to the middle of the - # treeview viewport (0.0 should align it with the top, 1.0 with bottom) - # If the cell is the uppermost one, it should align it with the top - # of the viewport. - # - # Unfortunately, this does not work as expected due to a bug in GTK. - # So currently if the cell is the upper most one, it will sort of - # align it with the top of the viewport, leaving about 30% of - # it hidden. If the target cell is not the upper most one, - # it seems to just scroll the treeview a bit, leaving the - # target cell well out of the viewport. - # - # In short, once that GTK bug is fixed, row_align=0.5 should work - # correctly for showing both upper most cells and all other cells - # in the tree view. - treeview.scroll_to_cell(path, use_align=True, row_align=0.5) - - return itr + lang_itr = set_treeview_selection(self._langView, parts["language"], col=2) + + # find matches and use the one with the highest rank + locales = localization.get_language_locales(locale) + locale_itr = set_treeview_selection(self._localeView, locales[0], col=1) + + return (lang_itr, locale_itr) + + def _refresh_locale_store(self, lang): + """Refresh the localeStore with locales for the given language.""" + + self._localeStore.clear() + locales = localization.get_language_locales(lang) + for locale in locales: + self._addLocale(self._localeStore, + localization.get_native_name(locale), + locale) + + # select the first locale (with the highest rank) + set_treeview_selection(self._localeView, locales[0], col=1)
# Signal handlers. - def on_selection_changed(self, selection): + def on_lang_selection_changed(self, selection): + (store, selected) = selection.get_selected_rows() + + if selected: + lang = store[selected[0]][2] + self._refresh_locale_store(lang) + else: + if hasattr(self.window, "set_may_continue"): + self.window.set_may_continue(False) + self._localeStore.clear() + + def on_locale_selection_changed(self, selection): (store, selected) = selection.get_selected_rows() if hasattr(self.window, "set_may_continue"): self.window.set_may_continue(len(selected) > 0)
if selected: - lang = store[selected[0]][2] - localization.setup_locale(lang, self.data.lang) + lang = store[selected[0]][1] + localization.setup_locale(lang) self.retranslate(lang)
def on_clear_icon_clicked(self, entry, icon_pos, event): diff --git a/pyanaconda/ui/gui/utils.py b/pyanaconda/ui/gui/utils.py index b1dfb06..2f5f9c1 100644 --- a/pyanaconda/ui/gui/utils.py +++ b/pyanaconda/ui/gui/utils.py @@ -138,3 +138,43 @@ def fancy_set_sensitive(widget, value): widget.set_sensitive(value) for w in widget.list_mnemonic_labels(): w.set_sensitive(value) + +def set_treeview_selection(treeview, item, col=0): + """ + Select the given item in the given treeview and scroll to it. + + :param treeview: treeview to select and item in + :type treeview: GtkTreeView + :param item: item to be selected + :type item: str + :param col: column to search for the item in + :type col: int + :return: selected iterator or None if item was not found + :rtype: GtkTreeIter or None + + """ + + model = treeview.get_model() + itr = model.get_iter_first() + while itr and not model[itr][col] == item: + itr = model.iter_next(itr) + + if not itr: + # item not found, cannot be selected + return None + + # otherwise select the item and scroll to it + selection = treeview.get_selection() + selection.select_iter(itr) + path = model.get_path(itr) + + # row_align=0.5 tells GTK to move the cell to the middle of the + # treeview viewport (0.0 should align it with the top, 1.0 with bottom) + # If the cell is the uppermost one, it should align it with the top + # of the viewport. + # + # Unfortunately, this does not work as expected due to a bug in GTK. + # (see rhbz#970048) + treeview.scroll_to_cell(path, use_align=True, row_align=0.5) + + return itr
On 08/30/2013 11:15 AM, Vratislav Podzimek wrote:
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 5fab330..b3aec82 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -17,6 +17,7 @@ # Red Hat, Inc. # # Red Hat Author(s): Chris Lumens clumens@redhat.com +# Vratislav Podzimek vpodzime@redhat.com #
import sys @@ -26,7 +27,7 @@ import langtable from gi.repository import Gtk, Pango from pyanaconda.ui.gui.hubs.summary import SummaryHub from pyanaconda.ui.gui.spokes import StandaloneSpoke -from pyanaconda.ui.gui.utils import enlightbox +from pyanaconda.ui.gui.utils import enlightbox, set_treeview_selection
from pyanaconda import localization from pyanaconda.product import distributionText, isFinal, productName, productVersion @@ -35,6 +36,7 @@ from pyanaconda import flags from pyanaconda import geoloc from pyanaconda.i18n import _ from pyanaconda.iutil import is_unsupported_hw, strip_accents +from pyanaconda.constants import DEFAULT_LANG
import logging log = logging.getLogger("anaconda") @@ -44,7 +46,8 @@ __all__ = ["WelcomeLanguageSpoke"] class WelcomeLanguageSpoke(StandaloneSpoke): mainWidgetName = "welcomeWindow" uiFile = "spokes/welcome.glade"
- builderObjects = ["languageStore", "languageStoreFilter", "welcomeWindow", "betaWarnDialog", "unsupportedHardwareDialog"]
- builderObjects = ["languageStore", "languageStoreFilter", "localeStore",
"welcomeWindow", "betaWarnDialog"]
I guess we don't want to remove unsupportedHardwareDialog from builderObjects?
On Tue, 2013-09-03 at 14:35 +0200, Radek Vykydal wrote:
On 08/30/2013 11:15 AM, Vratislav Podzimek wrote:
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 5fab330..b3aec82 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -17,6 +17,7 @@ # Red Hat, Inc. # # Red Hat Author(s): Chris Lumens clumens@redhat.com +# Vratislav Podzimek vpodzime@redhat.com #
import sys @@ -26,7 +27,7 @@ import langtable from gi.repository import Gtk, Pango from pyanaconda.ui.gui.hubs.summary import SummaryHub from pyanaconda.ui.gui.spokes import StandaloneSpoke -from pyanaconda.ui.gui.utils import enlightbox +from pyanaconda.ui.gui.utils import enlightbox, set_treeview_selection
from pyanaconda import localization from pyanaconda.product import distributionText, isFinal, productName, productVersion @@ -35,6 +36,7 @@ from pyanaconda import flags from pyanaconda import geoloc from pyanaconda.i18n import _ from pyanaconda.iutil import is_unsupported_hw, strip_accents +from pyanaconda.constants import DEFAULT_LANG
import logging log = logging.getLogger("anaconda") @@ -44,7 +46,8 @@ __all__ = ["WelcomeLanguageSpoke"] class WelcomeLanguageSpoke(StandaloneSpoke): mainWidgetName = "welcomeWindow" uiFile = "spokes/welcome.glade"
- builderObjects = ["languageStore", "languageStoreFilter", "welcomeWindow", "betaWarnDialog", "unsupportedHardwareDialog"]
- builderObjects = ["languageStore", "languageStoreFilter", "localeStore",
"welcomeWindow", "betaWarnDialog"]I guess we don't want to remove unsupportedHardwareDialog from builderObjects?
Good catch, thanks! Fixing locally.
This is a follow up for the Welcome spoke reworking.
Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- pyanaconda/ui/gui/spokes/langsupport.glade | 167 ++++++++++++++------ pyanaconda/ui/gui/spokes/langsupport.py | 244 +++++++++++++++++------------ 2 files changed, 264 insertions(+), 147 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/langsupport.glade b/pyanaconda/ui/gui/spokes/langsupport.glade index afb5995..e6fbb55 100644 --- a/pyanaconda/ui/gui/spokes/langsupport.glade +++ b/pyanaconda/ui/gui/spokes/langsupport.glade @@ -2,7 +2,7 @@ <interface> <!-- interface-requires gtk+ 3.0 --> <!-- interface-requires AnacondaWidgets 1.0 --> - <object class="GtkListStore" id="langsupportStore"> + <object class="GtkListStore" id="languageStore"> <columns> <!-- column-name nativeName --> <column type="gchararray"/> @@ -10,14 +10,10 @@ <column type="gchararray"/> <!-- column-name langSetting --> <column type="gchararray"/> - <!-- column-name langSupportSelected --> - <column type="gboolean"/> - <!-- column-name isAdditional --> - <column type="gboolean"/> </columns> </object> - <object class="GtkTreeModelFilter" id="langsupportStoreFilter"> - <property name="child_model">langsupportStore</property> + <object class="GtkTreeModelFilter" id="languageStoreFilter"> + <property name="child_model">languageStore</property> </object> <object class="AnacondaSpokeWindow" id="langsupportWindow"> <property name="startup_id">filler</property> @@ -82,54 +78,47 @@ </packing> </child> <child> - <object class="GtkBox" id="box1"> + <object class="GtkGrid" id="mainGrid"> <property name="visible">True</property> <property name="can_focus">False</property> + <property name="halign">center</property> <property name="hexpand">True</property> <property name="vexpand">True</property> - <property name="orientation">vertical</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> <child> - <object class="GtkScrolledWindow" id="langsupportScrolledwindow1"> + <object class="GtkScrolledWindow" id="languageWindow"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="hexpand">True</property> <property name="vexpand">True</property> + <property name="hscrollbar_policy">never</property> <property name="shadow_type">in</property> <child> - <object class="GtkTreeView" id="langsupportView"> + <object class="GtkTreeView" id="languageView"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="hexpand">True</property> <property name="vexpand">True</property> - <property name="model">langsupportStoreFilter</property> + <property name="model">languageStoreFilter</property> <property name="headers_visible">False</property> <property name="headers_clickable">False</property> + <property name="enable_search">False</property> <property name="search_column">0</property> <child internal-child="selection"> - <object class="GtkTreeSelection" id="langsupportViewSelection"/> - </child> - <child> - <object class="GtkTreeViewColumn" id="langsupportSelectedCol"> - <property name="title" translatable="yes">column</property> - <signal name="clicked" handler="on_langsupport_toggled" swapped="no"/> - <child> - <object class="GtkCellRendererToggle" id="langsupportSelectedRenderer"> - <signal name="toggled" handler="on_langsupport_toggled" swapped="no"/> - </object> - <attributes> - <attribute name="sensitive">4</attribute> - <attribute name="activatable">4</attribute> - <attribute name="active">3</attribute> - </attributes> - </child> + <object class="GtkTreeSelection" id="languageViewSelection"> + <signal name="changed" handler="on_lang_selection_changed" swapped="no"/> </object> </child> <child> - <object class="GtkTreeViewColumn" id="nativeNameCol"> - <property name="title" translatable="yes">column</property> + <object class="GtkTreeViewColumn" id="nativeName"> + <property name="title" translatable="yes">nativeName</property> <property name="expand">True</property> + <property name="clickable">True</property> + <property name="sort_column_id">0</property> <child> <object class="GtkCellRendererText" id="nativeNameRenderer"> + <property name="xalign">0.89999997615814209</property> <property name="font">Cantarell 12</property> </object> <attributes> @@ -139,52 +128,126 @@ </object> </child> <child> - <object class="GtkTreeViewColumn" id="englishNameCol"> - <property name="title" translatable="yes">column</property> + <object class="GtkTreeViewColumn" id="englishName"> + <property name="title" translatable="yes">englishName</property> <property name="expand">True</property> + <property name="clickable">True</property> + <property name="sort_column_id">1</property> <child> <object class="GtkCellRendererText" id="englishNameRenderer"> + <property name="xalign">0.10000000149011612</property> <property name="font">Cantarell Italic 14</property> <property name="foreground">gray</property> </object> <attributes> - <attribute name="markup">1</attribute> + <attribute name="text">1</attribute> </attributes> </child> </object> </child> + <child> + <object class="GtkTreeViewColumn" id="langSelectedColumn"> + <property name="title" translatable="yes">selected</property> + <child> + <object class="GtkCellRendererPixbuf" id="langSelectedRenderer"/> + </child> + </object> + </child> </object> </child> </object> <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> </packing> </child> <child> - <object class="GtkEntry" id="langsupportEntry"> + <object class="GtkEntry" id="languageEntry"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="valign">start</property> - <property name="margin_top">6</property> - <property name="hexpand">True</property> <property name="invisible_char">●</property> <property name="invisible_char_set">True</property> <property name="secondary_icon_name">edit-clear-symbolic</property> - <property name="placeholder_text" translatable="yes">Type here to search.</property> - <signal name="changed" handler="on_entry_changed" swapped="no"/> - <signal name="icon-release" handler="on_clear_icon_clicked" swapped="no"/> + <property name="placeholder_text">Type here to search.</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="localeWindow"> + <property name="height_request">150</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="hscrollbar_policy">never</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="localeView"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">localeStore</property> + <property name="headers_visible">False</property> + <property name="enable_search">False</property> + <property name="search_column">0</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="localeViewSelection"/> + </child> + <child> + <object class="GtkTreeViewColumn" id="checked"> + <property name="title" translatable="yes">checked</property> + <child> + <object class="GtkCellRendererToggle" id="checkedRenderer"> + <signal name="toggled" handler="on_locale_toggled" swapped="no"/> + </object> + <attributes> + <attribute name="active">2</attribute> + <attribute name="sensitive">3</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="localeNativeName"> + <property name="title" translatable="yes">nativeName</property> + <property name="expand">True</property> + <property name="clickable">True</property> + <property name="sort_column_id">0</property> + <child> + <object class="GtkCellRendererText" id="localeNativeNameRenderer"> + <property name="font">Cantarell 12</property> + </object> + <attributes> + <attribute name="markup">0</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> </object> <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> </packing> </child> + <child> + <placeholder/> + </child> </object> <packing> - <property name="expand">True</property> + <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> @@ -204,4 +267,16 @@ </object> </child> </object> + <object class="GtkListStore" id="localeStore"> + <columns> + <!-- column-name nativeName --> + <column type="gchararray"/> + <!-- column-name locale --> + <column type="gchararray"/> + <!-- column-name selected --> + <column type="gboolean"/> + <!-- column-name isAdditional --> + <column type="gboolean"/> + </columns> + </object> </interface> diff --git a/pyanaconda/ui/gui/spokes/langsupport.py b/pyanaconda/ui/gui/spokes/langsupport.py index 5f44599..6f75ebc 100644 --- a/pyanaconda/ui/gui/spokes/langsupport.py +++ b/pyanaconda/ui/gui/spokes/langsupport.py @@ -17,6 +17,7 @@ # Red Hat, Inc. # # Red Hat Author(s): Radek Vykydal rvykydal@redhat.com +# Vratislav Podzimek vpodzime@redhat.com #
from gi.repository import Gtk, Pango @@ -25,6 +26,7 @@ from pyanaconda.i18n import N_ from pyanaconda.iutil import strip_accents from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.gui.categories.localization import LocalizationCategory +from pyanaconda.ui.gui.utils import set_treeview_selection from pyanaconda import localization
import re @@ -34,14 +36,8 @@ log = logging.getLogger("anaconda")
__all__ = ["LangsupportSpoke"]
-COL_NATIVE_NAME = 0 -COL_ENGLISH_NAME = 1 -COL_LANG_SETTING = 2 -COL_SELECTED = 3 -COL_IS_ADDITIONAL = 4 - class LangsupportSpoke(NormalSpoke): - builderObjects = ["langsupportStore", "langsupportStoreFilter", "langsupportWindow"] + builderObjects = ["languageStore", "languageStoreFilter", "localeStore", "langsupportWindow"] mainWidgetName = "langsupportWindow" uiFile = "spokes/langsupport.glade"
@@ -52,60 +48,57 @@ class LangsupportSpoke(NormalSpoke):
def __init__(self, *args, **kwargs): NormalSpoke.__init__(self, *args, **kwargs) + self._selected_locales = set()
def initialize(self): - self._langsupportStore = self.builder.get_object("langsupportStore") - self._langsupportEntry = self.builder.get_object("langsupportEntry") - self._langsupportStoreFilter = self.builder.get_object("langsupportStoreFilter") - self._langsupportStoreFilter.set_visible_func(self._matches_entry, None) - - # mark selected items in language list bold - for col, rend, idx in (("englishNameCol", "englishNameRenderer", COL_ENGLISH_NAME), - ("nativeNameCol", "nativeNameRenderer", COL_NATIVE_NAME)): + self._languageStore = self.builder.get_object("languageStore") + self._languageEntry = self.builder.get_object("languageEntry") + self._languageStoreFilter = self.builder.get_object("languageStoreFilter") + self._languageStoreFilter.set_visible_func(self._matches_entry, None) + self._langView = self.builder.get_object("languageView") + self._langSelectedRenderer = self.builder.get_object("langSelectedRenderer") + self._langSelectedColumn = self.builder.get_object("langSelectedColumn") + self._langSelection = self.builder.get_object("languageViewSelection") + self._localeStore = self.builder.get_object("localeStore") + self._localeView = self.builder.get_object("localeView") + + # fill the list with available translations + for lang in localization.get_available_translations(): + self._add_language(self._languageStore, + localization.get_native_name(lang), + localization.get_english_name(lang), lang) + + # render a right arrow for the chosen language + self._right_arrow = Gtk.Image.new_from_file("/usr/share/anaconda/pixmaps/right-arrow-icon.png") + self._langSelectedColumn.set_cell_data_func(self._langSelectedRenderer, + self._render_lang_selected) + + # mark selected locales and languages with selected locales bold + localeNativeColumn = self.builder.get_object("localeNativeName") + localeNativeNameRenderer = self.builder.get_object("localeNativeNameRenderer") + localeNativeColumn.set_cell_data_func(localeNativeNameRenderer, + self._mark_selected_locale_bold) + + for col, rend in [("nativeName", "nativeNameRenderer"), + ("englishName", "englishNameRenderer")]: column = self.builder.get_object(col) renderer = self.builder.get_object(rend) - column.set_cell_data_func(renderer, self._mark_selected_bold, idx) - - # source of lang code <-> UI name mapping - # (localization.get_available_translations() returns a generator) - self.locale_infos_for_data = list(localization.get_available_translations()) - self.locale_infos_for_ui = self.locale_infos_for_data[:] - - for locale in sorted(self.locale_infos_for_ui): - self._add_language(self._langsupportStore, - localization.get_native_name(locale), - localization.get_english_name(locale), - locale, - False, True) - - self._select_language(self._langsupportStore, self.data.lang.lang) + column.set_cell_data_func(renderer, self._mark_selected_language_bold)
def apply(self): - self.data.lang.addsupport = [row[COL_LANG_SETTING] - for row in self._langsupportStore - if row[COL_SELECTED] and row[COL_IS_ADDITIONAL]] + # store only additional langsupport locales + self.data.lang.addsupport = list(sorted(self._selected_locales - set([self.data.lang.lang])))
def refresh(self): - self._langsupportEntry.set_text("") - lang_infos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_ui) - if len(lang_infos) > 1: - log.warning("Found multiple locales for lang %s: %s, picking first", - self.data.lang.lang, lang_infos) - # Just take the first found - # TODO - for corner cases choose the one that is common prefix - lang_infos = lang_infos[:1] - - addsupp_infos = [] - for code in self.data.lang.addsupport: - code_infos = self._find_localeinfos_for_code(code, self.locale_infos_for_ui) - addsupp_infos.extend(code_infos) - - for row in self._langsupportStore: - if row[COL_LANG_SETTING] in addsupp_infos: - row[COL_SELECTED] = True - if row[COL_LANG_SETTING] in lang_infos: - row[COL_SELECTED] = True - row[COL_IS_ADDITIONAL] = False + self._languageEntry.set_text("") + self._selected_locales = set(self._installed_langsupports) + + # select the first locale from the "to be installed" langsupports + self._select_locale(self._installed_langsupports[0]) + + @property + def _installed_langsupports(self): + return sorted(self.data.lang.addsupport + [self.data.lang.lang])
@property def showable(self): @@ -113,13 +106,8 @@ class LangsupportSpoke(NormalSpoke):
@property def status(self): - # TODO: translate - infos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_data)[:1] - for code in self.data.lang.addsupport: - for info in self._find_localeinfos_for_code(code, self.locale_infos_for_data): - if info not in infos: - infos.append(info) - return ", ".join(localization.get_english_name(info) for info in infos) + return ", ".join(localization.get_native_name(locale) + for locale in self._installed_langsupports)
@property def mandatory(self): @@ -129,42 +117,64 @@ class LangsupportSpoke(NormalSpoke): def completed(self): return True
- def _find_localeinfos_for_code(self, code, infos): - if code in infos: - return [code] - else: - retval = [info for info in infos - if code in localization.expand_langs(info)] - log.debug("locale infos found for %s: %s", code, retval) - return retval - - def _add_language(self, store, native, english, setting, selected, additional): - store.append(['<span lang="%s">%s</span>' % (re.sub(r'..*', '', setting), native), - english, setting, selected, additional]) - - def _select_language(self, store, language): - itr = store.get_iter_first() - while itr and language not in localization.expand_langs(store[itr][COL_LANG_SETTING]): - itr = store.iter_next(itr) - - # If we were provided with an unsupported language, just use the default. - if not itr: - return - - treeview = self.builder.get_object("langsupportView") - selection = treeview.get_selection() - selection.select_iter(itr) - path = store.get_path(itr) - treeview.scroll_to_cell(path) + def _add_language(self, store, native, english, lang): + native_span = '<span lang="%s">%s</span>' % (lang, native) + store.append([native_span, english, lang]) + + def _addLocale(self, store, native, locale): + native_span = '<span lang="%s">%s</span>' % (re.sub(r'..*', '', locale), native) + + # native, locale, selected, additional + store.append([native_span, locale, locale in self._selected_locales, + locale != self.data.lang.lang]) + + def _refresh_locale_store(self, lang): + """Refresh the localeStore with locales for the given language.""" + + self._localeStore.clear() + locales = localization.get_language_locales(lang) + for locale in locales: + self._addLocale(self._localeStore, + localization.get_native_name(locale), + locale) + + # select the first locale (with the highest rank) + set_treeview_selection(self._localeView, locales[0], col=1) + + def _select_locale(self, locale): + """ + Try to select the given locale in the language and locale + treeviews. This method tries to find the best match for the given + locale. + + :return: a pair of selected iterators (language and locale) + :rtype: a pair of GtkTreeIter or None objects + + """ + + # get lang and select it + parts = localization.parse_langcode(locale) + if "language" not in parts: + # invalid locale, cannot select + return (None, None) + + lang_itr = set_treeview_selection(self._langView, parts["language"], col=2) + self._refresh_locale_store(parts["language"]) #XXX: needed? + + # find matches and use the one with the highest rank + locales = localization.get_language_locales(locale) + locale_itr = set_treeview_selection(self._localeView, locales[0], col=1) + + return (lang_itr, locale_itr)
def _matches_entry(self, model, itr, *args): # Need to strip out the pango markup before attempting to match. # Otherwise, starting to type "span" for "spanish" will match everything # due to the enclosing span tag. # (success, attrs, native, accel) - native = Pango.parse_markup(model[itr][COL_NATIVE_NAME], -1, "_")[2] - english = model[itr][COL_ENGLISH_NAME] - entry = self._langsupportEntry.get_text().strip() + native = Pango.parse_markup(model[itr][0], -1, "_")[2] + english = model[itr][1] + entry = self._languageEntry.get_text().strip()
# Nothing in the text entry? Display everything. if not entry: @@ -179,21 +189,53 @@ class LangsupportSpoke(NormalSpoke): else: return False
- def _mark_selected_bold(self, column, renderer, model, itr, idx): - value = model[itr][idx] - if model[itr][COL_SELECTED]: - value = "<b>%s</b>" % value - renderer.set_property("markup", value) + def _render_lang_selected(self, column, renderer, model, itr, user_data=None): + (lang_store, sel_itr) = self._langSelection.get_selected() + + lang_locales = set(localization.get_language_locales(model[itr][2])) + if sel_itr and lang_store[sel_itr][2] == model[itr][2]: + renderer.set_property("pixbuf", self._right_arrow.get_pixbuf()) + else: + renderer.set_property("pixbuf", None) + + def _mark_selected_locale_bold(self, column, renderer, model, itr, user_data=None): + value = model[itr][0] + if model[itr][2]: + renderer.set_property("weight", Pango.Weight.BOLD.real) + else: + renderer.set_property("weight", Pango.Weight.NORMAL.real) + + def _mark_selected_language_bold(self, column, renderer, model, itr, user_data=None): + lang_locales = set(localization.get_language_locales(model[itr][2])) + if not lang_locales.isdisjoint(self._selected_locales): + renderer.set_property("weight", Pango.Weight.BOLD.real) + else: + renderer.set_property("weight", Pango.Weight.NORMAL.real) + + # Signal handlers. + def on_lang_selection_changed(self, selection): + (store, selected) = selection.get_selected_rows() + + if selected: + lang = store[selected[0]][2] + self._refresh_locale_store(lang) + else: + self._localeStore.clear() + + def on_locale_toggled(self, renderer, path): + itr = self._localeStore.get_iter(path) + row = self._localeStore[itr]
- def on_langsupport_toggled(self, renderer, path): - selected = not self._langsupportStoreFilter[path][COL_SELECTED] - itr = self._langsupportStoreFilter.get_iter(path) - itr = self._langsupportStoreFilter.convert_iter_to_child_iter(itr) - self._langsupportStore[itr][COL_SELECTED] = selected + row[2] = not row[2] + + if row[2]: + self._selected_locales.add(row[1]) + else: + self._selected_locales.remove(row[1])
def on_clear_icon_clicked(self, entry, icon_pos, event): if icon_pos == Gtk.EntryIconPosition.SECONDARY: entry.set_text("")
def on_entry_changed(self, *args): - self._langsupportStoreFilter.refilter() + self._languageStoreFilter.refilter()
On 08/30/2013 11:15 AM, Vratislav Podzimek wrote:
diff --git a/pyanaconda/ui/gui/spokes/langsupport.py b/pyanaconda/ui/gui/spokes/langsupport.py index 5f44599..6f75ebc 100644 --- a/pyanaconda/ui/gui/spokes/langsupport.py +++ b/pyanaconda/ui/gui/spokes/langsupport.py @@ -17,6 +17,7 @@ # Red Hat, Inc. # # Red Hat Author(s): Radek Vykydal rvykydal@redhat.com +# Vratislav Podzimek vpodzime@redhat.com #
from gi.repository import Gtk, Pango @@ -25,6 +26,7 @@ from pyanaconda.i18n import N_ from pyanaconda.iutil import strip_accents from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.gui.categories.localization import LocalizationCategory +from pyanaconda.ui.gui.utils import set_treeview_selection from pyanaconda import localization
import re @@ -34,14 +36,8 @@ log = logging.getLogger("anaconda")
__all__ = ["LangsupportSpoke"]
-COL_NATIVE_NAME = 0 -COL_ENGLISH_NAME = 1 -COL_LANG_SETTING = 2 -COL_SELECTED = 3 -COL_IS_ADDITIONAL = 4
- class LangsupportSpoke(NormalSpoke):
- builderObjects = ["langsupportStore", "langsupportStoreFilter", "langsupportWindow"]
- builderObjects = ["languageStore", "languageStoreFilter", "localeStore", "langsupportWindow"] mainWidgetName = "langsupportWindow" uiFile = "spokes/langsupport.glade"
@@ -52,60 +48,57 @@ class LangsupportSpoke(NormalSpoke):
def __init__(self, *args, **kwargs): NormalSpoke.__init__(self, *args, **kwargs)
self._selected_locales = set() def initialize(self):
self._langsupportStore = self.builder.get_object("langsupportStore")self._langsupportEntry = self.builder.get_object("langsupportEntry")self._langsupportStoreFilter = self.builder.get_object("langsupportStoreFilter")self._langsupportStoreFilter.set_visible_func(self._matches_entry, None)# mark selected items in language list boldfor col, rend, idx in (("englishNameCol", "englishNameRenderer", COL_ENGLISH_NAME),("nativeNameCol", "nativeNameRenderer", COL_NATIVE_NAME)):
self._languageStore = self.builder.get_object("languageStore")self._languageEntry = self.builder.get_object("languageEntry")self._languageStoreFilter = self.builder.get_object("languageStoreFilter")self._languageStoreFilter.set_visible_func(self._matches_entry, None)self._langView = self.builder.get_object("languageView")self._langSelectedRenderer = self.builder.get_object("langSelectedRenderer")self._langSelectedColumn = self.builder.get_object("langSelectedColumn")self._langSelection = self.builder.get_object("languageViewSelection")self._localeStore = self.builder.get_object("localeStore")self._localeView = self.builder.get_object("localeView")# fill the list with available translationsfor lang in localization.get_available_translations():self._add_language(self._languageStore,localization.get_native_name(lang),localization.get_english_name(lang), lang)# render a right arrow for the chosen languageself._right_arrow = Gtk.Image.new_from_file("/usr/share/anaconda/pixmaps/right-arrow-icon.png")self._langSelectedColumn.set_cell_data_func(self._langSelectedRenderer,self._render_lang_selected)# mark selected locales and languages with selected locales boldlocaleNativeColumn = self.builder.get_object("localeNativeName")localeNativeNameRenderer = self.builder.get_object("localeNativeNameRenderer")localeNativeColumn.set_cell_data_func(localeNativeNameRenderer,self._mark_selected_locale_bold)for col, rend in [("nativeName", "nativeNameRenderer"),("englishName", "englishNameRenderer")]: column = self.builder.get_object(col) renderer = self.builder.get_object(rend)
column.set_cell_data_func(renderer, self._mark_selected_bold, idx)# source of lang code <-> UI name mapping# (localization.get_available_translations() returns a generator)self.locale_infos_for_data = list(localization.get_available_translations())self.locale_infos_for_ui = self.locale_infos_for_data[:]for locale in sorted(self.locale_infos_for_ui):self._add_language(self._langsupportStore,localization.get_native_name(locale),localization.get_english_name(locale),locale,False, True)self._select_language(self._langsupportStore, self.data.lang.lang)
column.set_cell_data_func(renderer, self._mark_selected_language_bold) def apply(self):
self.data.lang.addsupport = [row[COL_LANG_SETTING]for row in self._langsupportStoreif row[COL_SELECTED] and row[COL_IS_ADDITIONAL]]
# store only additional langsupport localesself.data.lang.addsupport = list(sorted(self._selected_locales - set([self.data.lang.lang])))
The list constructor is not needed here.
def refresh(self):
self._langsupportEntry.set_text("")lang_infos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_ui)if len(lang_infos) > 1:log.warning("Found multiple locales for lang %s: %s, picking first",self.data.lang.lang, lang_infos)# Just take the first found# TODO - for corner cases choose the one that is common prefixlang_infos = lang_infos[:1]addsupp_infos = []for code in self.data.lang.addsupport:code_infos = self._find_localeinfos_for_code(code, self.locale_infos_for_ui)addsupp_infos.extend(code_infos)for row in self._langsupportStore:if row[COL_LANG_SETTING] in addsupp_infos:row[COL_SELECTED] = Trueif row[COL_LANG_SETTING] in lang_infos:row[COL_SELECTED] = Truerow[COL_IS_ADDITIONAL] = False
self._languageEntry.set_text("")self._selected_locales = set(self._installed_langsupports)# select the first locale from the "to be installed" langsupportsself._select_locale(self._installed_langsupports[0])@property
def _installed_langsupports(self):
return sorted(self.data.lang.addsupport + [self.data.lang.lang]) @property def showable(self):@@ -113,13 +106,8 @@ class LangsupportSpoke(NormalSpoke):
@property def status(self):
# TODO: translateinfos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_data)[:1]for code in self.data.lang.addsupport:for info in self._find_localeinfos_for_code(code, self.locale_infos_for_data):if info not in infos:infos.append(info)return ", ".join(localization.get_english_name(info) for info in infos)
return ", ".join(localization.get_native_name(locale)for locale in self._installed_langsupports)
IIRC, on proposal in a review I added a patch to keep data.lang.lang first, we might want to keep it.
commit 72debbc57f8f72853f1cae4188fc012aae1f0ec1 Author: Radek Vykydal rvykydal@redhat.com Date: Wed Apr 24 18:23:39 2013 +0200
langsupport spoke: keep data.lang.lang first in status of spoke
@property def mandatory(self):@@ -129,42 +117,64 @@ class LangsupportSpoke(NormalSpoke): def completed(self): return True
- def _find_localeinfos_for_code(self, code, infos):
if code in infos:return [code]else:retval = [info for info in infosif code in localization.expand_langs(info)]log.debug("locale infos found for %s: %s", code, retval)return retval- def _add_language(self, store, native, english, setting, selected, additional):
store.append(['<span lang="%s">%s</span>' % (re.sub(r'\..*', '', setting), native),english, setting, selected, additional])- def _select_language(self, store, language):
itr = store.get_iter_first()while itr and language not in localization.expand_langs(store[itr][COL_LANG_SETTING]):itr = store.iter_next(itr)# If we were provided with an unsupported language, just use the default.if not itr:returntreeview = self.builder.get_object("langsupportView")selection = treeview.get_selection()selection.select_iter(itr)path = store.get_path(itr)treeview.scroll_to_cell(path)
def _add_language(self, store, native, english, lang):
native_span = '<span lang="%s">%s</span>' % (lang, native)store.append([native_span, english, lang])def _addLocale(self, store, native, locale):
native_span = '<span lang="%s">%s</span>' % (re.sub(r'\..*', '', locale), native)# native, locale, selected, additionalstore.append([native_span, locale, locale in self._selected_locales,locale != self.data.lang.lang])def _refresh_locale_store(self, lang):
"""Refresh the localeStore with locales for the given language."""self._localeStore.clear()locales = localization.get_language_locales(lang)for locale in locales:self._addLocale(self._localeStore,localization.get_native_name(locale),locale)# select the first locale (with the highest rank)set_treeview_selection(self._localeView, locales[0], col=1)def _select_locale(self, locale):
"""Try to select the given locale in the language and localetreeviews. This method tries to find the best match for the givenlocale.:return: a pair of selected iterators (language and locale):rtype: a pair of GtkTreeIter or None objects"""# get lang and select itparts = localization.parse_langcode(locale)if "language" not in parts:# invalid locale, cannot selectreturn (None, None)lang_itr = set_treeview_selection(self._langView, parts["language"], col=2)self._refresh_locale_store(parts["language"]) #XXX: needed?# find matches and use the one with the highest ranklocales = localization.get_language_locales(locale)locale_itr = set_treeview_selection(self._localeView, locales[0], col=1)return (lang_itr, locale_itr) def _matches_entry(self, model, itr, *args): # Need to strip out the pango markup before attempting to match. # Otherwise, starting to type "span" for "spanish" will match everything # due to the enclosing span tag. # (success, attrs, native, accel)
native = Pango.parse_markup(model[itr][COL_NATIVE_NAME], -1, "_")[2]english = model[itr][COL_ENGLISH_NAME]entry = self._langsupportEntry.get_text().strip()
native = Pango.parse_markup(model[itr][0], -1, "_")[2]english = model[itr][1]entry = self._languageEntry.get_text().strip() # Nothing in the text entry? Display everything. if not entry:@@ -179,21 +189,53 @@ class LangsupportSpoke(NormalSpoke): else: return False
- def _mark_selected_bold(self, column, renderer, model, itr, idx):
value = model[itr][idx]if model[itr][COL_SELECTED]:value = "<b>%s</b>" % valuerenderer.set_property("markup", value)
- def _render_lang_selected(self, column, renderer, model, itr, user_data=None):
(lang_store, sel_itr) = self._langSelection.get_selected()lang_locales = set(localization.get_language_locales(model[itr][2]))if sel_itr and lang_store[sel_itr][2] == model[itr][2]:renderer.set_property("pixbuf", self._right_arrow.get_pixbuf())else:renderer.set_property("pixbuf", None)- def _mark_selected_locale_bold(self, column, renderer, model, itr, user_data=None):
value = model[itr][0]
value is unused
if model[itr][2]:renderer.set_property("weight", Pango.Weight.BOLD.real)else:renderer.set_property("weight", Pango.Weight.NORMAL.real)- def _mark_selected_language_bold(self, column, renderer, model, itr, user_data=None):
lang_locales = set(localization.get_language_locales(model[itr][2]))if not lang_locales.isdisjoint(self._selected_locales):renderer.set_property("weight", Pango.Weight.BOLD.real)else:renderer.set_property("weight", Pango.Weight.NORMAL.real)- # Signal handlers.
- def on_lang_selection_changed(self, selection):
(store, selected) = selection.get_selected_rows()if selected:lang = store[selected[0]][2]self._refresh_locale_store(lang)else:self._localeStore.clear()- def on_locale_toggled(self, renderer, path):
itr = self._localeStore.get_iter(path)row = self._localeStore[itr]
- def on_langsupport_toggled(self, renderer, path):
selected = not self._langsupportStoreFilter[path][COL_SELECTED]itr = self._langsupportStoreFilter.get_iter(path)itr = self._langsupportStoreFilter.convert_iter_to_child_iter(itr)self._langsupportStore[itr][COL_SELECTED] = selected
row[2] = not row[2]if row[2]:self._selected_locales.add(row[1])else:self._selected_locales.remove(row[1]) def on_clear_icon_clicked(self, entry, icon_pos, event): if icon_pos == Gtk.EntryIconPosition.SECONDARY: entry.set_text("") def on_entry_changed(self, *args):
self._langsupportStoreFilter.refilter()
self._languageStoreFilter.refilter()
On Tue, 2013-09-03 at 14:43 +0200, Radek Vykydal wrote:
On 08/30/2013 11:15 AM, Vratislav Podzimek wrote:
diff --git a/pyanaconda/ui/gui/spokes/langsupport.py b/pyanaconda/ui/gui/spokes/langsupport.py index 5f44599..6f75ebc 100644 --- a/pyanaconda/ui/gui/spokes/langsupport.py +++ b/pyanaconda/ui/gui/spokes/langsupport.py @@ -17,6 +17,7 @@ # Red Hat, Inc. # # Red Hat Author(s): Radek Vykydal rvykydal@redhat.com +# Vratislav Podzimek vpodzime@redhat.com #
from gi.repository import Gtk, Pango @@ -25,6 +26,7 @@ from pyanaconda.i18n import N_ from pyanaconda.iutil import strip_accents from pyanaconda.ui.gui.spokes import NormalSpoke from pyanaconda.ui.gui.categories.localization import LocalizationCategory +from pyanaconda.ui.gui.utils import set_treeview_selection from pyanaconda import localization
import re @@ -34,14 +36,8 @@ log = logging.getLogger("anaconda")
__all__ = ["LangsupportSpoke"]
-COL_NATIVE_NAME = 0 -COL_ENGLISH_NAME = 1 -COL_LANG_SETTING = 2 -COL_SELECTED = 3 -COL_IS_ADDITIONAL = 4
- class LangsupportSpoke(NormalSpoke):
- builderObjects = ["langsupportStore", "langsupportStoreFilter", "langsupportWindow"]
- builderObjects = ["languageStore", "languageStoreFilter", "localeStore", "langsupportWindow"] mainWidgetName = "langsupportWindow" uiFile = "spokes/langsupport.glade"
@@ -52,60 +48,57 @@ class LangsupportSpoke(NormalSpoke):
def __init__(self, *args, **kwargs): NormalSpoke.__init__(self, *args, **kwargs)
self._selected_locales = set() def initialize(self):
self._langsupportStore = self.builder.get_object("langsupportStore")self._langsupportEntry = self.builder.get_object("langsupportEntry")self._langsupportStoreFilter = self.builder.get_object("langsupportStoreFilter")self._langsupportStoreFilter.set_visible_func(self._matches_entry, None)# mark selected items in language list boldfor col, rend, idx in (("englishNameCol", "englishNameRenderer", COL_ENGLISH_NAME),("nativeNameCol", "nativeNameRenderer", COL_NATIVE_NAME)):
self._languageStore = self.builder.get_object("languageStore")self._languageEntry = self.builder.get_object("languageEntry")self._languageStoreFilter = self.builder.get_object("languageStoreFilter")self._languageStoreFilter.set_visible_func(self._matches_entry, None)self._langView = self.builder.get_object("languageView")self._langSelectedRenderer = self.builder.get_object("langSelectedRenderer")self._langSelectedColumn = self.builder.get_object("langSelectedColumn")self._langSelection = self.builder.get_object("languageViewSelection")self._localeStore = self.builder.get_object("localeStore")self._localeView = self.builder.get_object("localeView")# fill the list with available translationsfor lang in localization.get_available_translations():self._add_language(self._languageStore,localization.get_native_name(lang),localization.get_english_name(lang), lang)# render a right arrow for the chosen languageself._right_arrow = Gtk.Image.new_from_file("/usr/share/anaconda/pixmaps/right-arrow-icon.png")self._langSelectedColumn.set_cell_data_func(self._langSelectedRenderer,self._render_lang_selected)# mark selected locales and languages with selected locales boldlocaleNativeColumn = self.builder.get_object("localeNativeName")localeNativeNameRenderer = self.builder.get_object("localeNativeNameRenderer")localeNativeColumn.set_cell_data_func(localeNativeNameRenderer,self._mark_selected_locale_bold)for col, rend in [("nativeName", "nativeNameRenderer"),("englishName", "englishNameRenderer")]: column = self.builder.get_object(col) renderer = self.builder.get_object(rend)
column.set_cell_data_func(renderer, self._mark_selected_bold, idx)# source of lang code <-> UI name mapping# (localization.get_available_translations() returns a generator)self.locale_infos_for_data = list(localization.get_available_translations())self.locale_infos_for_ui = self.locale_infos_for_data[:]for locale in sorted(self.locale_infos_for_ui):self._add_language(self._langsupportStore,localization.get_native_name(locale),localization.get_english_name(locale),locale,False, True)self._select_language(self._langsupportStore, self.data.lang.lang)
column.set_cell_data_func(renderer, self._mark_selected_language_bold) def apply(self):
self.data.lang.addsupport = [row[COL_LANG_SETTING]for row in self._langsupportStoreif row[COL_SELECTED] and row[COL_IS_ADDITIONAL]]
# store only additional langsupport localesself.data.lang.addsupport = list(sorted(self._selected_locales - set([self.data.lang.lang])))The list constructor is not needed here.
Yeah, I've found out sorted() returns a list instead of generator while I was writing those patches. Thanks!
def refresh(self):
self._langsupportEntry.set_text("")lang_infos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_ui)if len(lang_infos) > 1:log.warning("Found multiple locales for lang %s: %s, picking first",self.data.lang.lang, lang_infos)# Just take the first found# TODO - for corner cases choose the one that is common prefixlang_infos = lang_infos[:1]addsupp_infos = []for code in self.data.lang.addsupport:code_infos = self._find_localeinfos_for_code(code, self.locale_infos_for_ui)addsupp_infos.extend(code_infos)for row in self._langsupportStore:if row[COL_LANG_SETTING] in addsupp_infos:row[COL_SELECTED] = Trueif row[COL_LANG_SETTING] in lang_infos:row[COL_SELECTED] = Truerow[COL_IS_ADDITIONAL] = False
self._languageEntry.set_text("")self._selected_locales = set(self._installed_langsupports)# select the first locale from the "to be installed" langsupportsself._select_locale(self._installed_langsupports[0])@property
def _installed_langsupports(self):
return sorted(self.data.lang.addsupport + [self.data.lang.lang]) @property def showable(self):@@ -113,13 +106,8 @@ class LangsupportSpoke(NormalSpoke):
@property def status(self):
# TODO: translateinfos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_data)[:1]for code in self.data.lang.addsupport:for info in self._find_localeinfos_for_code(code, self.locale_infos_for_data):if info not in infos:infos.append(info)return ", ".join(localization.get_english_name(info) for info in infos)
return ", ".join(localization.get_native_name(locale)for locale in self._installed_langsupports)IIRC, on proposal in a review I added a patch to keep data.lang.lang first, we might want to keep it.
okay, will change that.
@property def mandatory(self):@@ -129,42 +117,64 @@ class LangsupportSpoke(NormalSpoke): def completed(self): return True
- def _find_localeinfos_for_code(self, code, infos):
if code in infos:return [code]else:retval = [info for info in infosif code in localization.expand_langs(info)]log.debug("locale infos found for %s: %s", code, retval)return retval- def _add_language(self, store, native, english, setting, selected, additional):
store.append(['<span lang="%s">%s</span>' % (re.sub(r'\..*', '', setting), native),english, setting, selected, additional])- def _select_language(self, store, language):
itr = store.get_iter_first()while itr and language not in localization.expand_langs(store[itr][COL_LANG_SETTING]):itr = store.iter_next(itr)# If we were provided with an unsupported language, just use the default.if not itr:returntreeview = self.builder.get_object("langsupportView")selection = treeview.get_selection()selection.select_iter(itr)path = store.get_path(itr)treeview.scroll_to_cell(path)
def _add_language(self, store, native, english, lang):
native_span = '<span lang="%s">%s</span>' % (lang, native)store.append([native_span, english, lang])def _addLocale(self, store, native, locale):
native_span = '<span lang="%s">%s</span>' % (re.sub(r'\..*', '', locale), native)# native, locale, selected, additionalstore.append([native_span, locale, locale in self._selected_locales,locale != self.data.lang.lang])def _refresh_locale_store(self, lang):
"""Refresh the localeStore with locales for the given language."""self._localeStore.clear()locales = localization.get_language_locales(lang)for locale in locales:self._addLocale(self._localeStore,localization.get_native_name(locale),locale)# select the first locale (with the highest rank)set_treeview_selection(self._localeView, locales[0], col=1)def _select_locale(self, locale):
"""Try to select the given locale in the language and localetreeviews. This method tries to find the best match for the givenlocale.:return: a pair of selected iterators (language and locale):rtype: a pair of GtkTreeIter or None objects"""# get lang and select itparts = localization.parse_langcode(locale)if "language" not in parts:# invalid locale, cannot selectreturn (None, None)lang_itr = set_treeview_selection(self._langView, parts["language"], col=2)self._refresh_locale_store(parts["language"]) #XXX: needed?# find matches and use the one with the highest ranklocales = localization.get_language_locales(locale)locale_itr = set_treeview_selection(self._localeView, locales[0], col=1)return (lang_itr, locale_itr) def _matches_entry(self, model, itr, *args): # Need to strip out the pango markup before attempting to match. # Otherwise, starting to type "span" for "spanish" will match everything # due to the enclosing span tag. # (success, attrs, native, accel)
native = Pango.parse_markup(model[itr][COL_NATIVE_NAME], -1, "_")[2]english = model[itr][COL_ENGLISH_NAME]entry = self._langsupportEntry.get_text().strip()
native = Pango.parse_markup(model[itr][0], -1, "_")[2]english = model[itr][1]entry = self._languageEntry.get_text().strip() # Nothing in the text entry? Display everything. if not entry:@@ -179,21 +189,53 @@ class LangsupportSpoke(NormalSpoke): else: return False
- def _mark_selected_bold(self, column, renderer, model, itr, idx):
value = model[itr][idx]if model[itr][COL_SELECTED]:value = "<b>%s</b>" % valuerenderer.set_property("markup", value)
- def _render_lang_selected(self, column, renderer, model, itr, user_data=None):
(lang_store, sel_itr) = self._langSelection.get_selected()lang_locales = set(localization.get_language_locales(model[itr][2]))if sel_itr and lang_store[sel_itr][2] == model[itr][2]:renderer.set_property("pixbuf", self._right_arrow.get_pixbuf())else:renderer.set_property("pixbuf", None)- def _mark_selected_locale_bold(self, column, renderer, model, itr, user_data=None):
value = model[itr][0]value is unused
Good catch, thanks!
Using expand_langs was non-deterministic in a way that if e.g. "pt" came before "pt_BR" in searching for a match for something from expand_langs("pt_BR"), it was picked up. However, in such a case we want to pick up "pt_BR" as it matches better.
This patch adds a function for finding matches (now the best one) deterministicly and a function to determine if given langcode (e.g. "en") matches some locale (e.g. "en_US"). The former one is useful e.g. for getting the best rnotes to show based on the installation language, the latter one is useful for adding langsupport groups to the installation transaction.
Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- pyanaconda/localization.py | 125 +++++++++++++++++++--------- pyanaconda/packaging/yumpayload.py | 9 +- pyanaconda/ui/gui/hubs/progress.py | 53 +++++++----- tests/pyanaconda_tests/localization_test.py | 61 ++++++++++++-- 4 files changed, 175 insertions(+), 73 deletions(-)
diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py index b0e8a1b..cf47a43 100644 --- a/pyanaconda/localization.py +++ b/pyanaconda/localization.py @@ -88,64 +88,111 @@ def parse_langcode(langcode): else: return None
-def expand_langs(astring): +def is_supported_locale(locale): """ - Converts a single language into a "language search path". For example, - for "fr_FR.UTF-8@euro" would return set containing: - "fr", "fr_FR", "fr_FR.UTF-8@euro", "fr.UTF-8@euro", "fr_FR@euro", - "fr_FR.UTF-8", "fr@euro", "fr.UTF-8" + Function that tells if the given locale is supported by the Anaconda or + not. We consider locales supported by the langtable as supported by the + Anaconda.
- :rtype: list of strings + :param locale: locale to test + :type locale: str + :return: whether the given locale is supported or not + :rtype: bool + :raise InvalidLocaleSpec: if an invalid locale is given (see LANGCODE_RE)
"""
- langs = set([astring]) - - lang_dict = parse_langcode(astring) - - if not lang_dict: - return list(langs) + en_name = get_english_name(locale) + return bool(en_name)
- base, loc, enc, script = [lang_dict[key] for key in ("language", - "territory", "encoding", "script")] +def langcode_matches_locale(langcode, locale): + """ + Function that tells if the given langcode matches the given locale. I.e. if + all parts of appearing in the langcode (language, territory, script and + encoding) are the same as the matching parts of the locale.
- if not base: - return list(langs) + :param langcode: a langcode (e.g. en, en_US, en_US@latin, etc.) + :type langcode: str + :param locale: a valid locale (e.g. en_US.UTF-8 or sr_RS.UTF-8@latin, etc.) + :type locale: str + :return: whether the given langcode matches the given locale or not + :rtype: bool
- if not enc: - enc = "UTF-8" + """
- langs.add(base) - langs.add("%s.%s" % (base, enc)) + langcode_parts = parse_langcode(langcode) + locale_parts = parse_langcode(locale)
- if loc: - langs.add("%s_%s" % (base, loc)) - langs.add("%s_%s.%s" %(base, loc, enc)) - if script: - langs.add("%s@%s" % (base, script)) - langs.add("%s.%s@%s" % (base, enc, script)) + if not langcode_parts or not locale_parts: + # to match, both need to be valid langcodes (need to have at least + # language specified) + return False
- if loc and script: - langs.add("%s_%s@%s" % (base, loc, script)) + # Check parts one after another. If some part appears in the langcode and + # doesn't match the one from the locale (or is missing in the locale), + # return False, otherwise they match + for part in ("language", "territory", "script", "encoding"): + if langcode_parts[part] and langcode_parts[part] != locale_parts.get(part): + return False
- return list(langs) + return True
-def is_supported_locale(locale): +def find_best_locale_match(locale, langcodes): """ - Function that tells if the given locale is supported by the Anaconda or - not. We consider locales supported by the langtable as supported by the - Anaconda. + Find the best match for the locale in a list of langcodes. This is useful + when e.g. pt_BR is a locale and there are possibilities to choose an item + (e.g. rnote) for a list containing both pt and pt_BR or even also pt_PT.
- :param locale: locale to test + :param locale: a valid locale (e.g. en_US.UTF-8 or sr_RS.UTF-8@latin, etc.) :type locale: str - :return: whether the given locale is supported or not - :rtype: bool - :raise InvalidLocaleSpec: if an invalid locale is given (see LANGCODE_RE) + :param langcodes: a list or generator of langcodes (e.g. en, en_US, en_US@latin, etc.) + :type langcodes: list(str) or generator(str) + :return: the best matching langcode from the list of None if none matches + :rtype: str or None
"""
- en_name = get_english_name(locale) - return bool(en_name) + score_map = { "language" : 1000, + "territory": 100, + "script" : 10, + "encoding" : 1 } + + def get_match_score(locale, langcode): + score = 0 + + locale_parts = parse_langcode(locale) + langcode_parts = parse_langcode(langcode) + if not locale_parts or not langcode_parts: + return score + + for part, part_score in score_map.iteritems(): + if locale_parts[part] and langcode_parts[part]: + if locale_parts[part] == langcode_parts[part]: + # match + score += part_score + else: + # not match + score -= part_score + elif langcode_parts[part] and not locale_parts[part]: + # langcode has something the locale doesn't have + score -= part_score + + return score + + scores = [] + + # get score for each langcode + for langcode in langcodes: + scores.append((langcode, get_match_score(locale, langcode))) + + # find the best one + sorted_langcodes = sorted(scores, key=lambda item_score: item_score[1], reverse=True) + + # matches matching only script or encoding or both are not useful + if sorted_langcodes and sorted_langcodes[0][1] > score_map["territory"]: + return sorted_langcodes[0][0] + else: + return None
def setup_locale(locale, lang=None): """ diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py index 0ad383b..72e016a 100644 --- a/pyanaconda/packaging/yumpayload.py +++ b/pyanaconda/packaging/yumpayload.py @@ -79,7 +79,7 @@ from pyanaconda.packaging import DependencyError, MetadataError, NoNetworkError, PayloadSetupError from pyanaconda.progress import progressQ
-from pyanaconda.localization import expand_langs +from pyanaconda.localization import langcode_matches_locale
from pykickstart.constants import GROUP_ALL, GROUP_DEFAULT, KS_MISSING_IGNORE
@@ -979,10 +979,9 @@ reposdir=%s with _yum_lock: groups = yum_groups.get_groups() for lang_code in lang_codes: - for lang_code_guess in expand_langs(lang_code): - for group in groups: - if group.langonly == lang_code_guess: - lang_groups.add(group.groupid) + for group in groups: + if langcode_matches_locale(group.langonly, lang_code): + lang_groups.add(group.groupid)
return list(lang_groups)
diff --git a/pyanaconda/ui/gui/hubs/progress.py b/pyanaconda/ui/gui/hubs/progress.py index 1394e2f..45b4734 100644 --- a/pyanaconda/ui/gui/hubs/progress.py +++ b/pyanaconda/ui/gui/hubs/progress.py @@ -26,9 +26,10 @@ from gi.repository import GLib, Gtk import itertools import os import sys +import glob
from pyanaconda.i18n import _ -from pyanaconda.localization import expand_langs +from pyanaconda.localization import langcode_matches_locale, find_best_locale_match from pyanaconda.product import productName from pyanaconda.flags import flags from pyanaconda.constants import THREAD_INSTALL, THREAD_CONFIGURATION, DEFAULT_LANG @@ -142,28 +143,38 @@ class ProgressHub(Hub): self._progressNotebook.set_current_page(0)
def _get_rnotes(self): - import glob - # We first look for rnotes in paths containing the language, then in # directories without the language component. You know, just in case. - try: - langs = expand_langs(os.environ["LANG"]) + [""] - except KeyError: - # if for some reason LANG isn't set to anything, set it to - # the default value - os.environ["LANG"] = DEFAULT_LANG - langs = expand_langs(os.environ["LANG"]) + [""] - - paths = ["/tmp/updates/pixmaps/rnotes/%s/", - "/tmp/product/pixmaps/rnotes/%s/", - "/usr/share/anaconda/pixmaps/rnotes/%s/"] - - for (l, d) in itertools.product(langs, paths): - pixmaps = glob.glob((d % l) + "*.png") + glob.glob((d % l) + "*.jpg") - if len(pixmaps) > 0: - return pixmaps - - return [] + + paths = ["/tmp/updates/pixmaps/rnotes/", + "/tmp/product/pixmaps/rnotes/", + "/usr/share/anaconda/pixmaps/rnotes/"] + + all_lang_pixmaps = [] + for path in paths: + all_lang_pixmaps += glob.glob(path + "*/*.png") + glob.glob(path + "*/*.jpg") + + pixmap_langs = [pixmap.split(os.path.sep)[-2] for pixmap in all_lang_pixmaps] + best_lang = find_best_locale_match(os.environ["LANG"], pixmap_langs) + + if not best_lang: + # nothing found, try the default language + best_lang = find_best_locale_match(DEFAULT_LANG, pixmap_langs) + + if not best_lang: + # nothing found even for the default language, try non-localized rnotes + non_localized = [] + for path in paths: + non_localized += glob.glob(path + "*.png") + glob.glob(path + "*.jpg") + + return non_localized + + best_lang_pixmaps = [] + for path in paths: + best_lang_pixmaps += (glob.glob(path + best_lang + "/*.png") + + glob.glob(path + best_lang + "/*.jpg")) + + return best_lang_pixmaps
def _cycle_rnotes(self): # Change the ransom notes image every minute by grabbing the next diff --git a/tests/pyanaconda_tests/localization_test.py b/tests/pyanaconda_tests/localization_test.py index 2cdcce8..7f94a71 100644 --- a/tests/pyanaconda_tests/localization_test.py +++ b/tests/pyanaconda_tests/localization_test.py @@ -99,11 +99,56 @@ class UpcaseFirstLetterTests(unittest.TestCase): self.assertEqual(localization._upcase_first_letter("czech Republic"), "Czech Republic")
-class ExpandLangsTest(unittest.TestCase): - def expand_langs_test(self): - """expand_langs should return every valid combination.""" - - expected_result = ["fr", "fr_FR", "fr_FR.UTF-8@euro", "fr.UTF-8@euro", - "fr_FR@euro", "fr_FR.UTF-8", "fr@euro", "fr.UTF-8"] - self.assertListEqual(localization.expand_langs("fr_FR.UTF-8@euro"), - expected_result) +class LangcodeLocaleMatchingTests(unittest.TestCase): + def langcode_matches_locale_test(self): + """Langcode-locale matching should work as expected.""" + + # should match + self.assertTrue(localization.langcode_matches_locale("sr", "sr")) + self.assertTrue(localization.langcode_matches_locale("sr", "sr_RS")) + self.assertTrue(localization.langcode_matches_locale("sr", "sr_RS.UTF-8")) + self.assertTrue(localization.langcode_matches_locale("sr", "sr_RS.UTF-8@latin")) + self.assertTrue(localization.langcode_matches_locale("sr_RS", "sr_RS")) + self.assertTrue(localization.langcode_matches_locale("sr_RS", "sr_RS.UTF-8")) + self.assertTrue(localization.langcode_matches_locale("sr_RS", "sr_RS.UTF-8@latin")) + self.assertTrue(localization.langcode_matches_locale("sr_RS.UTF-8", "sr_RS.UTF-8")) + self.assertTrue(localization.langcode_matches_locale("sr_RS.UTF-8", "sr_RS.UTF-8@latin")) + self.assertTrue(localization.langcode_matches_locale("sr_RS.UTF-8@latin", "sr_RS.UTF-8@latin")) + + # missing language, shouldn't match + self.assertFalse(localization.langcode_matches_locale("", "sr")) + self.assertFalse(localization.langcode_matches_locale("sr", "")) + + # missing items in the locale, shouldn't match + self.assertFalse(localization.langcode_matches_locale("sr_RS", "sr")) + self.assertFalse(localization.langcode_matches_locale("sr_RS.UTF-8", "sr_RS")) + self.assertFalse(localization.langcode_matches_locale("sr.UTF-8", "sr_RS")) + self.assertFalse(localization.langcode_matches_locale("sr_RS.UTF-8", "sr.UTF-8")) + self.assertFalse(localization.langcode_matches_locale("sr_RS.UTF-8@latin", "sr_RS")) + self.assertFalse(localization.langcode_matches_locale("sr_RS@latin", "sr_RS")) + self.assertFalse(localization.langcode_matches_locale("sr.UTF-8@latin", "sr_RS.UTF-8")) + self.assertFalse(localization.langcode_matches_locale("sr@latin", "sr_RS")) + + # different parts, shouldn't match + self.assertFalse(localization.langcode_matches_locale("sr", "en")) + self.assertFalse(localization.langcode_matches_locale("de_CH", "fr_CH")) + self.assertFalse(localization.langcode_matches_locale("sr_RS", "sr_ME")) + self.assertFalse(localization.langcode_matches_locale("sr_RS@latin", "sr_RS@cyrilic")) + self.assertFalse(localization.langcode_matches_locale("sr_RS@latin", "sr_ME@latin")) + + def find_best_locale_match_test(self): + """Finding best locale matches should work as expected.""" + + # can find best matches + self.assertEqual(localization.find_best_locale_match("cs_CZ", ["cs", "cs_CZ", "en", "en_US"]), "cs_CZ") + self.assertEqual(localization.find_best_locale_match("cs", ["cs_CZ", "cs", "en", "en_US"]), "cs") + self.assertEqual(localization.find_best_locale_match("pt_BR", ["pt", "pt_BR"]), "pt_BR") + self.assertEqual(localization.find_best_locale_match("pt_BR", ["pt", "pt_BR", "pt_PT"]), "pt_BR") + self.assertEqual(localization.find_best_locale_match("cs_CZ.UTF-8", ["cs", "cs_CZ", "cs_CZ.UTF-8"]), + "cs_CZ.UTF-8") + self.assertEqual(localization.find_best_locale_match("cs_CZ.UTF-8@latin", + ["cs", "cs_CZ@latin", "cs_CZ.UTF-8"]), "cs_CZ@latin") + + # no matches + self.assertIsNone(localization.find_best_locale_match("pt_BR", ["en_BR", "en"])) + self.assertIsNone(localization.find_best_locale_match("cs_CZ.UTF-8", ["en", "en.UTF-8"]))
The patchset looks good to me with some minor comments.
I like the UI, only the boldness of langs selected in langsupport spoke doesn't seem to highlight the items very much in the video.
On 08/30/2013 11:15 AM, Vratislav Podzimek wrote:
These patches do mainly three things:
- Rework the Welcome spoke and Langsupport spoke to work with all locales
available instead of few locales we think are best for languages we have translations for.
- Remove the cryptic checkbutton for setting only the language-default keyboard
layout instead of a combination with the 'us' layout. Having the layout indicator now, we may set the language-default layout as the only one and switch to it from the default 'us' layout (active on the Welcome spoke) automatically. Exceptions are languages the default layout of which don't support typing ASCII characters. For those layouts we should append 'us' to the list of activated layouts and set up layout switching automatically.
- Get rid of the expand_langs() function and it's usage resulting in
highly-probably nondeterministic results.
I've recorded a video preview [1] demonstrating the new look and behaviour.
[1] http://vpodzime.fedorapeople.org/locales_changes.webm
Altough there are many lines of code added by those patches, the overall user experience should be much better and they resolve a lot of bugs and complaints. And I want to come up with a patch dealing with a lot of code duplicated in Welcome and Langsupport spokes' code that should result in many lines removed. However, I'd like to see those 7 patches landing in the Fedora 20 asap because it is quite a significant change for users.
Vratislav Podzimek (7): Remove an unused argument of get_available_translations Allow seting up locale without modifying ksdata Remove the cryptic "language-default keyboard" checkbutton Improve import in GUI utils a bit Rework the Welcome spoke to allow users choose from all locales Rework the Langsupport spoke to work with all locales Get rid of the non-deterministic expand_langs and its usage
pyanaconda/localization.py | 156 ++++++++----- pyanaconda/packaging/yumpayload.py | 9 +- pyanaconda/ui/gui/hubs/progress.py | 53 +++-- pyanaconda/ui/gui/spokes/langsupport.glade | 167 ++++++++++---- pyanaconda/ui/gui/spokes/langsupport.py | 244 +++++++++++--------- pyanaconda/ui/gui/spokes/welcome.glade | 336 ++++++++++++++-------------- pyanaconda/ui/gui/spokes/welcome.py | 192 +++++++++------- pyanaconda/ui/gui/utils.py | 45 +++- tests/pyanaconda_tests/localization_test.py | 61 ++++- 9 files changed, 772 insertions(+), 491 deletions(-)
Welcome screen:
(1) I'm not really a fan of the translated name being right aligned. What was wrong with the older style of "Translated (English)"?
(2) The "Which language would you like to use?" string is translated for just about every language, but I see a lot of other strings are not being translated. Is this just incompleteness of translation, or something else? Because it's pretty weird looking.
Language support screen:
(3) "Select additional language support to be installed" looks out of place - it's aligned way over on the left, but then the scroll boxes are pretty narrow in the center of the screen. It's also fairly close vertically, and doesn't end with a period.
(4) #1 applies here too.
(5) Can you do something about the horizontal shrinking and growing of the scroll boxes as different languages are selected? I know we fought this on the welcome screen in the past.
- Chris
On Tue, 2013-09-10 at 15:09 -0400, Chris Lumens wrote:
Welcome screen:
(1) I'm not really a fan of the translated name being right aligned. What was wrong with the older style of "Translated (English)"?
Try to ask Máirín, that's what she suggested in the mockup. [1] But formerly we had 'Translated (translated territory) English (english territory)' with quite a big gap in between and it was one of the "usability suggestions" to make the screen not so overwhelming [2].
[1] http://duffy.fedorapeople.org/anaconda/temp/LangAndSubLangSelection/lang-sel... [2] https://fedoraproject.org/wiki/Anaconda/UX_Redesign/Usability_Test_Suggestio...
(2) The "Which language would you like to use?" string is translated for just about every language, but I see a lot of other strings are not being translated. Is this just incompleteness of translation, or something else? Because it's pretty weird looking.
It's just incompleteness of translations.
Language support screen:
(3) "Select additional language support to be installed" looks out of place - it's aligned way over on the left, but then the scroll boxes are pretty narrow in the center of the screen. It's also fairly close vertically, and doesn't end with a period.
Good points, thanks. I'll add some spacing between the label and the boxes and make it centered ending with a period.
(5) Can you do something about the horizontal shrinking and growing of the scroll boxes as different languages are selected? I know we fought this on the welcome screen in the past.
I'll try to do something about it. The scroll box only gets wider and wider if languages with wider locale names are selected. But it never gets back. I'll try to set the default width to some wide-enough value so that it stays the same.
anaconda-patches@lists.fedorahosted.org