[newui] Populate the welcome spoke with translations

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Get the available translations for anaconda
and populate the welcome spoke with them.
---
 anaconda.spec.in                    |    1 +
 pyanaconda/localization.py          |  274 +++++++++++++++++++++++++++++++++++
 pyanaconda/ui/gui/spokes/welcome.py |   52 +++++--
 pyanaconda/ui/gui/spokes/welcome.ui |    4 +-
 4 files changed, 316 insertions(+), 15 deletions(-)
 create mode 100644 pyanaconda/localization.py

diff --git a/anaconda.spec.in b/anaconda.spec.in
index 31df6d5..682dd89 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -112,6 +112,7 @@ Requires: dosfstools
 Requires: e2fsprogs >= %{e2fsver}
 Requires: gzip
 Requires: libarchive
+Requires: python-babel
 %ifarch %{ix86} x86_64 ia64
 Requires: dmidecode
 %endif
diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py
new file mode 100644
index 0000000..f561557
--- /dev/null
+++ b/pyanaconda/localization.py
@@ -0,0 +1,274 @@
+# Localization classes and functions
+#
+# Copyright (C) 2012  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Martin Gracik <mgracik@xxxxxxxxxx>
+#
+
+from collections import defaultdict, deque
+import gettext
+import locale
+import os
+import re
+
+import babel
+
+
+LOCALE_PREFERENCES = {}
+
+
+class LocaleInfo(object):
+
+    def __init__(self, localedata):
+        self._localedata = localedata
+
+    @property
+    def language(self):
+        return self._localedata.language
+
+    @property
+    def territory(self):
+        return self._localedata.territory
+
+    @property
+    def script(self):
+        return self._localedata.script
+
+    @property
+    def variant(self):
+        return self._localedata.variant
+
+    @property
+    def english_name(self):
+        return self._localedata.english_name or u''
+
+    @property
+    def display_name(self):
+        # some languages don't have a display_name
+        display_name = self._localedata.display_name or self.english_name
+        # some start with lowercase
+        display_name = display_name.title()
+        return display_name
+
+    @property
+    def short_name(self):
+        return self.__repr__()
+
+    def __repr__(self):
+        formatstr = '{0.language}'
+        if self.territory is not None:
+            formatstr += '_{0.territory}'
+        if self.script is not None:
+            formatstr += '@{0.script}'
+        if self.variant is not None:
+            formatstr += '#{0.variant}'
+
+        return formatstr.format(self)
+
+    def __str__(self):
+        return self.english_name.encode('ascii', 'replace')
+
+    def __unicode__(self):
+        return self.english_name
+
+    def __eq__(self, other):
+        return repr(self) == repr(other)
+
+
+# XXX this should probably be somewhere else
+def partition(seq, func=bool, func_range=(True, False)):
+    buffers = dict(((x, deque()) for x in func_range))
+
+    def values(x, seq=iter(seq)):
+        while True:
+            while not buffers[x]:
+                item = seq.next()
+                buffers[func(item)].append(item)
+
+            yield buffers[x].popleft()
+
+    return tuple(values(x) for x in func_range)
+
+
+def get_all_locales():
+    localeset = set()
+    for localename in sorted(babel.localedata.list()):
+        try:
+            localedata = babel.Locale.parse(localename)
+        except babel.core.UnknownLocaleError:
+            continue
+
+        locale = LocaleInfo(localedata)
+        if repr(locale) not in localeset:
+            localeset.add(repr(locale))
+            yield locale
+
+
+def get_available_translations(domain=None, localedir=None):
+    domain = domain or gettext._current_domain
+    localedir = localedir or gettext._default_localedir
+
+    langdict = babel.Locale('en', 'US').languages
+    messagefiles = gettext.find(domain, localedir, langdict.keys(), all=True)
+    languages = [path.split(os.path.sep)[-3] for path in messagefiles]
+
+    # usually there are no message files for en_US
+    if 'en_US' not in languages:
+        languages.append('en_US')
+
+    for langcode in languages:
+        try:
+            localedata = babel.Locale.parse(langcode)
+        except babel.core.UnknownLocaleError:
+            continue
+
+        yield LocaleInfo(localedata)
+
+
+class PreferredLocale(object):
+
+    @staticmethod
+    def from_language(language):
+        locales = defaultdict(set)
+        for locale in get_all_locales():
+            locales[repr(locale)].add(locale)
+            locales[locale.language].add(locale)
+
+        return PreferredLocale(locales[language])
+
+    @staticmethod
+    def from_territory(territory):
+        locales = defaultdict(set)
+        for locale in get_all_locales():
+            locales[locale.territory].add(locale)
+
+        return PreferredLocale(locales[territory])
+
+    def __init__(self, localeset):
+        self._localedict = {repr(locale):locale for locale in localeset}
+
+    def get_all_locales(self, preferences=[]):
+        preferences = filter(self._localedict.__contains__, preferences)
+        inside, outside = partition(self._localedict.keys(), func=lambda x: x in preferences)
+        sorted_locales = [self._localedict[localename] for localename in list(inside) + list(outside)]
+        return sorted_locales
+
+    def get_preferred_locale(self, preferences=[]):
+        try:
+            return self.get_all_locales(preferences)[0]
+        except IndexError:
+            return None
+
+
+class Language(object):
+
+    def __init__(self, preferences={}, territory=None):
+        self.translations = {repr(locale):locale for locale in get_available_translations()}
+        self.locales = {repr(locale):locale for locale in get_all_locales()}
+        self.preferred_translation = self.translations['en_US']
+        self.preferred_locales = [self.locales['en_US']]
+        self.preferred_locale = self.preferred_locales[0]
+
+        self.all_preferences = preferences
+        self.preferences = self.all_preferences.get(territory, [])
+        self.territory = territory
+        if self.territory:
+            self._get_preferred_translation_and_locales()
+
+    def _get_preferred_translation_and_locales(self):
+        # get locales from territory
+        locales_from_territory = PreferredLocale.from_territory(self.territory)
+        all_locales = locales_from_territory.get_all_locales(self.preferences)
+
+        # get preferred translation
+        for locale in all_locales:
+            if locale.language in self.translations:
+                self.preferred_translation = self.translations[locale.language]
+                break
+
+        for locale in all_locales:
+            if locale.short_name in self.translations:
+                self.preferred_translation = self.translations[locale.short_name]
+                break
+
+        self.preferred_locales = all_locales
+
+    def select_translation(self, translation):
+        translation = self.translations[translation]
+        self.preferences.extend(self.all_preferences.get(translation.language, []))
+
+        # get locales from translation
+        locales_from_language = PreferredLocale.from_language(translation.short_name)
+        all_locales = locales_from_language.get_all_locales(self.preferences)
+
+        # get preferred locale
+        for locale in all_locales:
+            if locale in self.preferred_locales:
+                self.preferred_locale = locale
+                break
+        else:
+            try:
+                self.preferred_locale = all_locales[0]
+            except IndexError:
+                self.preferred_locale = self.preferred_locales[0]
+
+        # add the preferred locale to the beginning of locales
+        if self.preferred_locale in self.preferred_locales:
+            self.preferred_locales.remove(self.preferred_locale)
+        self.preferred_locales.insert(0, self.preferred_locale)
+
+        # if territory is not set, use the one from preferred locale
+        self.territory = self.territory or self.preferred_locale.territory
+
+    @staticmethod
+    def parse_langcode(langcode):
+        pattern = re.compile(r'(?P<language>[A-Za-z]+)(_(?P<territory>[A-Za-z]+))?(\.(?P<codeset>[-\w]+))?(@(?P<modifier>[-\w]+))?')
+        m = pattern.match(langcode)
+        return m.groupdict()
+
+    @property
+    def install_lang_as_dict(self):
+        self.parse_langcode(self.install_lang)
+
+    @property
+    def system_lang_as_dict(self):
+        self.parse_langcode(self.system_lang)
+
+    def set_install_lang(self, langcode):
+        self.install_lang = langcode
+
+        os.environ['LANG'] = langcode
+        os.environ['LC_NUMERIC'] = 'C'
+
+        try:
+            locale.setlocale(locale.LC_ALL, '')
+        except locale.Error:
+            pass
+
+        # XXX this is the sort of thing which you should never do,
+        # but we switch languages at runtime and thus need to invalidate
+        # the set of languages/mofiles which gettext knows about
+        gettext._translations = {}
+
+        # XXX DEBUG
+        print 'set install lang to "%s"' % self.install_lang
+
+    def set_system_lang(self, langcode):
+        self.system_lang = langcode
+
+        # XXX DEBUG
+        print 'set system lang to "%s"' % self.system_lang
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py
index c5d0e1d..17ec719 100644
--- a/pyanaconda/ui/gui/spokes/welcome.py
+++ b/pyanaconda/ui/gui/spokes/welcome.py
@@ -23,6 +23,8 @@ from gi.repository import AnacondaWidgets, Gtk
 from pyanaconda.ui.gui.hubs.summary import SummaryHub
 from pyanaconda.ui.gui.spokes import StandaloneSpoke
 
