Welcome | Get started | Dive into Lino | Contribute | Reference
Multilingual database content¶
One feature of Lino is its built-in support for single-table multilingual database content. This tutorial explains what it is.
Note that this is not about internationalization (i18n).
Internationalization is when the front end can speak different
languages. Lino has nothing to add to the existing Django techniques about
Internationalization,
that's why we deliberately didn't add lino.modlib.users
and front end
translation in this tutorial.
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
BabelCharField
instead of Django'sCharField
for every translatable field andset the
languages
attribute to"en"
for your US customer and to"en fr"
for your Canadian customer.
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
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**
==================== ================== ============= ============
Using the web interface¶
$ go mldbc
$ python manage.py prep
$ python manage.py runserver
Analyzing Tables...
Analyze 0 slave tables...
Discovering choosers for model fields...
Analyzing Tables...
Analyze 0 slave tables...
Discovering choosers for model fields...
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
November 05, 2019 - 07:32:06
Django version 2.2.6, using settings 'lino_book.projects.mldbc.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
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_apps(self):
yield super(Site, self).get_installed_apps()
# yield 'lino.modlib.system'
yield 'lino_book.projects.mldbc'
def setup_menu(self, user_type, main):
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 site
maintainer 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.