Implement Localization in your App Extensions

When localizing your app, you may need to display text in the user's chosen language. Users can set different languages for different interfaces. For example, they may choose one language for the dashboard, but allow their site users to set a different language for the site. This means your app needs to retrieve the chosen settings for each extension.

The essentials SDK's i18n submodule allows app extensions to access the active language and locale settings of a site's interfaces.

This article outlines the recommended steps for using the SDK to display localized text in different parts of your app.

Note: This article uses the FormatJS' react-intl package, but you may use any other localization library. Make sure to install your chosen package in your app before starting to code.

Step 1 | Prepare translations

For each language your app supports, create a JSON file containing all your app's strings.

Notes:

For example, to support English and Spanish, create 2 JSON files:

src/intl/messages/en.json

Copy
{ "greeting": "Hello, {name}!", "welcome": "Welcome to our app!", "todayIs": "Today is {ts, date, ::yyyyMMdd}" }

src/intl/messages/es.json

Copy
{ "greeting": "Hola, {name}!", "welcome": "Bienvenido a nuestra aplicación!", "todayIs": "Hoy es {ts, date, ::yyyyMMdd}" }

Step 2 | Create a function to load the messages JSON file

Create and export a helper function that loads the correct messages JSON based on the extension's language:

  1. Create a file to host the helper function. For example, src/intl/load-messages.js.
  2. Import i18n from @wix/essentials:
    Copy
    import { i18n } from "@wix/essentials";
  3. Create and export a function that calls i18n.getLanguage() and returns the correct messages JSON based on the language retrieved.
    For example:
    Copy
    export async function loadMessages() { switch (i18n.getLanguage()) { case "es": return (await import("./es.json")).default; default: return (await import("./en.json")).default; } }

At the end of this step, your file should look similar to this:

src/intl/load-messages.js

Copy
import { i18n } from "@wix/essentials"; export async function loadMessages() { switch (i18n.getLanguage()) { case "es": return (await import("./es.json")).default; default: return (await import("./en.json")).default; } }

Step 3 | Initialize intl

This step is implemented differently depending on whether your app's extensions use React or plain JavaScript.

React

Render your extensions using the IntlProvider component from the react-intl package. To avoid repeating this code for each of your apps' extension, create a higher order component (HOC) that you can use to wrap your extensions with IntlProvider:

  1. Create a component called withIntlProvider. This is your HOC that will wrap your extensions with IntlProvider.
  2. Create a state variable.
  3. Using useEffect(), when the component loads, call loadMessages() that you defined in Step 2, and then save the response in your state variable.
  4. If loadMessages() doesn't return a value, the component should return null.
  5. Return an IntlProvider component with the following props:
    • Pass the loaded messages to the messages prop.
    • Pass the value of i18n.getLocale() to the locale prop.
    • Set the default locale.
    • Pass <Component {...props} /> as the children prop.

At the end of this step, your file should look like this:

src/intl/withIntlProvider.jsx

Copy
import { i18n } from "@wix/essentials"; import React from "react"; import { IntlProvider } from "react-intl"; import { loadMessages } from "./load-messages"; // Edit according to where you saved your file. export function withIntlProvider(Component) { return (props) => { const [messagesInLanguage, setMessagesInLanguage] = React.useState(null); React.useEffect(() => { loadMessages().then((data) => { setMessagesInLanguage(data); }); }, []); if (!messagesInLanguage) { return null; } return ( <IntlProvider messages={messagesInLanguage} locale={i18n.getLocale()} defaultLocale="en" > <Component {...props} /> </IntlProvider> ); }; }

Plain JavaScript

You will need an intl object instance to format your messages. To create this instance:

  1. Create a file, such as src/intl/create-intl.js and add the following import statements:
    Copy
    import { i18n } from "@wix/essentials"; import { createIntlCache, createIntl as createIntlCore } from "react-intl"; import { loadMessages } from "./load-messages"; // Edit according to where you saved your file.
  2. Create and export a function called createIntl.
  3. Your createIntl function should return the result of calling createIntlCore() and passing the following parameters.

At the end of this step, your file should look like this:

src/intl/create-intl.js

Copy
import { i18n } from "@wix/essentials"; import { createIntlCache, createIntl as createIntlCore } from "react-intl"; import { loadMessages } from "./load-messages"; export async function createIntl() { return createIntlCore( { locale: i18n.getLocale(), messages: await loadMessages(), }, createIntlCache(), ); }

Step 4 | Display localized text in your app's extensions

Retrieve and render localized text.

This step is implemented differently depending on whether your app's extensions use React or plain JavaScript.

React

To display localized code, wrap your extension with the withIntlProvider() function that you defined in Step 3, and use the FormattedMessage component to display your text.

Note: You only need to wrap the components in your extension that need to access the localized text. However, wrapping your main app extension component with withIntlProvider() makes further development smoother.

Example

The following example uses FormattedMessage with the following props:

  • id: The key of the message you want to render from your JSON file.
  • defaultMessage: The message to render if no message is retrieved.
  • values: An object of the variable values to use in the message.

src/dashboard/pages/page.tsx

Copy
import React from "react"; import { withIntlProvider } from "../intl/withIntlProvider"; function MyAppPage() { return ( <h1> <FormattedMessage id="welcome" defaultMessage="Welcome to our app!" /> </h1> <h3> <FormattedMessage id="greeting" defaultMessage="Hello, {name}!" values={{ name: "John" }} /> </h3> <p> <FormattedMessage id="todayIs" defaultMessage="Today is {ts, date, ::yyyyMMdd}" values={{ ts: Date.now() }} /> </p> ); } export default withIntlProvider(MyAppPage);

Plain JavaScript

To display localized code:

  1. Call the createIntl() function that you defined in Step 3 to create an intl instance
  2. Use the formatMessage property to return your translated text.

For example:

src/site/embedded-scripts/my-script/index.ts

Copy
import { createIntl } from "../../../intl/create-intl"; const intl = await createIntl(); console.log( intl.formatMessage( { id: "todayIs", defaultMessage: "Today is {ts, date, ::yyyyMMdd}" }, { ts: Date.now() }, ), );
Did this help?