+from pyanaconda.localization import Language, LOCALE_PREFERENCES
+
 __all__ = ["WelcomeLanguageSpoke"]
 
 class WelcomeLanguageSpoke(StandaloneSpoke):
@@ -36,7 +38,10 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
         selected = self.builder.get_object("languageViewSelection")
         (store, itr) = selected.get_selected()
 
-        self.data.lang.lang = store[itr][2]
+        lang = store[itr][2]
+        self.language.select_translation(lang)
+
+        self.data.lang.lang = lang
 
     def populate(self):
         StandaloneSpoke.populate(self)
@@ -47,26 +52,39 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
         completion.set_text_column(1)
 
         store = self.builder.get_object("languageStore")
-        self._addLanguage(store, "English", "English", "en_US")
-        self._addLanguage(store, "Language A", "Language A", "C")
-        self._addLanguage(store, "Language B", "Language B", "C")
-        self._addLanguage(store, "Language C", "Language C", "C")
-        self._addLanguage(store, "Language D", "Language D", "C")
-        self._addLanguage(store, "Language E", "Language E", "C")
-        self._addLanguage(store, "Language F", "Language F", "C")
-        self._addLanguage(store, "Language G", "Language G", "C")
-        self._addLanguage(store, "Language H", "Language H", "C")
-        self._addLanguage(store, "Language I", "Language I", "C")
-        self._addLanguage(store, "Language J", "Language J", "C")
-        self._addLanguage(store, "Language K", "Language K", "C")
+
+        # TODO We can use the territory from geoip here
+        # to preselect the translation, when it's available.
+        # Until then, use None.
+        territory = None
+        self.language = Language(LOCALE_PREFERENCES, territory=territory)
+
+        # fill the list with available translations
+        for _code, trans in sorted(self.language.translations.items()):
+            self._addLanguage(store, trans.display_name,
+                              trans.english_name, trans.short_name)
+
+        # select the preferred translation
+        self._selectLanguage(store, self.language.preferred_translation.short_name)
 
     def setup(self):
         StandaloneSpoke.setup(self)
