For www.dipf.de we (starzel.de) are managing 10 lineage-subsites, some of them are multilingual (LinguaPlone). It is still Plone 4 with Archetypes.
We're currently migrating it to Plone 6 and we will use Lineage again, this time with plone.app.multilingual.
Because lineage & multilingual is not straightforward, to setup a subsite I created a simple type that allows you to decide if it is multilingual or not on creation. A subscriber will create the appropriate language-root-folders for you:
from Acquisition import aq_base
from collective.lineage.utils import enable_childsite
from logging import getLogger
from plone import api
from plone.app.dexterity.behaviors.exclfromnav import IExcludeFromNavigation
from plone.app.layout.navigation.interfaces import INavigationRoot
from plone.app.multilingual.interfaces import ITranslationManager
from plone.autoform import directives
from plone.dexterity.content import Container
from plone.supermodel import model
from Products.CMFPlone.interfaces import ILanguage
from Products.CMFPlone.utils import _createObjectByType
from z3c.form.interfaces import IEditForm
from zope import schema
from zope.event import notify
from zope.interface import alsoProvides
from zope.interface import implementer
from zope.lifecycleevent import modified
log = getLogger(__name__)
class ISubsite(model.Schema):
"""Dexterity Schema for Subsite
"""
directives.omitted(IEditForm, 'multilingual')
multilingual = schema.Bool(
title='Create language root-foldes for this subsite?',
required=False,
default=False,
)
@implementer(ISubsite)
class Subsite(Container):
"""Subsite instance"""
def enable_subsite(obj, event):
enable_childsite(obj)
if obj.multilingual:
subsite_setup = SetupMultilingualSubsite()
subsite_setup.setupSubsite(obj)
class SetupMultilingualSubsite(object):
# portal_type that is added as root language folder
folder_type = 'LRF'
def __init__(self, context=None):
self.context = context
self.folders = {}
self.languages = []
self.defaultLanguage = None
def setupSubsite(self, context, forceOneLanguage=False):
self.context = context
self.folders = {}
language_tool = api.portal.get_tool('portal_languages')
self.languages = languages = language_tool.getSupportedLanguages()
self.defaultLanguage = language_tool.getDefaultLanguage()
if len(languages) == 1 and not forceOneLanguage:
return 'Only one supported language configured.'
doneSomething = False
available = language_tool.getAvailableLanguages()
for language in languages:
info = available[language]
name = info.get('native', info.get('name'))
doneSomething += self.setUpLanguage(language, name)
doneSomething += self.linkTranslations()
doneSomething += self.setupLanguageSwitcher()
if not doneSomething:
return 'Nothing done.'
else:
return f'Setup of language root folders on Plone site {self.context.getId()}'
def linkTranslations(self):
"""Links the translations of the default language Folders
"""
doneSomething = False
try:
canonical = ITranslationManager(self.folders[self.defaultLanguage])
except TypeError as e:
raise TypeError(str(e) + u' Are your folders ITranslatable?')
for language in self.languages:
if language == self.defaultLanguage:
continue
if not canonical.has_translation(language):
language_folder = self.folders[language]
canonical.register_translation(language, language_folder)
doneSomething = True
if doneSomething:
log.info(u'Translations linked.')
return doneSomething
def setUpLanguage(self, code, name):
"""Create the language folders in the subsite
"""
doneSomething = False
folderId = str(code)
folder = getattr(aq_base(self.context), folderId, None)
if folder is None:
_createObjectByType(self.folder_type, self.context, folderId)
folder = self.context[folderId]
ILanguage(folder).set_language(code)
folder.setTitle(name)
api.content.transition(folder, to_state='published')
# Exclude folder from navigation (if applicable)
adapter = IExcludeFromNavigation(folder, None)
if adapter is not None:
adapter.exclude_from_nav = True
# We've modified the object; reindex.
notify(modified(folder))
doneSomething = True
log.info(f'Added {code} folder: {folderId}')
self.folders[code] = folder
if not INavigationRoot.providedBy(folder):
alsoProvides(folder, INavigationRoot)
doneSomething = True
log.info(f'INavigationRoot setup on folder {code}')
return doneSomething
def setupLanguageSwitcher(self):
doneSomething = False
if self.context.getLayout() != '@@language-switcher':
self.context.setLayout('@@language-switcher')
self.context.reindexObject()
log.info('Language switcher set up for subsite.')
doneSomething = True
return doneSomething
For super-basic styling we allow adding css and a logo on the subsites with a behavior:
from plone.autoform.interfaces import IFormFieldProvider
from plone.namedfile.field import NamedBlobImage
from plone.supermodel import model
from zope import schema
from zope.interface import provider
@provider(IFormFieldProvider)
class ISubsiteTheme(model.Schema):
logo = NamedBlobImage(
title=u'Logo',
required=False,
)
css = schema.Text(
title=u'Custom css',
required=False,
missing_value=u'',
)