Boolean fields choices not localized

Issue #10 new
nolnol NA
created an issue

Seems that if you use a boolean field as a filter, you only get "True" and "False" as choices value, whatever the current language used in your app. Obviously, I'd like this to be displayed in Spanish if Spanish is the current language.

True / False strings don't appear when I use the "makemessages" command and I don't know how to hack the render_choice_object() method to fix this.

Any idea?

Comments (6)

  1. Luke Plant repo owner

    The default logic in render_choice_object just does unicode() or str() on the internal value, which is why you get "True" or "False".

    The simple fix is to provide your own Field subclass with render_choice_object() implemented to special case "True" and "False" - it shouldn't be hard.

    I don't think it is worth building in this i18n functionality into the app, because it is not obvious how to translate "True" and "False" - even in English there would often be multiple alternatives, e.g. "Yes"/"No", "Enabled"/"Disabled" depending on the field.

  2. nolnol NA reporter

    Thanks for your quick response: much appreciated!

    I agree with you that it ain't obvious how to translate "True/False" in a good way, and will try the option you mentioned, that is to use a dedicated render_choice_object().

    However, as I'll still need to manage a way to display the choices differently, depending on the language used (in my case English, Spanish, French), I'll need to add a new parameter to handle the langue code. Thinking of something like below, but i'm afraid of side effects as it would surely impact more code than I can imagine...

        personsfilter.fields = [
            #(field name, options, Custom Filter Class, language_code),
            ('celebrity', dict(order_by_count=False), CustomFilter, 'es'),

    Any idea on the way to pass the language_code easily to the CustomFilter / render_choice_object()?

  3. Luke Plant repo owner

    Can't you put the language as a parameter into the 2nd argument - the dictionary? It will require adding a new kwarg to the subclass __init__

    I'm thinking a good solution, which could go into core, would be something like:

    class MappedValuesFilter(ValuesFilter):
        def __init__(self, *args, **kwargs):
            self.mapping = kwargs.pop('mapping')
            super(MappedValuesFilter, self).__init__(*args, **kwargs)
        def render_choice_object(self, choice):
                return self.mapping[choice]
            except KeyError:
                return super(MappedValuesFilter, self).render_choice_object(choice)

    You would then passing a 'mapping' kwarg to the dict, like

    personsfilter.fields = [
            ('celebrity', dict(order_by_count=False, mapping={True: "yes", False: "no"), MappedValuesFilter),

    Of course, instead of "yes" etc you can use "si" or ugettext_lazy("yes"), so you can get runtime translation if you want.

    I haven't tested the above code, but I think it should work. It seems generic enough to go in django-easyfilters itself. If you provided a patch that properly implemented it (preferably with tests, and definitely needs docs), I would definitely merge it.


  4. nolnol NA reporter

    thanks :-) Sounds interesting.

    As I'm not very skilled in coding, it'll take me some days/weeks for a first try and I guess I won't provide tests (never done yet): don't expect result soon...

    I'm also thinking of merging this with a way to use "display_field" parameter for ForeignKey and ManyToMany fields. I indeed hardcoded my translations inside models, and will need to specifiy the field to be displayed as choice.

    class Profession(models.Model):
        name_en = models.CharField()    # name in English
        name_es = models.CharField()    # name in Spanish
        name_fr = models.CharField()     # name in French
    class Person(models.Model):
        gender = models.ForeignKey(Gender)
        first_name = models.CharField()
        first_name = models.CharField()
        profession = models.ForeignKey(Profession)

    Following your idea, the result could be something like:

    personsfilter.fields = [
            ('profession', dict(order_by_count=False, display_field='name_es', MappedValuesFilter),

    I'll keep you updated.

  5. Luke Plant repo owner

    Hmm, interesting.

    Thinking about it a bit more, I think this is a feature that would be useful for all filters. Essentially, you want an easy way to override what is displayed for any value. You can always subclass and implement 'render_choice_object' to do that, but that is a heavyweight solution.

    A nicer solution would be to provide a parameter that is a callable to be used instead of the just doing 'unicode' (which is what Filter does at the moment).

    So, I'm thinking of an API that would look like this:

    def my_profession_renderer(filter, choice):
        return profession.name_es
    personsfilter.fields = [
        ('profession', dict(order_by_count=False, render_using=my_profession_renderer))

    This API would be simple enough to use inline i.e. using a lambda

    personsfilter.fields = [
        ('profession', dict(order_by_count=False,
                                render_using=lambda filter, choice: choice.name_es))

    That is only slightly more bulky that your 'display_field', but would be much more general.

    So, I reckon this should be implemented as a parameter to the base Filter class.

  6. Luke Plant repo owner

    BTW, I also wanted to say that it's fine to take a while over this. I hope this doesn't sound patronising, but writing patches for existing open source projects is an excellent way to get better at programming. It's how I started, and the feedback I got on my patches was vital in honing my skills. It was that experience that eventually enabled me to get on the Django core team pretty quickly. So I would encourage you to keep going, even though it will be slow initially!

  7. Log in to comment