-        self.window.set_may_continue(False)
+        #self.window.set_may_continue(False)
 
     def _addLanguage(self, store, native, english, setting):
         store.append([native, english, setting])
 
+    def _selectLanguage(self, store, language):
+        itr = store.get_iter_first()
+        while itr and store[itr][2] != language:
+            itr = store.iter_next(itr)
+
+        treeview = self.builder.get_object("languageView")
+        selection = treeview.get_selection()
+        selection.select_iter(itr)
+        path = store.get_path(itr)
+        treeview.scroll_to_cell(path)
+
     # Signal handlers.
     def clearLanguageEntry(self, entry, icon_pos, event):
         if icon_pos == Gtk.EntryIconPosition.SECONDARY:
@@ -76,6 +94,12 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
         (store, selected) = selection.get_selected_rows()
         self.window.set_may_continue(len(selected) > 0)
 
+        if selected:
+            lang = store[selected[0]][2]
+            self.language.set_install_lang(lang)
+            self.language.set_system_lang(lang)
+            # TODO reload the whole window so it gets translated
+
     # Override the default in StandaloneSpoke so we can display the beta
     # warning dialog first.
     def _on_continue_clicked(self, cb):
diff --git a/pyanaconda/ui/gui/spokes/welcome.ui b/pyanaconda/ui/gui/spokes/welcome.ui
index b1894d5..d6cacd7 100644
--- a/pyanaconda/ui/gui/spokes/welcome.ui
+++ b/pyanaconda/ui/gui/spokes/welcome.ui
@@ -242,7 +242,9 @@ OS you can rely on.  It's for testing purposes only.</property>
                         <property name="headers_visible">False</property>
                         <property name="search_column">0</property>
                         <child internal-child="selection">
-                          <object class="GtkTreeSelection" id="treeview-selection"/>
+                          <object class="GtkTreeSelection" id="treeview-selection">
+                            <signal name="changed" handler="on_selection_changed" swapped="no"/>
+                          </object>
                         </child>
                         <child>
                           <object class="GtkTreeViewColumn" id="nativeName">
-- 
1.7.5.4

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list


[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux