Commit 03c14040 authored by Michal Čihař's avatar Michal Čihař

Sort languages alphabetically in prefs (issue #204)

Uses PyICU if available, fallback to normal cmp in case it is not.
parent 6813bc06
...@@ -19,13 +19,81 @@ ...@@ -19,13 +19,81 @@
# #
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _, get_language
from accounts.models import Profile from accounts.models import Profile
from lang.models import Language from lang.models import Language
from trans.models import Project from trans.models import Project
from django.contrib.auth.models import User from django.contrib.auth.models import User
from registration.forms import RegistrationFormUniqueEmail from registration.forms import RegistrationFormUniqueEmail
from django.utils.encoding import force_unicode
from itertools import chain
try:
from icu import Locale, Collator
HAS_ICU = True
except ImportError:
HAS_ICU = False
def sort_choices(choices):
'''
Sorts choices alphabetically.
Either using cmp or ICU.
'''
if not HAS_ICU:
sorter = cmp
else:
sorter = Collator.createInstance(Locale(get_language())).compare
# Actually sort values
return sorted(
choices,
key=lambda tup: tup[1],
cmp=sorter
)
class SortedSelectMixin(object):
'''
Mixin for Select widgets to sort choices alphabetically.
'''
def render_options(self, choices, selected_choices):
'''
Renders sorted options.
'''
# Normalize to strings.
selected_choices = set(force_unicode(v) for v in selected_choices)
output = []
# Actually sort values
all_choices = sort_choices(list(chain(self.choices, choices)))
# Stolen from Select.render_options
for option_value, option_label in all_choices:
if isinstance(option_label, (list, tuple)):
output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value)))
for option in option_label:
output.append(self.render_option(selected_choices, *option))
output.append(u'</optgroup>')
else:
output.append(self.render_option(selected_choices, option_value, option_label))
return u'\n'.join(output)
class SortedSelectMultiple(SortedSelectMixin, forms.SelectMultiple):
'''
Wrapper class to sort choices alphabetically.
'''
pass
class SortedSelect(SortedSelectMixin, forms.Select):
'''
Wrapper class to sort choices alphabetically.
'''
pass
class ProfileForm(forms.ModelForm): class ProfileForm(forms.ModelForm):
...@@ -39,6 +107,11 @@ class ProfileForm(forms.ModelForm): ...@@ -39,6 +107,11 @@ class ProfileForm(forms.ModelForm):
'languages', 'languages',
'secondary_languages', 'secondary_languages',
) )
widgets = {
'language': SortedSelect,
'languages': SortedSelectMultiple,
'secondary_languages': SortedSelectMultiple,
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs) super(ProfileForm, self).__init__(*args, **kwargs)
......
...@@ -28,6 +28,8 @@ south ...@@ -28,6 +28,8 @@ south
http://south.aeracode.org/ http://south.aeracode.org/
libravatar (optional for federated avatar support) libravatar (optional for federated avatar support)
https://pypi.python.org/pypi/pyLibravatar https://pypi.python.org/pypi/pyLibravatar
PyICU (optional for proper sorting of strings)
https://pypi.python.org/pypi/PyICU
Database backend Database backend
Any database supported in Django will work, check their documentation for more details. Any database supported in Django will work, check their documentation for more details.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment