Localisation in Contensis React Base v4
Log in to add to favouritesPage last updated 22 April 2026
Localisation
Localisation and internationalization, often abbreviated to "i18n", is supported from version 4 of Contensis React Base onwards.
Its aim is to support:
- loading of a "dictionary" or "catalog" of phrases for supported locales
- language switching that resolves and navigates to translated versions of content
- present the correct dictionary so static elements can be rendered in the correct language
Language codes follow the same format and casing as they appear in Contensis (IETF BCP 47 language tag with region subtag).
Configure
Adding a configuration key i18n with the required attributes populated in your app's client and server entrypoints should be enough to get up and running.
Create a configuration file with the config exported, we need to use it in two places:
// i18n.config.ts
import { I18nAppConfig } from '@zengenti/contensis-react-base/i18n';
export const i18n: I18nAppConfig = {
primaryLanguage: 'en-GB',
supportedLanguages: ['cy', 'en-GB', 'fr-FR', 'de-DE'],
locales: {
'en-GB': { title: 'Welcome' },
cy: { title: 'Croeso' },
'fr-FR': { title: 'Bienvenue' },
'de-DE': { title: 'Willkommen' },
},
};
Server
The config is added to the server config options:
// server.ts
import ZengentiAppServer from '@zengenti/contensis-react-base';
import ReactApp from '~/App';
import { i18n } from '~/locales/i18n.config';
ZengentiAppServer.start(
ReactApp,
{
i18n,
// ...other app config
Client
The config is also added to the client config options:
// client-entrypoint.ts
import { AppConfig } from '@zengenti/contensis-react-base';
import { i18n } from '~/locales/i18n.config';
const config: AppConfig = {
i18n,
routes: {
// ...app config continues
primaryLanguage
This is the language that is considered the primary language code for the application, e.g. en-GB.
supportedLanguages
These are the languages that are supported by the application.
Supply an empty array to fetch supportedLanguages from the Contensis project API.
locales
Optionally supply all your application locales.
The shape of the locales must be a keyed object with each key exactly matching the language code as presented in Contensis.
resolver
Instead of supplying all locales above, we can provide an async function where we can resolve and load our locale (file) for the current language only, the example below will also split each locale into its own bundle in a Webpack builds and dynamically load the correct locale when the current language is changed:
// Dynamic import of locale dictionaries
resolver: (language: string) =>
import(
/* webpackChunkName: "locale.[request]" */ `~/locales/${language}`
).then(({ default: dictionary }) => dictionary),
When the resolver function is called, the provided language argument is the language we are changing to.
The resolver function can be coded to suit the layout of your application:
resolver: async (language: string) => {
let dictionaryFile = '';
// Multiple regions for a language map to the same dictionary
switch (language) {
case 'de-DE':
dictionaryFile = 'german';
break;
case 'fr-FR':
case 'fr-BE':
dictionaryFile = 'french';
break;
case 'nl-NL':
case 'nl-BE':
dictionaryFile = 'dutch';
break;
case 'en-CA':
case 'en-GB':
case 'en-IN':
case 'en-US':
default:
dictionaryFile = 'english';
break;
}
console.log(`Loading dictionary file: ${dictionaryFile}`);
const dictionary = await import(
/* webpackChunkName: "locale.[request]" */ `./languages/${dictionaryFile}`
).then(({ default: dictionary }) => dictionary);
return dictionary;
},
Routes
Site View content
Translated entry versions are automatically resolved when the updateLanguage action is called from within a language switcher component.
- The existing entry loaded into routing state has a
sys.availableLanguagesarray - If the language we are switching to has an entry available in that language
- call the Delivery API and resolve the
sys.urifor the translated entry
- call the Delivery API and resolve the
- If no entry translation exists
- use a provided
fallbackPath, or fall back to the language site view root, e.g./en-ca
- use a provided
- Navigate to the translated URI
- avoid navigation if you need to by providing option
redirect: false - navigate to the same page with
fallbackPath: window.location.pathname
- avoid navigation if you need to by providing option
Static routes
Static routes configured in your app that don't have translated entry versions in Site View can be handled by either:
- Copying and pasting the route, changing the
- Static routes also accept a
languageattribute to set the current language automatically
pathfor each language variation. - Static routes also accept a
- Supplying a
i18nattribute with the main route which contains the locale language code and the translated path for that locale
// staticRoutes.ts
{
path: '/search/:facet?',
component: Search,
searchOptions: {
facet: facets.all,
},
i18n: {
'en-GB': '/search/:facet?',
cy: '/chwilio/:facet?',
'fr-FR': '/recherche/:facet?',
'de-DE': '/suche/:facet?',
},
},
Having these i18n translated paths available means we can redirect to the localised path should the updateLanguage action be triggered where we've rendered that route
Render
It wouldn't be React without a good hook to wrap it all up so we can use the functionality in our components
useI18n hook
The hook should provide everything needed to render translated content and trigger language changes/navigation however it is not essential should you require another solution.
All redux elements are available for import from /i18n should you wish to roll your own solution or integrate a best-of-breed package such as react-i18next
translate
Use the translate function to render the phrases from the current locale dictionary
import { useI18n } from '@zengenti/contensis-react-base/i18n';
const TitleComponent = () => {
const { translate } = useI18n<Dictionary>();
return <h1>{translate('title', 'Fallback title')}</h1>;
};
The Dictionary type specifies the shape of your dictionary configured for each locale and feeds into the allowed keys for the translate function
Provide a safe default to fallback to should the app fail to load or is presented with an unknown locale
updateLanguage
This is the most important function for a language switcher component:
- We might render a list of buttons or links, with a flag next to each, or a dropdown containing language choices.
- Call the
updateLanguagefunction from theonClickevent, providing the language code (and any navigation options)
import React from 'react';
import { useI18n } from '@zengenti/contensis-react-base/i18n';
export const LanguageSwitch = () => {
const { updateLanguage } = useI18n();
return (
<>
<div>
<button
onClick={() => {
updateLanguage('en-GB');
}}
>
Set language to en-GB
</button>
</div>
<div>
<button
onClick={() => {
updateLanguage('cy', {
fallbackPath: window.location.pathname,
});
}}
>
Set language to cy
</button>
</div>
</>
);
};
Search package
Search is configured with facets (and listings). The keys we assign to the facets configuration are used to drive route parameters when we perform actions such as switching facet, or applying filters.
// search.config.ts
news: {
title: 'News Search',
i18n: {
'en-GB': 'news',
cy: 'newyddion',
'fr-FR': 'nouvelles',
'de-DE': 'nachrichten',
},
filters: {
category: {
title: 'Category',
i18n: {
'en-GB': 'category',
cy: 'categori',
'fr-FR': 'categorie',
'de-DE': 'kategorie',
},
contentTypeId: 'category',
fieldId: 'category.sys.id',
items: [],
},
},
// ...config continues
},
Facet parameter
An i18n option can be added to a facet (or listing) configuration to provide the locales and their translated equivalents.
Filters
As with facets, the i18n option is supported when configuring filters in a search facet to localise the filter parameters used when navigating to the next route containing the selected filters.