def set_language(self, language): # Support both iconview and treeview if self.only: model = self.iconview.get_model() iterator = model.iter_children(None) while iterator is not None: if misc.utf8(model.get_value(iterator, 0)) == language: path = model.get_path(iterator) self.iconview.select_path(path) self.iconview.scroll_to_path(path, True, 0.5, 0.5) break iterator = model.iter_next(iterator) else: model = self.treeview.get_model() iterator = model.iter_children(None) while iterator is not None: if misc.utf8(model.get_value(iterator, 0)) == language: path = model.get_path(iterator) self.treeview.get_selection().select_path(path) self.treeview.scroll_to_cell(path, use_align=True, row_align=0.5) break iterator = model.iter_next(iterator) if not self.only and 'UBIQUITY_GREETER' in os.environ: self.try_ubuntu.set_sensitive(True) self.install_ubuntu.set_sensitive(True)
def run(self, priority, question): if self.done: return self.succeeded if question == 'keyboard-configuration/layout': # TODO cjwatson 2006-09-07: no keyboard-configuration support # for layout choice translation yet self.ui.set_keyboard_choices(self.choices_untranslated(question)) self.ui.set_keyboard(misc.utf8(self.db.get(question))) # Reset these in case we just backed up from the variant # question. self.store_defaults(True) self.has_variants = False self.succeeded = True return True elif question in ('keyboard-configuration/variant', 'keyboard-configuration/altgr'): if question == 'keyboard-configuration/altgr': if self.has_variants: return True else: # If there's only one variant, it is always the same as # the layout name. single_variant = misc.utf8( self.db.get('keyboard-configuration/layout')) self.ui.set_keyboard_variant_choices([single_variant]) self.ui.set_keyboard_variant(single_variant) else: # TODO cjwatson 2006-10-02: no keyboard-configuration # support for variant choice translation yet self.has_variants = True self.ui.set_keyboard_variant_choices( self.choices_untranslated(question)) self.ui.set_keyboard_variant(misc.utf8(self.db.get(question))) # keyboard-configuration preseeding is special, and needs to be # checked by hand. The seen flag on # keyboard-configuration/layout is used internally by # keyboard-configuration, so we can't just force it to true. if (self.is_automatic and self.db.fget( 'keyboard-configuration/layoutcode', 'seen') == 'true'): return True else: return plugin.Plugin.run(self, priority, question) elif question == 'keyboard-configuration/model': # Backing up from the variant question inconveniently goes back # to the model question. Catch this and go forward again so # that we can reach the layout question. return True elif question.startswith('keyboard-configuration/unsupported_'): response = self.frontend.question_dialog( self.description(question), self.extended_description(question), ('ubiquity/imported/yes', 'ubiquity/imported/no')) if response == 'ubiquity/imported/yes': self.preseed(question, 'true') else: self.preseed(question, 'false') return True else: return True
def set_language(self, language): # Support both iconview and treeview if self.only: model = self.iconview.get_model() iterator = model.iter_children(None) while iterator is not None: if misc.utf8(model.get_value(iterator, 0)) == language: path = model.get_path(iterator) self.iconview.select_path(path) self.iconview.scroll_to_path(path, True, 0.5, 0.5) break iterator = model.iter_next(iterator) else: model = self.treeview.get_model() iterator = model.iter_children(None) while iterator is not None: if misc.utf8(model.get_value(iterator, 0)) == language: path = model.get_path(iterator) self.treeview.get_selection().select_path(path) self.treeview.scroll_to_cell( path, use_align=True, row_align=0.5) break iterator = model.iter_next(iterator) if not self.only and 'UBIQUITY_GREETER' in os.environ: self.try_ubuntu.set_sensitive(True) self.install_ubuntu.set_sensitive(True)
def set_keyboard(self, layout): index = self.page.keyboard_layout_combobox.findText(misc.utf8(layout)) if index > -1: self.page.keyboard_layout_combobox.setCurrentIndex(index) if self.keyboardDisplay: lang = 'C' ly = keyboard_names.layout_id(lang, misc.utf8(layout)) self.keyboardDisplay.setLayout(ly)
def set_keyboard(self, layout): index = self.page.keyboard_layout_combobox.findText(misc.utf8(layout)) if index > -1: self.page.keyboard_layout_combobox.setCurrentIndex(index) if self.keyboardDisplay: l = self.controller.dbfilter.get_locale() ly = keyboard_names.layout_id(l, misc.utf8(layout)) self.keyboardDisplay.setLayout(ly)
def get_string(name, lang, prefix=None): """Get the translation of a single string.""" question = map_widget_name(prefix, name) translations = get_translations() if question not in translations: return None if lang is None: lang = 'c' else: lang = lang.lower() if name in string_extended: lang = 'extended:%s' % lang if lang in translations[question]: text = translations[question][lang] else: ll_cc = lang.split('.')[0] ll = ll_cc.split('_')[0] if ll_cc in translations[question]: text = translations[question][ll_cc] elif ll in translations[question]: text = translations[question][ll] elif lang.startswith('extended:'): text = translations[question]['extended:c'] else: text = translations[question]['c'] return misc.utf8(text, errors='replace')
def set_keyboard_choices(self, choices): self.page.keyboard_layout_combobox.clear() for choice in sorted(choices): self.page.keyboard_layout_combobox.addItem(misc.utf8(choice)) if self.current_layout is not None: self.set_keyboard(self.current_layout)
def get_keyboard_variant(self): selection = self.keyboardvariantview.get_selection() (model, iterator) = selection.get_selected() if iterator is None: return None else: return misc.utf8(model.get_value(iterator, 0))
def changed(self, entry): from gi.repository import Gtk, GObject, GLib, Soup text = misc.utf8(self.city_entry.get_text()) if not text: return # TODO if the completion widget has a selection, return? How do we # determine this? if text in self.geoname_cache: model = self.geoname_cache[text] self.city_entry.get_completion().set_model(model) else: model = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING) if self.geoname_session is None: self.geoname_session = Soup.SessionAsync() url = _geoname_url % (quote(text), misc.get_release().version) message = Soup.Message.new('GET', url) message.request_headers.append('User-agent', 'Ubiquity/1.0') self.geoname_session.abort() if self.geoname_timeout_id is not None: GLib.source_remove(self.geoname_timeout_id) self.geoname_timeout_id = \ GLib.timeout_add_seconds(2, self.geoname_timeout, (text, model)) self.geoname_session.queue_message(message, self.geoname_cb, (text, model))
def set_keyboard_variant(self, variant): index = self.page.keyboard_variant_combobox.findText( misc.utf8(variant)) if index > -1: self.page.keyboard_variant_combobox.setCurrentIndex(index) if self.keyboardDisplay: l = self.controller.dbfilter.get_locale() layout = keyboard_names.layout_id(l, self.get_keyboard()) if variant: try: var = keyboard_names.variant_id( l, layout, misc.utf8(variant)) except KeyError: var = None self.keyboardDisplay.setVariant(var)
def on_keyboard_layout_selected(self, *args): layout = self.get_keyboard() lang = self.controller.dbfilter.get_locale() if layout is not None: # skip updating keyboard if not using display if self.keyboardDisplay: try: ly = keyboard_names.layout_id(lang, misc.utf8(layout)) except KeyError: ly = keyboard_names.layout_id('C', misc.utf8(layout)) self.keyboardDisplay.setLayout(ly) # no variants, force update by setting none # if not keyboard_names.has_variants(l, ly): # self.keyboardDisplay.setVariant(None) self.current_layout = layout self.controller.dbfilter.change_layout(layout)
def set_keyboard_variant(self, variant): index = self.page.keyboard_variant_combobox.findText( misc.utf8(variant)) if index > -1: self.page.keyboard_variant_combobox.setCurrentIndex(index) if self.keyboardDisplay: lang = 'C' layout = keyboard_names.layout_id(lang, self.get_keyboard()) if variant: try: var = keyboard_names.variant_id( lang, layout, misc.utf8(variant)) except KeyError: var = None self.keyboardDisplay.setVariant(var)
def set_keyboard_variant(self, variant): index = self.page.keyboard_variant_combobox.findText( misc.utf8(variant)) if index > -1: self.page.keyboard_variant_combobox.setCurrentIndex(index) if self.keyboardDisplay: lang = 'C' layout = keyboard_names.layout_id(lang, self.get_keyboard()) if variant: try: var = keyboard_names.variant_id(lang, layout, misc.utf8(variant)) except KeyError: var = None self.keyboardDisplay.setVariant(var)
def default_locales(): with open('/usr/lib/ubiquity/localechooser/languagelist') as languagelist: defaults = {} for line in languagelist: line = misc.utf8(line) if line == '' or line == '\n': continue bits = line.strip('\n').split(';') code = bits[0] locale = bits[4] defaults[code] = locale return defaults
def check_hostname(hostname): """Returns a list of reasons why the hostname is invalid.""" errors = [] for result in validation.check_hostname(misc.utf8(hostname)): if result == validation.HOSTNAME_LENGTH: errors.append('hostname_error_length') elif result == validation.HOSTNAME_BADCHAR: errors.append('hostname_error_badchar') elif result == validation.HOSTNAME_BADHYPHEN: errors.append('hostname_error_badhyphen') elif result == validation.HOSTNAME_BADDOTS: errors.append('hostname_error_baddots') return errors
def set_keyboard_variant(self, variant): model = self.keyboardvariantview.get_model() if model is None: return iterator = model.iter_children(None) while iterator is not None: if misc.utf8(model.get_value(iterator, 0)) == variant: path = model.get_path(iterator) self.keyboardvariantview.get_selection().select_path(path) self.keyboardvariantview.scroll_to_cell( path, use_align=True, row_align=0.5) break iterator = model.iter_next(iterator)
def set_keyboard(self, layout): self.current_layout = layout model = self.keyboardlayoutview.get_model() if model is None: return iterator = model.iter_children(None) while iterator is not None: if misc.utf8(model.get_value(iterator, 0)) == layout: path = model.get_path(iterator) selection = self.keyboardlayoutview.get_selection() if not selection.path_is_selected(path): selection.select_path(path) self.keyboardlayoutview.scroll_to_cell( path, use_align=True, row_align=0.5) break iterator = model.iter_next(iterator)
def get_language(self): # Support both iconview and treeview if self.only: model = self.iconview.get_model() items = self.iconview.get_selected_items() if not items: return None iterator = model.get_iter(items[0]) else: selection = self.treeview.get_selection() (model, iterator) = selection.get_selected() if iterator is None: return None else: value = misc.utf8(model.get_value(iterator, 0)) return self.language_choice_map[value][1]
def on_keyboard_variant_selected(self, *args): layout = self.get_keyboard() variant = self.get_keyboard_variant() if self.keyboardDisplay: var = None l = self.controller.dbfilter.get_locale() ly = keyboard_names.layout_id(l, layout) if variant: try: var = keyboard_names.variant_id(l, ly, misc.utf8(variant)) except KeyError: var = None self.keyboardDisplay.setVariant(var) if layout is not None and variant is not None: self.controller.dbfilter.apply_keyboard(layout, variant)
def set_fullname(self, value): self.page.fullname.setText(misc.utf8(value))
def get_languages(current_language_index=-1, only_installable=False): import gzip import icu current_language = "中文(简体)" if only_installable: from apt.cache import Cache #workaround for an issue where euid != uid and the #apt cache has not yet been loaded causing a SystemError #when libapt-pkg tries to load the Cache the first time. with misc.raised_privileges(): cache = Cache() languagelist = gzip.open( '/usr/lib/ubiquity/localechooser/languagelist.data.gz') # language_display_map = {} language_display_map = collections.OrderedDict() i = 0 for line in languagelist: line = misc.utf8(line) if line == '' or line == '\n': continue code, name, trans = line.strip('\n').split(':')[1:] if code in ('C', 'dz', 'km'): i += 1 continue # KDE fails to round-trip strings containing U+FEFF ZERO WIDTH # NO-BREAK SPACE, and we don't care about the NBSP anyway, so strip # it. # https://bugs.launchpad.net/bugs/1001542 # (comment #5 and on) trans = trans.strip(" \ufeff") if only_installable: pkg_name = 'language-pack-%s' % code #special case these if pkg_name.endswith('_CN'): pkg_name = 'language-pack-zh-hans' elif pkg_name.endswith('_TW'): pkg_name = 'language-pack-zh-hant' elif pkg_name.endswith('_NO'): pkg_name = pkg_name.split('_NO')[0] elif pkg_name.endswith('_BR'): pkg_name = pkg_name.split('_BR')[0] try: pkg = cache[pkg_name] if not (pkg.installed or pkg.candidate): i += 1 continue except KeyError: i += 1 continue language_display_map[trans] = (name, code) if i == current_language_index: current_language = trans i += 1 languagelist.close() if only_installable: del cache try: # Note that we always collate with the 'C' locale. This is far # from ideal. But proper collation always requires a specific # language for its collation rules (languages frequently have # custom sorting). This at least gives us common sorting rules, # like stripping accents. collator = icu.Collator.createInstance(icu.Locale('C')) except: collator = None def compare_choice(x): if language_display_map[x][1] == 'C': return None # place C first if collator: try: return collator.getCollationKey(x).getByteArray() except: pass # Else sort by unicode code point, which isn't ideal either, # but also has the virtue of sorting like-glyphs together return x # sorted_choices = sorted(language_display_map, reverse=True, key=compare_choice) # sorted_choices = language_display_map.keys() sorted_choices=[] for k, v in language_display_map.items(): sorted_choices.append(k) return current_language, sorted_choices, language_display_map
def set_username(self, value): self.page.username.setText(misc.utf8(value))
def run(self, priority, question): if self.done: return self.succeeded if question == 'keyboard-configuration/layout': # TODO cjwatson 2006-09-07: no keyboard-configuration support # for layout choice translation yet self.ui.set_keyboard_choices( self.choices_untranslated(question)) self.ui.set_keyboard(misc.utf8(self.db.get(question))) # Reset these in case we just backed up from the variant # question. self.store_defaults(True) self.has_variants = False self.succeeded = True return True elif question in ('keyboard-configuration/variant', 'keyboard-configuration/altgr'): if question == 'keyboard-configuration/altgr': if self.has_variants: return True else: # If there's only one variant, it is always the same as # the layout name. single_variant = misc.utf8(self.db.get( 'keyboard-configuration/layout')) self.ui.set_keyboard_variant_choices([single_variant]) self.ui.set_keyboard_variant(single_variant) else: # TODO cjwatson 2006-10-02: no keyboard-configuration # support for variant choice translation yet self.has_variants = True self.ui.set_keyboard_variant_choices( self.choices_untranslated(question)) self.ui.set_keyboard_variant(misc.utf8(self.db.get(question))) # keyboard-configuration preseeding is special, and needs to be # checked by hand. The seen flag on # keyboard-configuration/layout is used internally by # keyboard-configuration, so we can't just force it to true. if (self.is_automatic and self.db.fget( 'keyboard-configuration/layoutcode', 'seen') == 'true'): return True else: return plugin.Plugin.run(self, priority, question) elif question == 'keyboard-configuration/model': # Backing up from the variant question inconveniently goes back # to the model question. Catch this and go forward again so # that we can reach the layout question. return True elif question.startswith('keyboard-configuration/unsupported_'): response = self.frontend.question_dialog( self.description(question), self.extended_description(question), ('ubiquity/imported/yes', 'ubiquity/imported/no')) if response == 'ubiquity/imported/yes': self.preseed(question, 'true') else: self.preseed(question, 'false') return True else: return True
def extended_description(self, question): return misc.utf8(self.db.metaget(question, 'extended_description'), errors='replace')
def choices(self, question): choices = misc.utf8(self.db.metaget(question, 'choices'), errors='replace') return self.split_choices(choices)
def set_keyboard_variant_choices(self, choices): self.page.keyboard_variant_combobox.clear() for choice in sorted(choices): self.page.keyboard_variant_combobox.addItem(misc.utf8(choice))
def extended_description(self, question): return misc.utf8(self.db.metaget(question, "extended_description"), errors="replace")
def get_languages(current_language_index=-1, only_installable=False): import gzip import icu current_language = "English" if only_installable: from apt.cache import Cache #workaround for an issue where euid != uid and the #apt cache has not yet been loaded causing a SystemError #when libapt-pkg tries to load the Cache the first time. with misc.raised_privileges(): cache = Cache() languagelist = gzip.open('/usr/lib/ubiquity/localechooser/languagelist.data.gz') language_display_map = {} i = 0 for line in languagelist: line = misc.utf8(line) if line == '' or line == '\n': continue code, name, trans = line.strip(u'\n').split(u':')[1:] if code in ('C', 'dz', 'km'): i += 1 continue if only_installable: pkg_name = 'language-pack-%s' % code #special case these if pkg_name.endswith('_CN'): pkg_name = 'language-pack-zh-hans' elif pkg_name.endswith('_TW'): pkg_name = 'language-pack-zh-hant' elif pkg_name.endswith('_NO'): pkg_name = pkg_name.split('_NO')[0] elif pkg_name.endswith('_BR'): pkg_name = pkg_name.split('_BR')[0] try: pkg = cache[pkg_name] if not (pkg.installed or pkg.candidate): i += 1 continue except KeyError: i += 1 continue language_display_map[trans] = (name, code) if i == current_language_index: current_language = trans i += 1 languagelist.close() if only_installable: del cache try: # Note that we always collate with the 'C' locale. This is far # from ideal. But proper collation always requires a specific # language for its collation rules (languages frequently have # custom sorting). This at least gives us common sorting rules, # like stripping accents. collator = icu.Collator.createInstance(icu.Locale('C')) except: collator = None def compare_choice(x): if language_display_map[x][1] == 'C': return None # place C first if collator: try: return collator.getCollationKey(x).getByteArray() except: pass # Else sort by unicode code point, which isn't ideal either, # but also has the virtue of sorting like-glyphs together return x sorted_choices = sorted(language_display_map, key=compare_choice) return current_language, sorted_choices, language_display_map
def decode_ssid(characters): ssid = ''.join([chr(int(char)) for char in characters]) return utf8(ssid, errors='replace')
def choices(self, question): choices = misc.utf8(self.db.metaget(question, "choices"), errors="replace") return self.split_choices(choices)
def info_loop(self, widget): """check if all entries from Identification screen are filled. Callback defined in ui file.""" if (self.username_changed_id is None or self.hostname_changed_id is None): return if (widget is not None and widget.get_name() == 'fullname' and not self.username_edited): self.username.handler_block(self.username_changed_id) new_username = misc.utf8(widget.get_text().split(' ')[0]) new_username = new_username.encode('ascii', 'ascii_transliterate') new_username = new_username.decode().lower() new_username = re.sub('^[^a-z]+', '', new_username) new_username = re.sub('[^-a-z0-9_]', '', new_username) self.username.set_text(new_username) self.username.handler_unblock(self.username_changed_id) elif (widget is not None and widget.get_name() == 'username' and not self.hostname_edited): self.hostname.handler_block(self.hostname_changed_id) t = widget.get_text() if t: self.hostname.set_text(re.sub(r'\W', '', t) + self.suffix) self.hostname.handler_unblock(self.hostname_changed_id) # Do some initial validation. We have to process all the widgets so we # can know if we can really show the next button. Otherwise we'd show # it on any field being valid. complete = True if self.fullname.get_text(): self.fullname_ok.show() else: self.fullname_ok.hide() text = self.username.get_text() if text: errors = check_username(text) if errors: self.username_error(make_error_string(self.controller, errors)) complete = False else: self.username_ok.show() self.username_error_label.hide() else: self.username_ok.hide() self.username_error_label.hide() complete = False password_ok = validation.gtk_password_validate( self.controller, self.password, self.verified_password, self.password_ok, self.password_error_label, self.password_strength, self.allow_password_empty, ) complete = complete and password_ok txt = self.hostname.get_text() self.hostname_ok.show() if txt: errors = check_hostname(txt) if errors: self.hostname_error(make_error_string(self.controller, errors)) complete = False self.hostname_ok.hide() else: self.hostname_ok.show() self.hostname_error_label.hide() else: complete = False self.hostname_ok.hide() self.hostname_error_label.hide() self.controller.allow_go_forward(complete)
def test_description(self): question = 'partman-auto/init_automatically_partition' description = misc.utf8(self.page.db.metaget(question, 'description'), 'replace') self.assertEqual(self.page.description(question), description) self.assertIn(question, self.page.description_cache)
def info_loop(self, widget): """check if all entries from Identification screen are filled. Callback defined in ui file.""" if (self.username_changed_id is None or self.hostname_changed_id is None): return if (widget is not None and widget.get_name() == 'fullname' and not self.username_edited): self.username.handler_block(self.username_changed_id) new_username = misc.utf8(widget.get_text().split(' ')[0]) new_username = new_username.encode('ascii', 'ascii_transliterate') new_username = new_username.lower() new_username = re.sub('^[^a-z]+', '', new_username) new_username = re.sub('[^-a-z0-9_]', '', new_username) self.username.set_text(new_username) self.username.handler_unblock(self.username_changed_id) elif (widget is not None and widget.get_name() == 'username' and not self.hostname_edited): self.hostname.handler_block(self.hostname_changed_id) t = widget.get_text() if t: self.hostname.set_text(re.sub(r'\W', '', t) + self.suffix) self.hostname.handler_unblock(self.hostname_changed_id) # Do some initial validation. We have to process all the widgets so we # can know if we can really show the next button. Otherwise we'd show # it on any field being valid. complete = True if self.fullname.get_text(): self.fullname_ok.show() else: self.fullname_ok.hide() text = self.username.get_text() if text: errors = check_username(text) if errors: self.username_error(make_error_string(self.controller, errors)) complete = False else: self.username_ok.show() self.username_error_label.hide() else: self.username_ok.hide() self.username_error_label.hide() complete = False passw = self.password.get_text() vpassw = self.verified_password.get_text() allow_empty = self.allow_password_empty if passw != vpassw: complete = False self.password_ok.hide() if passw and (len(vpassw) / float(len(passw)) > 0.8): # TODO Cache, use a custom string. txt = self.controller.get_string('ubiquity/text/password_mismatch') txt = '<small><span foreground="darkred"><b>%s</b></span></small>' % txt self.password_error_label.set_markup(txt) self.password_error_label.show() else: self.password_error_label.hide() if allow_empty: self.password_strength.hide() elif not passw: self.password_strength.hide() complete = False else: (txt, color) = validation.human_password_strength(passw) # TODO Cache txt = self.controller.get_string('ubiquity/text/password/' + txt) txt = '<small><span foreground="%s"><b>%s</b></span></small>' \ % (color, txt) self.password_strength.set_markup(txt) self.password_strength.show() if passw == vpassw: self.password_ok.show() txt = self.hostname.get_text() self.hostname_ok.show() if txt: errors = check_hostname(txt) if errors: self.hostname_error(make_error_string(self.controller, errors)) complete = False self.hostname_ok.hide() else: self.hostname_ok.show() self.hostname_error_label.hide() else: complete = False self.hostname_ok.hide() self.hostname_error_label.hide() self.controller.allow_go_forward(complete)
def get_languages(current_language_index=-1, only_installable=False): import gzip import icu current_language = "English" if only_installable: from apt.cache import Cache # workaround for an issue where euid != uid and the # apt cache has not yet been loaded causing a SystemError # when libapt-pkg tries to load the Cache the first time. with misc.raised_privileges(): cache = Cache() languagelist = gzip.open( '/usr/lib/ubiquity/localechooser/languagelist.data.gz') language_display_map = {} i = 0 for line in languagelist: line = misc.utf8(line) if line == '' or line == '\n': continue code, name, trans = line.strip('\n').split(':')[1:] if code in ('C', 'dz', 'km'): i += 1 continue # KDE fails to round-trip strings containing U+FEFF ZERO WIDTH # NO-BREAK SPACE, and we don't care about the NBSP anyway, so strip # it. # https://bugs.launchpad.net/bugs/1001542 # (comment # 5 and on) trans = trans.strip(" \ufeff") if only_installable: pkg_name = 'language-pack-%s' % code # special case these if pkg_name.endswith('_CN'): pkg_name = 'language-pack-zh-hans' elif pkg_name.endswith('_TW'): pkg_name = 'language-pack-zh-hant' elif pkg_name.endswith('_NO'): pkg_name = pkg_name.split('_NO')[0] elif pkg_name.endswith('_BR'): pkg_name = pkg_name.split('_BR')[0] try: pkg = cache[pkg_name] if not (pkg.installed or pkg.candidate): i += 1 continue except KeyError: i += 1 continue language_display_map[trans] = (name, code) if i == current_language_index: current_language = trans i += 1 languagelist.close() if only_installable: del cache try: # Note that we always collate with the 'C' locale. This is far # from ideal. But proper collation always requires a specific # language for its collation rules (languages frequently have # custom sorting). This at least gives us common sorting rules, # like stripping accents. collator = icu.Collator.createInstance(icu.Locale('C')) except: collator = None def compare_choice(x): if language_display_map[x][1] == 'C': return None # place C first if collator: try: return collator.getCollationKey(x).getByteArray() except: pass # Else sort by unicode code point, which isn't ideal either, # but also has the virtue of sorting like-glyphs together return x sorted_choices = sorted(language_display_map, key=compare_choice) return current_language, sorted_choices, language_display_map