In this article, we're going to discuss how to change responses to repond with different data for different languages. This article does NOT address internal internationalization changes such as error messages responding in different languages (that is discussed in the docs.

The end goal is that I'll be able to make a request and somehow magically receive back English response data, and I'll also be able to make a different request and somehow magically receive back Spanish response data.

Changing the Model

Here's the situation. I'm working on a mobile application that displays various data items about Henna Tattoos. The main thing we'll be focusing on in this tutorial/article are the blog articles.


import os
from django.db import models
from tinymce.models import HTMLField

class Article(models.Model):
    def get_article_image_path(instance, filename):
        return os.path.join('designs/%s' % filename)

    title = models.CharField(max_length=40)
    headline = models.CharField(max_length=500)
    image = models.ImageField(upload_to=get_article_image_path)
    body = HTMLField()
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

I'm not doing any sort of automatic translating, I'm going to be translating articles manually, so that they're actually translated well. The first step is to separate out different fields for English and Spanish.

The first step is to separate out different fields for English and Spanish. I'm going to give various fields a prefix of en_ or es_ for the fields that will have language translation.


class Article(models.Model):
    
    es_title = models.CharField(max_length=40)
    es_headline = models.CharField(max_length=500)
    es_body = HTMLField()

    en_title = models.CharField(max_length=40, null=True, blank=True)
    en_headline = models.CharField(max_length=500, null=True, blank=True)
    en_body = HTMLField(null=True, blank=True)

    image = models.ImageField(upload_to=get_article_image_path)
    created = models.DateTimeField(auto_now_add=True)

    ...

As you can see from the null/blanks, Spanish is going to be the required language, and then English translation is optional.

Differentiating Language in the Request

There are two options I can think of to do this, and it's very similar to selecting an API verision. We can either pass in a header on the request to specify language, or pass in a language identifier in the URL. For this case, I'd rather pass in an HTTP Accept-Language Header.

The client-side changes are specific to what you're using to interact with the API, so I'm not going to give too many details as that is not what this article is focusing on. The key thing to note though is that the request should have an Accept-Language HTTP Header.

Modifying the Response Data

Now that we're sending an Accept-Language header in our requests to the API server, we get to start on the fun stuff.


class ArticleSerializer(ModelSerializer):
    class Meta:
        model = Article

class ArticleViewSet(ReadOnlyModelViewSet):
    serializer_class = ArticleSerializer
    queryset = Article.objects.all()

This is the basic Serializer-ViewSet combination. Now we're going to change it to have two different serializers - 1 for English and 1 for Spanish. We're going to look at the Accept-Language header (if it exists) and then use the appropriate Serializer class.


class ArticleViewSet(ReadOnlyModelViewSet):
    queryset = Article.objects.all()

    def get_serializer_class(self):
        if 'es' in self.request.META.get('HTTP_ACCEPT_LANGUAGE'):
            # using 'in' because it can be set to something like 'es-ES; es'
            return ArticleEsSerializer

        return ArticleEnSerializer


class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        exclude = ['es_body', 'es_title', 'es_headline',
            'en_body', 'en_title', 'en_headline']


class ArticleEnSerializer(serializers.ModelSerializer):
    body = serializers.CharField(source='en_body')
    title = serializers.CharField(source='en_title')
    headline = serializers.CharField(source='en_headline')

    class Meta(ArticleSerializer.Meta):
        pass


class ArticleEsSerializer(serializers.ModelSerializer):
    body = serializers.CharField(source='es_body')
    title = serializers.CharField(source='es_title')
    headline = serializers.CharField(source='es_headline')

    class Meta(ArticleSerializer.Meta):
        pass

And there we go! Depending on the Accept-Language header that we pass in with the HTTP Request, django will pass back data for body, title and headline in either Spanish or English. We're also transforming the field name so that in our front-end or mobile application we don't have to worry about the field names.