diff --git a/ccm-cms/src/main/java/org/librecms/ui/CmsAdminMessages.java b/ccm-cms/src/main/java/org/librecms/ui/CmsAdminMessages.java
new file mode 100644
index 000000000..cab590d6f
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/CmsAdminMessages.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.ui;
+
+import org.libreccm.ui.AbstractMessagesBean;
+import org.librecms.CmsConstants;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Named;
+
+/**
+ * Provides simple access to the messages in the cms admin bundle. The make it
+ * as easy as possible to access the messages this class is implemented as a map
+ * a made available as named bean. For simple messages,
+ * {@code CmsAdminMesssages} can be used like a map in a facelets template:
+ *
+ *
+ * #{CmsAdminMessages['some.message.key'])
+ *
+ *
+ * Messages with placeholders can be retrieved using
+ * {@link #getMessage(java.lang.String, java.util.List)} or
+ * {@link #getMessage(java.lang.String, java.lang.Object[])}.
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("CmsAdminMessages")
+public class CmsAdminMessages extends AbstractMessagesBean {
+
+ @Override
+ protected String getMessageBundle() {
+ return CmsConstants.CMS_ADMIN_BUNDLE;
+ }
+
+}
diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/cms.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/cms.xhtml
deleted file mode 100644
index 8b1378917..000000000
--- a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/cms.xhtml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/content-sections.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/content-sections/content-sections.xhtml
similarity index 100%
rename from ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/content-sections.xhtml
rename to ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/content-sections/content-sections.xhtml
diff --git a/ccm-core/src/main/java/org/libreccm/ui/AbstractMessagesBean.java b/ccm-core/src/main/java/org/libreccm/ui/AbstractMessagesBean.java
new file mode 100644
index 000000000..9cdf5661f
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/ui/AbstractMessagesBean.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2021 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.ui;
+
+import org.libreccm.l10n.GlobalizationHelper;
+
+import java.text.MessageFormat;
+import java.util.AbstractMap;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Base class for implementing a named bean which provides easy access to a
+ * resource bundle from an MVC template.. The only method to implement is
+ * {@link #getResourceBundle} which provides the name of the resource bundle to
+ * use. An implementation must also be annotated with the following annotations:
+ *
+ *
A scope annotation like {@link RequestScoped} or
+ * {@link ApplicationScoped}.
+ *
The {@link Named} annotation to make the implementing bean available in
+ * MVC templates.
+ *
+ *
+ * By default the name under which the bean will available is the simple class
+ * name, but starting with a lower case letter. A different name can be provided
+ * using the value of the {@link Named} annotation. For example by default the
+ * bean {@code com.example.ui.ExampleMessages} would be available as
+ * {@code exampleMessages}. To make it available as {@code ExampleMessages} the
+ * {@code @Named} annotation has to look like this:
+ * {@code @Named("ExamplesMessages")}. This bean will be used to illustrate the
+ * usage of an implementation in the following examples.
+ *
+ * For simple messages an implementation of this class can be used like a map in
+ * MVC templates, for example in a Facelets template to get the message with the
+ * key {@code some.message.key} you can simple write
+ *
+ * #{ExampleMessages['some.message.key']}
+ *
+ *
+ * Messages with placeholders can be retrieved using {@link {@link #getMessage(java.lang.String, java.util.List)} or
+ * {@link #getMessage(java.lang.String, java.lang.Object[])}, for example in a
+ * Facelets template
+ *
+ *
+ *
+ *
+ * @author Jens Pelzetter
+ */
+public abstract class AbstractMessagesBean extends AbstractMap {
+
+ /**
+ * Provides access to the locale negoiated by LibreCCM.
+ */
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ /**
+ * The {@link ResourceBundle} to use.
+ */
+ private ResourceBundle messages;
+
+ protected abstract String getMessageBundle();
+
+ /**
+ * Loads the resource bundle.
+ */
+ @PostConstruct
+ private void init() {
+ messages = ResourceBundle.getBundle(
+ getMessageBundle(),
+ globalizationHelper.getNegotiatedLocale()
+ );
+ }
+
+ /**
+ * Retrieves a message from the resource bundle.
+ *
+ * @param key The key of the message.
+ *
+ * @return The translated message or {@code ???message???} if the the key is
+ * not found in the resource bundle (message is replaced with the
+ * key).
+ */
+ public String getMessage(final String key) {
+ if (messages.containsKey(key)) {
+ return messages.getString(key);
+ } else {
+ return String.format("???%s???", key);
+ }
+ }
+
+ /**
+ * Retrieves a message with placeholders.
+ *
+ * @param key The key of the message.
+ * @param parameters The parameters for the placeholders.
+ *
+ * @return The translated message or {@code ???message???} if the the key is
+ * not found in the resource bundle (message is replaced with the
+ * key).
+ */
+ public String getMessage(
+ final String key, final List