Sulu CMS Multilingual and Webspaces Setup

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Showing 1 of 1 servicesAll 2065 services
Sulu CMS Multilingual and Webspaces Setup
Medium
~2-3 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Sulu Multilingual Webspaces Configuration

Sulu builds multilinguality and multisite capability around the Webspace concept. One Webspace equals one site with a set of languages and portals (subdomains/URL prefixes). Multiple Webspaces in one instance equals full multisite with shared backoffice and independent content.

Webspace Principles

  • Webspace — logical site (example.com, shop.example.com)
  • Portal — access variant to Webspace (production URL, staging URL)
  • Localization — content language (en, de, fr)
  • URL — language binding to domain or path

One Webspace can be served from multiple domains. The reverse is not possible — one domain always belongs to one Webspace.

Multilingual Webspace

<!-- config/packages/webspaces/main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<webspace xmlns="http://schemas.sulu.io/webspace/webspace"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://schemas.sulu.io/webspace/webspace
          http://schemas.sulu.io/webspace/webspace-1.1.xsd">

    <name>Main Site</name>
    <key>main</key>

    <localizations>
        <localization language="en" default="true" xDefault="true"/>
        <localization language="de"/>
        <localization language="fr"/>
    </localizations>

    <shadow-base-language>en</shadow-base-language>

    <default-templates>
        <default-template type="page">default</default-template>
        <default-template type="home">homepage</default-template>
    </default-templates>

    <templates>
        <template type="page">default</template>
        <template type="page">article</template>
        <template type="home">homepage</template>
    </templates>

    <excluded-templates>
        <excluded-template>overview</excluded-template>
    </excluded-templates>

    <portals>
        <portal>
            <name>Main</name>
            <key>main</key>
            <environments>
                <environment type="prod">
                    <urls>
                        <url language="en" redirect="false">example.com</url>
                        <url language="de">de.example.com</url>
                        <url language="fr">fr.example.com</url>
                    </urls>
                </environment>
                <environment type="stage">
                    <urls>
                        <url language="en">stage.example.com</url>
                    </urls>
                </environment>
                <environment type="dev">
                    <urls>
                        <url language="en">example.localhost</url>
                        <url language="de">de.example.localhost</url>
                    </urls>
                </environment>
            </environments>
        </portal>
    </portals>
</webspace>

Multisite: Multiple Webspaces

<!-- config/packages/webspaces/blog.xml -->
<webspace>
    <name>Blog</name>
    <key>blog</key>

    <localizations>
        <localization language="en" default="true"/>
    </localizations>

    <portals>
        <portal>
            <name>Blog</name>
            <key>blog</key>
            <environments>
                <environment type="prod">
                    <urls>
                        <url language="en">blog.example.com</url>
                    </urls>
                </environment>
            </environments>
        </portal>
    </portals>
</webspace>

Each Webspace has an independent content tree. In the backoffice they appear as separate sites. Media library is shared.

Security Configuration per Webspace

# config/packages/security.yaml
sulu_security:
    checker:
        enabled: true

security:
    providers:
        sulu_backend:
            id: sulu_security.user_provider
    firewalls:
        admin:
            pattern: /admin
            provider: sulu_backend
        main:
            pattern: '^/'
            stateless: false

Editor access rights can be limited to specific Webspace:

Backoffice → Users → Edit User → Webspace Permissions

Shadow Pages — Content Synchronization

Shadow allows making a page of one language a "shadow" of another — it displays the base language content without creating a separate translation.

// Programmatically create shadow via API
$document = $this->documentManager->find('/cmf/main/contents/about', 'en');
$document->setShadowLocale('de');     // take content from de
$document->setShadowLocalesEnabled(true);
$this->documentManager->persist($document, 'en');
$this->documentManager->flush();

In the backoffice — "Shadow Page" toggle on each page.

Language Switcher in Twig

{# templates/snippets/language-switcher.html.twig #}
{% set currentLocale = app.request.locale %}

<nav class="lang-switcher">
    {% for locale in ['en', 'de', 'fr'] %}
        {% set url = sulu_content_path(null, webspace, locale) %}
        <a
            href="{{ url }}"
            hreflang="{{ locale }}"
            lang="{{ locale }}"
            class="{{ locale == currentLocale ? 'active' : '' }}"
            {% if locale == currentLocale %}aria-current="page"{% endif %}
        >
            {{ locale|upper }}
        </a>
    {% endfor %}
</nav>

{# hreflang in <head> for SEO #}
{% block hreflang %}
    {% for locale in ['en', 'de', 'fr'] %}
        <link rel="alternate"
              hreflang="{{ locale }}"
              href="{{ sulu_content_path(null, webspace, locale) }}">
    {% endfor %}
    <link rel="alternate"
          hreflang="x-default"
          href="{{ sulu_content_path(null, webspace, 'en') }}">
{% endblock %}

Language-Aware Navigation

// src/Twig/NavigationExtension.php
class NavigationExtension extends AbstractExtension
{
    public function __construct(
        private readonly NavigationMapperInterface $navigationMapper
    ) {}

    public function getFunctions(): array
    {
        return [
            new TwigFunction('app_navigation', [$this, 'getNavigation']),
        ];
    }

    public function getNavigation(
        string $context,
        string $webspaceKey,
        string $locale,
        int $depth = 2
    ): array {
        return $this->navigationMapper->getNavigation(
            null,
            $webspaceKey,
            $locale,
            $depth,
            false,
            $context
        );
    }
}
{% set nav = app_navigation('main', webspace, locale, 2) %}
{% for item in nav %}
    <a href="{{ item.url }}"
       {% if item.uuid == content.uuid %}aria-current="page"{% endif %}>
        {{ item.title }}
    </a>
    {% if item.children %}
        <ul>
        {% for child in item.children %}
            <li><a href="{{ child.url }}">{{ child.title }}</a></li>
        {% endfor %}
        </ul>
    {% endif %}
{% endfor %}

URL Strategies for Languages

Three URL configuration variants:

Subdomains: en.example.com, de.example.com — different URLs in Webspace portal.

Path prefix:

<url language="en">/</url>
<url language="de">/de</url>
<url language="fr">/fr</url>

Separate domain per language:

<url language="en">example.com</url>
<url language="de">example.de</url>
<url language="fr">example.fr</url>

Initialization After Webspace Change

php bin/console cache:clear
php bin/console sulu:document:initialize
php bin/console sulu:phpcr:init --user=admin
php bin/console sulu:webspace:copy-locale main --from=en --to=de

sulu:webspace:copy-locale copies the page tree structure from one language to another — helpful when adding new language to already populated site.

Timeline

Adding second language to working site: 1–2 days. Multisite setup with two Webspaces from scratch: 2–3 days. Full configuration (3 languages, 2 Webspaces, shadow pages, hreflang, navigation): 4–5 days.