"""Localization management."""
from sys import exit as sys_exit
from os.path import dirname, join
from locale import getlocale
from json import dumps, loads
import colander
from pyramid.i18n import TranslationStringFactory, make_localizer
from pyramid.exceptions import ConfigurationError
from .config import settings_get_list
_ = TranslationStringFactory('chrysalio')
# =============================================================================
[docs]def locale_negotiator(request):
"""Locale negotiator to figure out the language to use.
:type request: pyramid.request.Request
:param request:
Current request.
:rtype: str
"""
if request.session.get('lang'):
return request.session['lang']
return request.accept_language.lookup(
settings_get_list(
request.registry.settings, 'languages', ['en']),
default_tag=request.registry['settings']['language'])
# =============================================================================
[docs]def add_translation_dirs(configurator, package):
"""Add one or more translation directory paths to the current configuration
state according to settings and package name.
:type configurator: pyramid.config.Configurator
:param configurator:
Object used to do configuration declaration within the application.
:param str package:
Name of the calling package.
"""
dirs = ['chrysalio:Locale', 'colander:locale']
if package != 'chrysalio':
dirs.insert(0, ('{0}:Locale'.format(package)))
if configurator.get_settings().get('translation_dirs'):
dirs = settings_get_list(
configurator.get_settings(), 'translation_dirs') + dirs
try:
configurator.add_translation_dirs(*dirs)
except (ImportError, ConfigurationError) as error:
sys_exit('*** Translation directories: {0}'.format(error))
# =============================================================================
[docs]def translate(text, lang=None, request=None):
"""Return ``text`` translated.
:param str text:
Text to translate.
:param str lang: (optional)
Language to use.
:type request: pyramid.request.Request
:param request: (optional)
Current request to find the current language.
:rtype: str
"""
if lang is None and request is not None:
lang = request.session.get('lang') \
or request.locale_name or request.registry['settings']['language']
return make_localizer(
lang or getlocale()[0] or 'en',
(join(dirname(__file__), '..', 'Locale'),)).translate(text)
# =============================================================================
[docs]def translate_field(request, i18n_fields, default=''):
"""Return the best translation according to user language.
:type request: pyramid.request.Request
:param request:
Current request.
:param dict i18n_fields:
Dictionary of avalaible translations.
:param str default:
Default label.
:rtype: str
"""
if not i18n_fields:
return ''
return \
'lang' in request.session and i18n_fields.get(request.session['lang'])\
or i18n_fields.get(request.locale_name) \
or i18n_fields.get(request.registry['settings']['language']) \
or i18n_fields.get(request.registry.settings.get(
'pyramid.default_locale_name', 'en')) \
or i18n_fields.get('en') or default
# =============================================================================
# =============================================================================
[docs]def view_i18n_labels(
request, form, dbitem, with_label=True, with_description=True):
"""Return HTML input to edit i18n labels.
:type request: pyramid.request.Request
:param request:
Current request.
:param dbitem:
Current SqlAlchemy object.
:param bool with_label: (default=True)
If ``True``, produce input fields for labels.
:param bool with_description: (default=True)
If ``True``, produce input fields for descriptions.
:rtype: helpers.literal.Literal
"""
html = ''
local_translate = request.localizer.translate
if with_label and dbitem.i18n_label:
labels = loads(dbitem.i18n_label)
for lang in sorted(labels):
html += form.grid_item(
local_translate(_('Label (${l}):', {'l': lang})), labels[lang],
clear=True)
if with_description and dbitem.i18n_description:
if dbitem.i18n_description:
for lang in sorted(dbitem.i18n_description):
html += form.grid_item(
local_translate(_('Description (${l}):', {'l': lang})),
dbitem.i18n_description[lang], clear=True)
return html
# =============================================================================
[docs]def schema_i18n_labels(request, schema, label_len=0, description_len=0,
prefix='', required=True):
"""Update the Colander schema with fields for label ans, possibly, for
descriptions of each available language.
:type request: pyramid.request.Request
:param request:
Current request.
:type schema: colander.SchemaNode
:param schema:
Current Colander schema.
:param int label_len: (optional)
Maximum length of label. If ``0``, no field is produced.
:param int description_len: (optional)
Maximum length of description. If ``0``, no field is produced.
:param str prefix: (optional)
A prefix for the name of field.
:param bool required: (default=True)
``True`` if the label in default language is required.
"""
default_lang = request.registry['settings']['language']
# Label
if label_len:
for lang in sorted(settings_get_list(
request.registry.settings, 'languages', ('en',))):
if lang == default_lang and required:
schema.add(colander.SchemaNode(
colander.String(),
name='{0}label_{1}'.format(prefix, lang),
validator=colander.Length(max=label_len)))
else:
schema.add(colander.SchemaNode(
colander.String(),
name='{0}label_{1}'.format(prefix, lang),
validator=colander.Length(max=label_len), missing=None))
# Description
if description_len:
for lang in sorted(settings_get_list(
request.registry.settings, 'languages', ('en',))):
schema.add(colander.SchemaNode(
colander.String(),
name='{0}description_{1}'.format(prefix, lang),
validator=colander.Length(max=description_len), missing=None))
# =============================================================================
[docs]def defaults_i18n_labels(
dbitem, with_label=True, with_description=True, prefix=''):
"""Return a dictionary of default values for labels.
:param dbitem:
Current SqlAlchemy object.
:param bool with_label: (default=True)
If ``True``, produce input fields for labels.
:param bool with_description: (default=True)
If ``True``, produce input fields for descriptions.
:param str prefix: (optional)
A prefix for the name of field.
"""
defaults = {}
if with_label and dbitem.i18n_label:
labels = loads(dbitem.i18n_label)
for lang in labels:
defaults['{0}label_{1}'.format(prefix, lang)] = labels[lang]
if with_description and dbitem.i18n_description:
for lang in dbitem.i18n_description:
defaults['{0}description_{1}'.format(prefix, lang)] = \
dbitem.i18n_description[lang]
return defaults
# =============================================================================
[docs]def edit_i18n_labels(
request, form, label_len=0, description_len=0, prefix=''):
"""Return HTML input to edit i18n labels.
:type request: pyramid.request.Request
:param request:
Current request.
:param int label_len: (optional)
Maximum length of label. If ``0``, no field is produced.
:param int description_len: (optional)
Maximum length of description. If ``0``, no field is produced.
:param str prefix: (optional)
A prefix for the name of field.
:rtype: helpers.literal.Literal
"""
html = ''
default_lang = request.registry['settings']['language']
local_translate = request.localizer.translate
# Label
if label_len:
for lang in sorted(settings_get_list(
request.registry.settings, 'languages', ('en',))):
html += form.grid_text(
'{0}label_{1}'.format(prefix, lang),
local_translate(_('Label (${l}):', {'l': lang})),
required=lang == default_lang,
maxlength=label_len, clear=True)
# Description
if description_len:
for lang in sorted(settings_get_list(
request.registry.settings, 'languages', ('en',))):
html += form.grid_text(
'{0}description_{1}'.format(prefix, lang),
local_translate(_('Description (${l}):', {'l': lang})),
maxlength=description_len, clear=True)
return html