Skip to main content

Localisation in Contensis React Base v4

Log in to add to favourites

Page 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:

TypeScript
// 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:

TypeScript
// 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:

TypeScript
// 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:

TypeScript
  // 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:

TypeScript
  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.availableLanguages array
  • If the language we are switching to has an entry available in that language
    • call the Delivery API and resolve the sys.uri for the translated entry
  • If no entry translation exists
    • use a provided fallbackPath, or fall back to the language site view root, e.g. /en-ca
    • avoid navigation if you need to by providing option redirect: false
    • navigate to the same page with fallbackPath: window.location.pathname
    Navigate to the translated URI

Static routes

Static routes configured in your app that don't have translated entry versions in Site View can be handled by either:

    • Static routes also accept a language attribute to set the current language automatically
    Copying and pasting the route, changing the path for each language variation.
  • Supplying a i18n attribute with the main route which contains the locale language code and the translated path for that locale
TypeScript
// 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

tsx
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 updateLanguage function from the onClick event, providing the language code (and any navigation options)
tsx
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.

TypeScript
// 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.

Still need help?

If you still need help after reading this article, don't hesitate to reach out to the Contensis community on Slack or raise a support ticket to get help from our team.
New support request