menu

Multiple Languages

Supporting multiple languages, including fallbacks

Support the documentation

This documentation was written by volunteers. Please help support our effort:

Help us improve the documentation

Neos supports content in multiple languages through a concept called content dimensions. On this page, we'll focus on the common case of having a single language dimension to achieve a multi-lingual website. Later on, we'll generalize this for arbitrary dimensions.

Concepts

Here, we'll explain the concept of node variants and fallbacks.

Introducing Node Variants

So far, we have explained the Content Repository as a big tree of nodes. It turns out that this is not 100% correct: At each tree node, there might be multiple so-called node variants, so one node variant for German and one for English.

When running Neos with just a single language, every node has exactly one node variant.

Using node variants, you can easily find all other variants of a given node, i.e. from the German version of a page, link to the English version of the same page. This allows to build good language menus.

Furthermore, the concept of Node Variants allows arbitrary translation directions: It is common that some pages might exist only in German, others only in English, while the majority of pages exists in both languages.

Introducing Language Fallbacks

Aside from disjunct languages (i.e. languages which have nothing in common), Neos also supports languages which are pretty similar to each other, like British English and American English. In this case, you probably do not want to maintain your full content in both languages, but on the other hand, certain teaser and introduction texts should be customized to each individual audience.

Using language fallbacks, you can define e.g. that British English falls back to American English. In practice, this means if content is available in British English, it will be shown; whereas all other content is shown from American English.

We usually visualize this like layers in a photo editing application: The final rendering is a combination of the base layer (i.e. American English in our example), plus the British English layer on top.

Make sure to remove the Neos.Demo Site

Before you start, make sure to remove the Neos.Demo site from your distribution, as it contains numerous language definitions which might interfere with the definitions we're doing below.

Configuring a bilingual site

We first start with a bilingual site, and later extend it with fallbacks.

First, let's open the Configuration/Settings.yaml of your Site package and configure a German and English dimension:

Neos:
  ContentRepository:
    contentDimensions:
      language:                      # (1)
        label: 'Language'
        default: en_US               # (2)
        defaultPreset: en            # (3)
        presets:
          en:                        # (4)
            label: 'English (US)'
            values:                  # (5)
              - en_US
            uriSegment: ''           # (6)
          de:                        # (7)
            label: German
            values:
              - de
            uriSegment: de
  • We define a content dimension called language at (1).
  • The language dimension consists of two presets en (4) and de (7). A preset is what you select in the Neos User Interface to choose which content you want to see.
  • Each preset configures a label which is used in the User Interface and (optionally) in the Dimension Menu, a list of values (5) (more on that a few lines down), and an uriSegment (6) with which URIs will be prefixed with.
  • An empty uriSegment is allowed (6); so in the example above, the English website is available directly at /, while the German website starts with URLs at /de.
  • Crucially important are the default and defaultPreset keys: They define what language you get when visiting the root of the website; and when logging into the backend. So the default (2) must reference the value en_US, while the defaultPreset (3) must reference the preset en.

So what's the matter with the values key? This is what is stored inside the node variant in the database. Below, we will use multiple values to configure language fallbacks.

uriSegment is not allowed to contain underscore (_)

The property uriSegment is not allowed to contain any underscores. As an example, a valid identifier is de-DE, but not de_DE.

There's an open bug report which explains the background of that.

Clear your cache after changing the dimension configuration

You may have to clear your cache using ./flow flow:cache:flush after adjustments to the content dimensions, because e.g. URLs have to be regenerated after modifications.

With the above configuration, when we refresh our website, our complete website has no visible content anymore, as the content in the system is not yet moved to the language dimension.

This can be done with a node migration which is included in the Neos.ContentRepository package:

Migrate existing content

./flow node:migrate 20150716212459

Now, every content in the system is moved to language en_US, because this is the dimension's default in the configuration above.

This means your content is visible again in en_US, and you can start translating from en_US to de.

Confused with Presets / Values / Variants / ...?

Yeah. In fact, we are as well :-) With the Event-Sourced Content Repository rewrite, this terminology here will change a bit to be more consistent and intuitive.

Configuring a Fallback Language

We will now take the example from above and extend it with British English, which should fall back to American English:

Neos:
  ContentRepository:
    contentDimensions:
      language:
        label: 'Language'
        default: en_US
        defaultPreset: en
        presets:
          en:
            label: 'English (US)'
            values:
              - en_US
            uriSegment: ''
          de:
            label: German
            values:
              - de
            uriSegment: de

          ##### modification starts here
          en_UK:                    # (1)
            label: 'English (UK)'   # (2)
            values:                 # (3)
              - en_UK
              - en_US
            uriSegment: uk          # (4)
  • We define a new preset en_UK (1), and give it a human readable label for the User Interface (2).
  • The values now contain two elements (3) meaning: "Please use the content from en_UK, falling back to content from en_US".
  • We also need to configure an uriSegment (4).

That's it :) By configuring multiple values, we specify the fallback order.

Behind the Scenes

The language fallbacks are applied while reading the content from the CR. This means in the database, if you check for British English content, you'll see the language: en_UK value.

Translating Content

When you switch to a language in the Neos User Interface, there are two things which can happen:

  • If the page already exists in the target language, it is shown.
  • If the page does not exist yet, a popup appears where you can create this node variant.

Language variation creation dialog

 

For translating with language fallbacks, you simply switch to the British English version of the website. At first, the default (American English) content will shine through. As soon as a shine-through node is updated, it will be automatically copied to the British English variant.

Language Menu

So not that you created content in multiple languages you probably want to allow your users to select the language. So let's create a language menu.

You can use the Neos.Neos:DimensionsMenu to generate links to other variants of the current page by using this Fusion object:

languageMenu = Neos.Neos:DimensionsMenu {
    dimension = 'language'
}

In case you need to support more advanced cases like combinations of language and country, continue reading the chapter about content dimensions next.