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:
A description of how the original minimal site sample data is laid out on the server and how its assets are interconnected.
How the localized versions of the data are laid out on the Oracle Content Management server for retrieval by the application.
A discussion of how the React minimal site sample gets its data from the Oracle Content Management server and how this affects localization.
A brief overview of the code changes needed to make the client locale aware and allow it to support changing languages.
What This Tutorial Does Not Include
This is what this tutorial does not include:
Details about how the sample data should be translated on the server. There are multiple options available to complete this task, both manual and automated. This tutorial was developed using localized data with the following format: all assets have a localized version, even assets that are only used in a structural role and don’t contain translatable data. This allows the asset tree for any language to be traversed the same way without reference to the original English content.
This tutorial is not a deep dive into localizing a React application. Issues such as right-to-left locales (for example, Hebrew and Arabic) or special formatting for dates, currencies and the like are not addressed here. The goal is to take a simple React application that pulls its data from Oracle Content Management and modify it to support multiple locales in as simple a manner as possible.
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:
MinimalMain — This is used as the entry point to the application. It contains a list of all the pages that should be displayed.
MinimalPage — This type models a generic page consisting of a set of one or more MinimalSection type instances. The data on these pages is obtained using the Oracle Content Management REST interface via the Oracle Content SDK.
PeoplePage — This defines a page consisting of a MinimalSection type containing the page header as well as a list of Person assets containing details about individual people. This page uses Apollo to POST GraphQL calls to Oracle Content Management’s GraphQL interface.
MinimalSection — This asset type contains the information used to display a portion of a page. This could be a header with a background image or simply a block of HTML data.
Person — This is a simple asset type containing an image of a person as well as some text information about them. Since it’s used exclusively in the PeoplePage context, it’s loaded by using Apollo to make calls to Oracle Content Management’s GraphQL interface.
The single-language minimal site sample application consists of the following assets and relationships:
A single instance of the MinimalMain asset is identified by a specific slug value. This in turn contains a list of references to all page assets (either MinimalPage or PeoplePage) in the application. In the case of the minimal site sample, there are two MinimalPage assets (home, contact-us) and one PeoplePage asset for the people page.
Each MinimalPage contains a list of MinimalSection type instances.
Each MinimalSection instance contains either an image and text used to display a page header or an arbitrary block of HTML used to render data in the page.
Each PeoplePage consists of a single MinimalSection instance that’s used for the page header and a list of person assets.
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:
Instead of retrieving one MinimalMain asset by slug, the application can now get a list of all MinimalMain assets with a slug matching a pattern. There is one such asset for each language. Looping over this list allows the application to process the asset tree of each language in turn.
As mentioned previously, slugs are used as special identifiers that control the role of the page assets. Giving all the home pages a slug of a common pattern (home_locale ) allows the code to identify and perform special processing of any page regardless of the language used.
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 French People page:
The Chinese home page:
Localize the React Minimal Site Sample
F62071-01
August 2022
Copyright © 2021, 2022, Oracle and/or its affiliates.
Primary Author: Oracle Corporation