Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More
Introduction to multilingual database content¶
One feature of Lino is its built-in support for single-table multilingual database content. This document explains what it is.
Note that this is not about internationalization (i18n). Internationalization is when the front end can speak different languages. Lino uses the existing Django techniques about Internationalization.
When to use MLDBC¶
Imagine a Canadian company that wants to print catalogues and price offers in two different languages, either English or French, depending on the customer’s preferred language. They don’t want to maintain different product tables because it is one company, one accounting, and prices are the same in French and in English. They need a Products table like this:
Designation (en)
Designation (fr)
Category
Price
ID
Chair
Chaise
Accessories
29.95
1
Table
Table
Accessories
89.95
2
Monitor
Écran
Hardware
19.95
3
Mouse
Souris
Accessories
2.95
4
Keyboard
Clavier
Accessories
4.95
5
Now imagine that your application is being used not only in Canada but also in the United States. Your US customers don’t want to have a “useless” column for the French designation of their products.
This is where you want multi-lingual database content. In that case you would simply
use a babel field instead of Django’s
CharField
for every translatable fieldand set the
languages
attribute to"en"
for your US customer and to"en fr"
for your Canadian customer.
- babel field¶
A special database field that behaves like a normal Django field but actually generates a series of fields in the database model, one for each
language
of the site.
Babel fields are implemented by BabelCharField
and
BabelTextField
defined in module lino.utils.mldbc.fields
. The
former creates single-line CharField
while The latter creates
multi-line TextField
.
An example¶
Go to the lino_book.projects.mldbc
demo project:
$ go mldbc
Make sure that the demo database is initialized:
$ python manage.py prep
Open the interactive Django shell:
$ pm shell
>>> from lino.api.doctest import *
>>> Product = rt.models.mldbc.Product
You can print a catalog in different languages:
>>> print(', '.join([str(p) for p in Product.objects.all()]))
Chair, Table, Monitor, Mouse, Keyboard, Consultation
>>> from django.utils import translation
>>> with translation.override('fr'):
... print(', '.join([str(p) for p in Product.objects.all()]))
Chaise, Table, Ecran, Souris, Clavier, Consultation
Here is how we got the above table:
>>> from lino.api import rt
>>> rt.show(mldbc.Products)
==================== ================== ============= ============
Designation Designation (fr) Category Price
-------------------- ------------------ ------------- ------------
Chair Chaise Accessories 29,95
Table Table Accessories 89,95
Monitor Ecran Hardware 19,95
Mouse Souris Accessories 2,95
Keyboard Clavier Accessories 4,95
Consultation Consultation Service 59,95
**Total (6 rows)** **207,70**
==================== ================== ============= ============
Screenshots¶
The screenshots on the left have been taken on a server with
languages = ['en']
,
those on the right on a server with
languages = ['de','fr']
.
The settings.py
file¶
from lino.projects.std.settings import *
class Site(Site):
title = "MLDBC Tutorial"
demo_fixtures = ['demo']
languages = 'en fr'
def get_installed_plugins(self):
yield super(Site, self).get_installed_plugins()
# yield 'lino.modlib.system'
yield 'lino_book.projects.mldbc'
def setup_menu(self, user_type, main, ar=None):
m = main.add_menu("products", "Products")
m.add_action('mldbc.Products')
super(Site, self).setup_menu(user_type, main)
SITE = Site(globals())
DEBUG = True
This is where you specify the languages
setting.
The models.py
file¶
from lino.api import dd
from lino import mixins
from django.utils.translation import gettext_lazy as _
class Categories(dd.ChoiceList):
verbose_name = _("Category")
verbose_name_plural = _("Categories")
Categories.add_item("01", _("Hardware"), 'hardware')
Categories.add_item("02", _("Service"), 'service')
Categories.add_item("03", _("Accessories"), 'accessories')
Categories.add_item("04", _("Software"), 'software')
class Product(mixins.BabelNamed):
price = dd.PriceField(_("Price"), blank=True, null=True)
category = Categories.field(blank=True, null=True)
class Meta:
verbose_name = 'Product'
verbose_name_plural = 'Products'
class Products(dd.Table):
model = Product
sort_order = ['name']
column_names = "name category price *"
auto_fit_column_widths = True
detail_layout = """
id price category
name
"""
insert_layout = """
name
category price
"""
In case you wonder what a choicelist is, see Introduction to choicelists.
The demo fixture¶
# -*- coding: UTF-8 -*-
from __future__ import unicode_literals
from lino.api import dd
Product = dd.resolve_model('mldbc.Product')
def P(en, de, fr, category, price):
return Product(price=price,
category=category,
**dd.babel_values('name', en=en, de=de, fr=fr))
def objects():
yield P("Chair", "Stuhl", "Chaise", '03', '29.95')
yield P("Table", "Tisch", "Table", '03', '89.95')
# doctests fail with non-ascii text, so we need to cheat:
# yield P("Monitor", "Bildschirm", "Écran", '01', '19.95')
yield P("Monitor", "Bildschirm", "Ecran", '01', '19.95')
yield P("Mouse", "Maus", "Souris", '03', '2.95')
yield P("Keyboard", "Tastatur", "Clavier", '03', '4.95')
yield P("Consultation", "Beratung", "Consultation", '02', '59.95')
Note how the application developer doesn’t know which languages
will be set at runtime.
Of course the fixture above supposes a single person who knows all the languages, but that’s just because we are simplifying. In reality you can do it as sophisticated as you want, reading the content from different sources.
BabelFields and migrations¶
BabelFields cause the database structure to change when a server
administrator locally changes the languages
setting of a Lino site.
That’s why the application developer does not provide Django migrations for their product. See Data migrations à la Lino and Django migrations on a Lino site.