The languages of a Lino site¶
The languages
attribute in the
settings.py
file of a Lino site specifies what we call the
language distribution.
The language distribution controls two things: (1) the front end languages available on this site and (2) how multilingual database content is stored in your database.
Every site user may chose one of these languages in their preferences.
The site maintainer can change the language distribution with the
Site.language
attribute in the settings.py
file.
- language distribution
The list of languages that are activated on a given Lino site.
- language code
A code that identifies a language for which screen messages have been translated.
Lino uses the same language codes as Django. You can see the list of available languages in django/conf/global_settings.py.
You may use any of these languages, but Lino-specific messages are currently translated only into German, French, Estonian and Dutch. You are welcome to contribute translations to your language of choice. See Instructions for translators.
Having more than one language in your language distribution will make your Lino site multilingual. Changing this setting may change your database structure and thus might require a data migration. This is because this setting also controls how multilingual database content is stored in your database. See Multilingual database content.
>>> import lino
>>> lino.startup('lino.projects.std.settings_test')
Developer reference¶
-
class
lino.core.site.
Site
-
languages = None
The language distribution to be used on this site.
This must be either None, or an iterable of language codes, or a string containing a space-separated series of language codes.
Examples:
languages = "en de fr nl et" languages = ['en', 'fr'] languages = 'en'
The first language in this list is the site's default language, returned by
get_default_language()
.
-
hidden_languages = None
A string with a space-separated list of language codes that should be hidden. This is useful when you have multilingual database content and a language distribution of more than three languages.
-
>>> from django.utils import translation
>>> from lino.core.site import TestSite as Site
>>> import json
>>> from pprint import pprint
Application code usually specifies Site.languages
as a single
string with a space-separated list of language codes. The
Site
will analyze this string during instantiation and
convert it into a tuple of LanguageInfo
objects.
The following examples use the TestSite
class just to show certain
things that apply also to "real" Sites.
>>> SITE = Site(languages="en fr de")
>>> print(SITE.languages)
(LanguageInfo(django_code='en', name='en', index=0, suffix=''),
LanguageInfo(django_code='fr', name='fr', index=1, suffix='_fr'),
LanguageInfo(django_code='de', name='de', index=2, suffix='_de'))
>>> SITE = Site(languages="de-ch de-be")
>>> print(SITE.languages)
(LanguageInfo(django_code='de-ch', name='de_CH', index=0, suffix=''), LanguageInfo(django_code='de-be', name='de_BE', index=1, suffix='_de_BE'))
If we have more than one locale of a same language on a same Site (e.g. 'en-us' and 'en-gb') then it is not allowed to specify just 'en'. But otherwise it is allowed to just say "en", which will mean "the English variant used on this Site".
>>> site = Site(languages="en-us fr de-be de")
>>> print(site.languages)
(LanguageInfo(django_code='en-us', name='en_US', index=0, suffix=''),
LanguageInfo(django_code='fr', name='fr', index=1, suffix='_fr'),
LanguageInfo(django_code='de-be', name='de_BE', index=2, suffix='_de_BE'),
LanguageInfo(django_code='de', name='de', index=3, suffix='_de'))
>>> pprint(site.language_dict)
{'de': LanguageInfo(django_code='de', name='de', index=3, suffix='_de'),
'de_BE': LanguageInfo(django_code='de-be', name='de_BE', index=2, suffix='_de_BE'),
'en': LanguageInfo(django_code='en-us', name='en_US', index=0, suffix=''),
'en_US': LanguageInfo(django_code='en-us', name='en_US', index=0, suffix=''),
'fr': LanguageInfo(django_code='fr', name='fr', index=1, suffix='_fr')}
>>> site.language_dict['de']
LanguageInfo(django_code='de', name='de', index=3, suffix='_de')
>>> site.language_dict['de_BE']
LanguageInfo(django_code='de-be', name='de_BE', index=2, suffix='_de_BE')
>>> site.language_dict['de'] == site.language_dict['de_BE']
False
>>> site.language_dict['en'] == site.language_dict['en_US']
True
>>> site.language_dict['en']
LanguageInfo(django_code='en-us', name='en_US', index=0, suffix='')
>>> site.language_dict['en']
LanguageInfo(django_code='en-us', name='en_US', index=0, suffix='')
>>> site.language_dict['fr']
LanguageInfo(django_code='fr', name='fr', index=1, suffix='_fr')
>>> pprint(site.django_settings['LANGUAGES'])
[('de', 'German'), ('fr', 'French')]
Note that Lino automatically sets USE_L10N
to True when you specify
languages
.
>>> site.django_settings['USE_L10N']
True
>>> pprint(site.django_settings['LANGUAGE_CODE'])
'en-us'
When you leave languages
at its default
value None, Lino will set the default language "en" at startup. But there is a
difference between None and "en": None will cause USE_L10N
to be
False because this is what we want when we don't worry about languages.
Lino's default language is "en" and not "en-us" because Django has no entry in
LANGUAGES
for this language code, and because we also reduce the
LANGUAGES
setting to the languages that are needed. Django 3.0.3
system check complained with "(translation.E004) You have provided a value for
the LANGUAGE_CODE setting that is not in the LANGUAGES setting."
>>> site = Site()
>>> print(site.languages)
(LanguageInfo(django_code='en', name='en', index=0, suffix=''),)
>>> pprint(site.django_settings['LANGUAGES'])
[('en', 'English')]
>>> 'USE_L10N' in site.django_settings
False
>>> 'LANGUAGE_CODE' in site.django_settings
False
API documentation¶
The SITE
object contains other information about the language
distribution.
-
class
lino.core.site.
Site
-
get_default_language
(self)¶ The django code of the default language to use in every
lino.utils.mldbc.fields.LanguageField
.
-
LANGUAGE_CHOICES
¶ A tuple in the format expected by Django's
Field.choices
attribute, used e.g. byLanguageField
. Its content is automatically populated fromlanguages
and application code should not change its value.
-
get_language_info
(self, code)¶ Use this in Python fixtures or tests to test whether a Site instance supports a given language. code must be a Django-style language code.
On a site with only one locale of a language (and optionally some other languages), you can use only the language code to get a tuple of
LanguageInfo
objects.>>> from lino.core.site import TestSite as Site >>> Site(languages="en-us fr de-be de").get_language_info('en') LanguageInfo(django_code='en-us', name='en_US', index=0, suffix='')
On a site with two locales of a same language (e.g. 'en-us' and 'en-gb'), the simple code 'en' yields that first variant:
>>> site = Site(languages="en-us en-gb") >>> print(site.get_language_info('en')) LanguageInfo(django_code='en-us', name='en_US', index=0, suffix='')
-
resolve_languages
(self, languages)¶ This is used by UserType.
Examples:
>>> from lino.core.site import TestSite as Site >>> lst = Site(languages="en fr de nl et pt").resolve_languages('en fr') >>> [i.name for i in lst] ['en', 'fr']
You may not specify languages that don't exist on this site:
>>> Site(languages="en fr de").resolve_languages('en nl') Traceback (most recent call last): ... Exception: Unknown language code 'nl' (must be one of ['en', 'fr', 'de'])
-
str2kw
(self, name, txt, **kw)¶ Return a dictionary that maps the internal field names for babelfield name to their respective translation of the given lazy translatable string txt.
>>> from django.utils.translation import gettext_lazy as _ >>> from lino.core.site import TestSite as Site >>> site = Site(languages='de fr es') >>> site.str2kw('name', _("January")) == {'name_fr': 'janvier', 'name': 'Januar', 'name_es': 'Enero'} True >>> site = Site(languages='fr de es') >>> site.str2kw('name', _("January")) == {'name_de': 'Januar', 'name': 'janvier', 'name_es': 'Enero'} True
-
babelkw
(self, name, **kw)¶ Return a dict with appropriate resolved field names for a BabelField name and a set of hard-coded values.
You have some hard-coded multilingual content in a fixture: >>> from lino.core.site import TestSite as Site >>> kw = dict(de="Hallo", en="Hello", fr="Salut")
The field names where this info gets stored depends on the Site's languages distribution.
>>> Site(languages="de-be en").babelkw('name',**kw) == {'name_en': 'Hello', 'name': 'Hallo'} True
>>> Site(languages="en de-be").babelkw('name',**kw) == {'name_de_BE': 'Hallo', 'name': 'Hello'} True
>>> Site(languages="en-gb de").babelkw('name',**kw) == {'name_de': 'Hallo', 'name': 'Hello'} True
>>> Site(languages="en").babelkw('name',**kw) == {'name': 'Hello'} True
>>> Site(languages="de-be en").babelkw('name',de="Hallo",en="Hello") == {'name_en': 'Hello', 'name': 'Hallo'} True
In the following example babelkw attributes the keyword de to the first language variant:
>>> Site(languages="de-ch de-be").babelkw('name',**kw) == {'name': 'Hallo'} True
-
args2kw
(self, name, *args)¶ Takes the basename of a BabelField and the values for each language. Returns a dict mapping the actual fieldnames to their values.
-
field2kw
(self, obj, name, **known_values)¶ Return a dict with all values of the BabelField name in the given object obj. The dict will have one key for each
languages
.Examples:
>>> from lino.core.site import TestSite as Site >>> from lino.utils import AttrDict >>> def testit(site_languages): ... site = Site(languages=site_languages) ... obj = AttrDict(site.babelkw( ... 'name', de="Hallo", en="Hello", fr="Salut")) ... return site,obj
>>> site, obj = testit('de en') >>> site.field2kw(obj, 'name') == {'de': 'Hallo', 'en': 'Hello'} True
>>> site, obj = testit('fr et') >>> site.field2kw(obj, 'name') == {'fr': 'Salut'} True
-
field2args
(self, obj, name)¶ Return a list of the babel values of this field in the order of this Site's
Site.languages
attribute.
-
babelitem
(self, *args, **values)¶ Given a dictionary with babel values, return the value corresponding to the current language.
This is available in templates as a function tr.
>>> kw = dict(de="Hallo", en="Hello", fr="Salut")
>>> from lino.core.site import TestSite as Site >>> from django.utils import translation
A Site with default language "de":
>>> site = Site(languages="de en") >>> tr = site.babelitem >>> with translation.override('de'): ... print(tr(**kw)) Hallo
>>> with translation.override('en'): ... print(tr(**kw)) Hello
If the current language is not found in the specified values, then it returns the site's default language:
>>> with translation.override('jp'): ... print(tr(en="Hello", de="Hallo", fr="Salut")) Hallo
Testing detail: default language should be "de" in our example, but we are playing here with more than one Site instance while Django knows only one "default language" which is the one specified in lino.projects.docs.settings.
Another way is to specify an explicit default value using a positional argument. In that case the language's default language doesn'n matter:
>>> with translation.override('jp'): ... print(tr("Tere", de="Hallo", fr="Salut")) Tere
>>> with translation.override('de'): ... print(tr("Tere", de="Hallo", fr="Salut")) Hallo
You may not specify more than one default value:
>>> tr("Hello", "Hallo") Traceback (most recent call last): ... ValueError: ('Hello', 'Hallo') is more than 1 default value.
-
babelattr
(self, obj, attrname, default=NOT_PROVIDED, language=None)¶ Return the value of the specified babel field attrname of obj in the current language.
This is to be used in multilingual document templates. For example in a document template of a contract you may use the following expression:
babelattr(self.type, 'name')
This will return the correct value for the current language.
Examples:
>>> from __future__ import unicode_literals >>> from django.utils import translation >>> from lino.core.site import TestSite as Site >>> from lino.utils import AttrDict >>> def testit(site_languages): ... site = Site(languages=site_languages) ... obj = AttrDict(site.babelkw( ... 'name', de="Hallo", en="Hello", fr="Salut")) ... return site, obj
>>> site,obj = testit('de en') >>> with translation.override('de'): ... print(site.babelattr(obj,'name')) Hallo
>>> with translation.override('en'): ... print(site.babelattr(obj,'name')) Hello
If the object has no translation for a given language, return the site's default language. Two possible cases:
The language exists on the site, but the object has no translation for it:
>>> site,obj = testit('en es') >>> with translation.override('es'): ... print(site.babelattr(obj, 'name')) Hello
Or a language has been activated which doesn't exist on the site:
>>> with translation.override('fr'): ... print(site.babelattr(obj, 'name')) Hello
-