Localize the React Minimal Site Sample

Introduction

This tutorial discusses how to display a localized version of the React minimal site sample. The goal of this exercise is to modify the application so that it can support multiple languages at the same time and allow users to switch between them easily. The languages are not defined in the application code; rather, they are determined by what is downloaded from the server at build time.

What This Tutorial Includes

This tutorial includes the following:

What This Tutorial Does Not Include

This is what this tutorial does not include:

The Original React Minimal Site Sample

If you’re unfamiliar with the application discussed in this tutorial, we recommend that you first have a look at the original, English-only version running on the demo site.

You can find the source code for the original application on GitHub.

For an in-depth look at the English-only application and how it was written, have a look at the tutorial.

How the Untranslated React Minimal Site Sample Works

The existing sample is a simple three-page application. It has a home page, a Contact Us page, and a People page.

Structure of the Untranslated Minimal Site Sample Data

The minimal site sample uses the following Oracle Content Management asset types:

The single-language minimal site sample application consists of the following assets and relationships:

This application code uses predefined slugs as identifiers to reference pages or data sections that need special handling. The people page is built using a different asset type than the home and contact-us pages, but the code uses its slug as a way to distinguish it.

How the Untranslated Minimal Site Sample Loads Data from the Oracle Content Management Server

The React minimal site sample uses two methods to load data: REST calls to Oracle Content Management via the Content SDK and POST calls to the GraphQL endpoint of Oracle Content Management using the Apollo library. One quirk in the application is that all pages loaded by entering an explicit URL are rendered on the server side, while all pages loaded by clicking on a link will be loaded and rendered by code running in the web browser.

Changes Needed to Create a Localized Copy of the Application

Structure of the Localized Asset Data

No changes are needed to the Oracle Content Management asset data types. All the visible translated text can be stored in assets using the existing types.

For the purpose of this exercise, an automated tool was used to create translations of the base data into Canadian French, Spanish (Spain), and Simplified Chinese. This data may have some inaccuracies due to a lack of context in the automated process, but will suffice for this exercise.

As mentioned in the introduction, each translated locale was given its own complete asset tree which can be processed independently of the source language. For example, the English asset tree looks like this:

   MinimalMainGraphQL (slug: minimalmaingraphql)
      Home-Page (slug: home)
            Home-Announcement
            Home-Section1
            Home-Section2
      Contact-Us-Page (slug: contact-us)
            ContactUs-Announcement
            ContactUs-Section1
            ContactUs-Section2
      People-Page (slug: people)
            People-Announcement
            List-of-Person-Assets

and the Spanish one looks like this (note the appended ’_es’ locale in the slugs):

   MinimalMainGraphQL-ES (slug: minimalmaingraphql_es)
      Home-Page-ES (slug: home_es)
            Home-Announcement-ES
            Home-Section1-ES
            Home-Section2-ES
      Contact-Us-Page-ES (slug: contact-us_es)
            ContactUs-Announcement-ES
            ContactUs-Section1-ES
            ContactUs-Section2-ES
      People-Page-ES (slug: people_es)
            People-Announcement-ES
            List-of-Person-Assets-ES

Note how the slugs of the MinimalMain and Page assets share a common root across languages. This is for the following reasons:

Note: Using slugs as special identifiers is not an optimal solution for a localized application and was done in this case to make the asset types and instances as simple as possible. Using different asset types to distinguish pages or adding an untranslatable field would be a more secure approach. Alternatively, taxonomies could be used for the same purpose at the expense of greater code complexity.

Code Modifications Needed to Display a Localized React Minimal Site Sample

The only visible change in the application is to add a locale switching control in the page header. (See the description of the Header.jsx file below). Since all translations are published on the same channel token, there’s no need to support multiple channels for data acquisition.

Below are some of the source code changes required to support multiple locales.

/src/pages/App.jsx

Modified functions

render — This now redirects the ‘/’ link to the English home page using the pattern defined in src/Pages/Routes.js. It now redirects ‘/’ to ‘/page/en/home’ instead of ‘/page/home’.

/src/pages/Routes.js

The routes used for the Home, Contact-Us, and People pages now contain the locale of the pages as well as the slug of the individual pages. This was done to make the locale easily discoverable based on the URL. The slugs do partially encode the language so this could have been left out, but it does make the code simpler.

/src/scripts/services.js

This set of utility functions performs the calls to the Content SDK to retrieve server data for the application. Originally, it was built on the idea that there would be one MinimalMain asset that would contain the references to all the pages that the application should know about. This is no longer the case with localization, as each language has its own MinimalMain asset that contains a list of localized pages. Rather than rework the application to support multiple lists of pages, it was decided to have the application get the list of all MinimalMain assets that have a slug starting with “minimalmaingraphql” and then load each one in turn. The list of all pages defined in each MinimalMain instance is then merged into one list and passed into the application for processing.

New functions

getAllMains — Gets a list of all the MinimalMain assets on the server that have a slug starting with “minimalmaingraphql.” Each localized copy of the data has its own MinimalMain asset that matches this pattern. Since the list of these assets is not hard-coded in the client, it’s not necessary to maintain a list of predefined locales to display. Instead, the application can determine this information based on the list of MinimalMain assets that the server returns to it.

Modified functions

fetchOceMinimalMain — It now calls getAllMains and loads each one of the returned slugs in turn. It then merges the page lists from all the returned assets into one asset and returns it. Previously it would load one asset only by slug name and return that. The footer logo and header logo are still derived from the first MinimalMain asset loaded so if these were localized, the code would not currently handle them being displayed differently for each language. Instead, the application would have to be modified to manage the set of all MinimalMain assets.

/src/components/Header.jsx

The only GUI change made in this component is to add a selection box to choose which language should be displayed. This serves two purposes: when a new language is selected, it will change the menu to display only the links for the pages in the new locale, and then it will reload the current page in the new locale. For example, if the user is viewing the French home page and then selects Chinese, it will remove the French page links, display the Chinese page links, and then navigate to the Chinese home page.

New functions

componentDidMount — This function now initializes the default language in the dropdown to English. It used to select the first language defined in the list.

onLanguageSelected — This is the callback for the language dropdown selector. It causes the page menu to update to display the pages for the newly selected language. It also checks the current page and loads its equivalent in the new language.

updatePageLinksForLocale — This is a function that hides the page links for all languages except the currently selected one.

Modified functions

constructor — The constructor now builds a map of languages to page URLs. This is used when changing the language to give the application an idea of what page is equivalent to the current page in the new language. Note that the list is positional, so if the MinimalMain asset of a translation reorders its list of page assets, then this map will no longer work. It maps the nth page in the current locale to the nth page in the new locale.

render — This now builds the language selector based on what languages are found in the set of current pages. It also generates the links for each page so that it matches the layout defined in the React Router config in src/pages/Routes.js.

/src/styles/styles.css

Add a style used to hide languages that are not in use.

Views of the Localized Sample Site

Here are a few images showing different pages in varying locales. Note that there is text in the header and footer of each page that is untranslated. This is because the text is part of an image, not free text. It would have been possible to create localized versions of the image as well if this was intended to be a production site.

The Spanish home page:

The Spanish home page

The French People page:

The French People page

The Chinese home page:

The Chinese home page