Building a full ecommerce site part 8: multilanguage support

10 June 2018, note: modeltranslation does not work on ipad! Oh no!

Our ecommerce site is made up of the Mezzanine CMS and Django-Oscar. They share one database. I thought about separating them into their own databases with a router but I really don't know how that would work since they're still using the same settings.py file. So, I thought I'd leave it as it is, install multilanguage support, and see how things go.

Step 1: do it with Mezzanine

I started with Mezzanine by following its documentation on Django-Modeltranslation. It's really easy and worked perfectly. One note though, the Mezzanine documentation says to set USE_MODELTRANSLATION = True. This setting is already in the settings.py file, so you don't have to add it yourself. Just set it to True.

I was dumb and added it all the way at the bottom of the file and it broke because you're supposed to have it before INSTALLED_APPS. And then I added it in the right place but there was an error because now I had two instances of the setting in the same file. That's when I realised, oh, Mezzanine ships with this setting already somewhere in this file. ctrl+f, found it.

I added German for testing but you can add whatever else you want as long as you follow the conventions in django.conf.locale.__init__.py. For instance, I thought Japanese was jp, but Django kept complaining that there's no such language. I took a look and it's ja, not jp.

step 2: do it with Oscar

I had so much difficulty trying to integrate Django-ModelTranslation into Django-Oscar’s Dashboard that I asked for advice on the Django-Oscar IRC channel. Someone gave me a clue.

<FalseID> oscar.apps.dashboard.catalogue.forms.ProductForm is a modelform that specifies fields in Meta. Forking that app and subclassing that formclass and adding the modeltranslation fields enables those in dashboard

<FalseID> like for me, I added 'title_fi' and 'title_en' to the fields

Step 3: fork the Oscar.apps.dashboard.catalogue

I’d already forked Oscar’s Dashboard but doing that gives you just the root Dashboard directory. So I ran python manage.py Oscar_fork_app dashboard.catalogue Oscar_forked_apps.

This places the catalogue app into your dashboard directory in your forked apps directory. Don’t forget to correct the backslashes to dots in config.py and __init__.py in the forked catalogue directory as usual.

I then added it to settings.py:

INSTALLED_APPS = [

] + get_core_apps([Oscar_forked_apps.dashboard,
Oscar_forked_apps.dashboard.catalogue])

Step 4: override Oscar’s ProductForm

I created forms.py in the forked catalogue directory. Then added this:

from oscar.apps.dashboard.catalogue.forms import ProductForm

class ProductForm(ProductForm):
model = Product
class Meta(ProductForm.Meta):
fields = [
'title',

# Added these two title lines.
'title_en',
'title_de',

'upc',
'description',
'is_discountable',
'structure']

This overrides the fields part of Oscar’s ProductForm.

I ran this but it broke so it’s not enough. I don't remember the exact Traceback but it said something about how there were no such fields as title_en and title_de. So, I thought maybe I’d flubbed it somehow and ModelTranslation didn’t create the fields I wanted.

I usually leave my first development stage database as the SQLite default so that I can easily open it with DB Browser for SQLite and take a look at the fields and values. I did that now and title_en and title_de were definitely in the catalogue_product table, which holds details on all our products.

UPDATE 16 Apr 2018: Do NOT override the Product model as I did with Step 5. This will prevent you from adding more languages in future. Turns out, I had done something wrong, but I've no clue what it was. Something must have gone haywire when I played with the admin forms trying to get translation forms to look pretty on the Dashboard. So, I restarted the project and redid everything up to this point without playing with admin forms and it worked perfectly.

Step 5: override the Product model (do NOT do this)

Maybe I should have, but I didn’t try to look at Dashboard’s views. Instead, I made a guess that Dashboard takes its cue from the Product class, which subclasses from AbstractProduct. I guessed that if something isn’t in these two classes, then Dashboard won’t find it.

So, I overrode Product, the original of which is in oscar.apps.catalogue.models. Which means, the overriding module should be Oscar_forked_apps.catalogue.models.

Careful here. I kept confusing Oscar_forked_apps.catalogue with Oscar_forked_apps.dashboard.catalogue.

Also, when you fork this app, it comes with a models.py already. It has from oscar.apps.catalogue.models import * # noqa isort:skip in it. Delete or comment out this line.

Anyway, I wrote this code into the forked models module:

from django.utils.translation import get_language, pgettext_lazy
from django.db import models
from oscar.apps.catalogue.abstract_models import AbstractProduct

class Product(AbstractProduct):
    title_en = models.CharField(pgettext_lazy(u'Product title', u'Title_en'),
    max_length=255, blank=True)
title_de = models.CharField(pgettext_lazy(u'Product title', u'Title_de'),
max_length=255, blank=True)

from oscar.apps.catalogue.models import *

Following the documentation, we do need to place the import * statement at the bottom, which, as the documentation confesses, is strange.

I ran this solution and it worked! The fields showed up on the product creation and update pages. However, they showed up one on top of the other, which means the more fields you need to translate, the more of them stack up on your page, making it very, very long indeed.

So ugly.

Step 6: make it nice (didn’t happen)

I tried to use ModelTranslation’s TabbedTranslationAdmin class but it didn’t work. FalseID suggested to use modeltranslation.forms.TranslationModelForm as a mixin for oscar.apps.dashboard.catalogue.forms.ProductForm. Didn’t work either.

So, Dashboard remains fugly. I'm on a deadline right now so I'll let it be for now. But, I'll probably try to do some happy little JS to it later. Stay tuned!

Comments

There are currently no comments

New Comment

required

required (not published)

optional

required