How to translate slug with django-modeltranslation

I have been to a django conference this morning (DjangoCong / @djangocong) , and a talk was about django-modeltranslation. At the end of the presentation, someone ask the question: “How can I translate my slugs with this app ?”. I remembered I used this app and I do that but I could’nt remember HOW ! So, here is how I did that, I think it might be tricky but it worked in my case:

I’ll pass about how to use the app, there is a good documentation on the author site but just remember that the app add “_lang” (i.e: _en, _fr…) at the end of each translated attribute. 

So, let’s begin:

First make your model with a slug (or add to you existing model), I choose to describe a product :


#File models.py

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.template.defaultfilters import slugify

class Product(Model):
    name = models.CharField(max_length=200, help_text=_(u"Name of the product"))
    slug = AutoSlugField(_('slug'), populate_from='name', overwrite=True)

    class Meta:
        app_label = "my_app"
        verbose_name = _(u"Product") 
        verbose_name_plural = _(u"Products") 

 

Then in your translation.py, say that “slug” has to be translated:


#File translation.py
from modeltranslation.translator import translator, TranslationOptions

from my_app.models import Product

class ProductTranslationOptions(TranslationOptions):
    fields = ('name', 'slug')

translator.register(Product, ProductTranslationOptions)

 

So by now, your slug can be translated but it is never filled and I don’t want my clients to fill this information. The populate_from could’nt work with translations and I don’t want to translate myself each time someone added a Product. So I overrode the save of Product using the translated “name” (which is done by the person who add the Product).

I defined also “get_absolute_url” which we’ll talk later.


#File models.py

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.template.defaultfilters import slugify

class Product(models.Model):
    name = models.CharField(max_length=200, help_text=_(u"Name of the product"))
    slug = AutoSlugField(_('slug'), populate_from='name', overwrite=True)

    class Meta:
        app_label = "my_app"
        verbose_name = _(u"Product") 
        verbose_name_plural = _(u"Products") 

    #So for each defined language I set the slug with the name of the Product
    # I think the test of hasattr could be removed if you entirely "control" your application
    def save(self, *args, **kwargs):
        for lang_code, lang_verbose in settings.LANGUAGES:
            if hasattr(self, 'slug_%s' % lang_code) and hasattr(self, 'name_%s' % lang_code):
                setattr(self, 'slug_%s' % lang_code, slugify(getattr(self, 'name_%s' % lang_code, u"")))
        super(Product, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return "/product/%s-%s" % (self.id, self.slug)

 

Now all my Products have their slug automaticaly translated ! I defined the “get_absolute_url” to use the translated slug (as you know django-modeltranslation uses the defined attribute [here “slug”] as a wrapper for “slug_fr”, “slug_en”,…and returns you the translated attribute in the current choosen language).

But, which pattern will match with my translated url ?? Answer : No one ! Here is the tricky part:

I defined the detailview url of a product as its product_id and “a slug”:


#File urls.py

from django.conf.urls.defaults import *
urlpatterns = patterns('my_app.views',
    (r'^product/(?P\d+)-(?P([\w,-]+)*)$', "products.detail_product"),
)

 

And in my view, I set the default value slug to an empty string (maybe None works?), so it doesn’t matter which slug the url pattern will receive it matches only the product_id will be catched (and verified) !


#File products.py (== views.py)

def detail_product(request, product_id, slug=""):
    product = get_object_or_404(Product, pk=product_id)
    #Return your HttpResponse..

So my product could be found either by:
/products/product/1-my-product
or
/products/product/1-mon-produit

 

That’s it ! I hope I was enough clear otherwise tell me in comments 😉

Advertisements
This entry was posted in django, python. Bookmark the permalink.

7 Responses to How to translate slug with django-modeltranslation

  1. Like your blog entry! I am a bit of a beginner myself. Always looking for people that are happy to contribute their time to help people like myself get a grip on django better.

  2. raphaelbeck says:

    Thanks 😀
    I wish I have more time to write more entries about django 🙂

  3. . says:

    AutoSlugField?

  4. raphaelbeck says:

    I used AutoSlugField from django_extensions when this article was written. I think now it’s a part of a whole package (http://packages.python.org/django-autoslug/fields.html ?). The purpose (as you might have guessed) is to generate automatically the slug representation of the field 🙂

  5. Jean-Baptiste says:

    Hi,
    I used the trick you describe in this post recently: I just updated it in the view to avoid the extra ID parameter by doing this:
    from django.utils.translation import get_language
    def my_view(category_slug):
    category = eval(“get_object_or_404(Category,slug_”+get_language()+”=category_slug)”)

    so that you retrieve your object directly using the current language, the one that match the url slug. It wors whan using i18n_patterns that automatically add the language code at the beggining of each URL.
    This post is from 2011 so Django is 1.4 now… don’t know if this was possible in april 2011.

    Hope it may help,
    Cheers,

    Jean-Baptiste

  6. raphaelbeck says:

    I have not tested your solution but it seems fine, even if I don’t like to use raw eval.
    About the i18n_patterns, indeed I think it wasn’t exist at the time of this writting (I think it appears in django 1.4).

  7. Jenpoul says:

    Thanks a lot for this post. I was making my own website but I had a lot of 404 when I shared external link for different languages to the same post. Why ? Because I forgot to change the save methods in models. Thanks a lot again 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s