On September 25th, 2024, we released v2 of the Apps SDK. To learn what’s new and how to upgrade, see Migration FAQ and Migration guide.

Migrate an existing app

How to add localization support to an existing app.

This procedure describes how to update an existing app to support Canva’s translation process.

The Canva translation process currently requires use of the react-intl library.

Phase 1: Configure your workspace

This phase explains how to configure your environment to use the recommended i18n tools.

Step 1: Install prerequisites

  1. Install the react-intl, @formatjs/cli, and @canva/app-i18n-kit packages, as well as the latest version of @canva/app-ui-kit:

    npm install @canva/app-i18n-kit react-intl @canva/app-ui-kit@latest && npm install --save-dev @formatjs/cli
    SHELL

    To be able to localize components, your app must use version 4 or later of the App UI Kit. Older versions of the App UI Kit don't support localization.

Step 2: Configure ESLint

This step explains how to install and configure ESLint. For more information, see the eslint-plugin-formatjs plugin documentation(opens in a new tab or window).

  1. Install the eslint-plugin-formatjs plugin. This provides linting rules for the FormatJS library.

    npm install --save-dev eslint eslint-plugin-formatjs
    SHELL
  2. Add the recommended rules to your eslint.config.mjs:

    import formatjs from "eslint-plugin-formatjs"
    //...
    plugins: {
    formatjs,
    //...
    rules: {
    "formatjs/no-invalid-icu": "error",
    "formatjs/no-literal-string-in-jsx": [
    2,
    {
    props: {
    // These rules are for @canva/app-ui-kit components.
    // For your own components, suppress any false positives using eslint ignore comments.
    include: [
    ["*", "(*Label|label|alt)"],
    ["*", "(title|description|name|text)"],
    ["*", "(placeholder|additionalPlaceholder|defaultValue)"],
    ["FormField", "error"],
    ],
    exclude: [["FormattedMessage", "description"]],
    },
    },
    ],
    "formatjs/enforce-description": ["error", "literal"],
    "formatjs/enforce-default-message": ["error", "literal"],
    "formatjs/enforce-placeholders": "error",
    "formatjs/no-id": "error",
    "formatjs/no-emoji": "error",
    "formatjs/no-useless-message": "error",
    "formatjs/no-multiple-plurals": "error",
    "formatjs/no-offset": "error",
    "formatjs/blocklist-elements": [2, ["selectordinal"]],
    "formatjs/no-complex-selectors": "error",
    JS

Step 3: Configure webpack for FormatJS

  1. Configure the FormatJS TS transformer to automatically generate IDs. To do this, add the @formatjs/ts-transformer to your webpack configuration:

    npm install --save-dev @formatjs/ts-transformer
    SHELL
  2. To generate unique message IDs, add a transformer to your webpack.config.js. This example adds a function called getCustomTransformers:

    const { transform } = require("@formatjs/ts-transformer");
    // ...
    module: {
    rules: [
    {
    test: /\.tsx?$/,
    exclude: /node_modules/,
    use: [
    {
    loader: "ts-loader",
    options: {
    transpileOnly: true,
    getCustomTransformers() {
    return {
    before: [
    transform({
    overrideIdFn: "[sha512:contenthash:base64:6]",
    }),
    ],
    };
    },
    },
    },
    ],
    JSON
  3. In package.json, add an extract script with the following contents:

    formatjs extract \"src/**/*{ts,tsx}\" --out-file dist/messages_en.json
    JSON

    Append the extract script to the build script. For example:

    "scripts": {
    "start": "ts-node «/scripts/start/start.ts",
    + "extract": "formatjs extract \"src/**/*{ts,tsx}\" --out-file dist/messages_en.json",
    + "build": "webpack --config webpack.config.js --mode production && npm run extract",
    - "build": "webpack --config webpack.config.js --mode production",
    "lint: types": "tsc",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    DIFF

Step 4: Configure webpack chunk limit

Canva's app submission process requires a single app bundle. Implementing localization might increase the size of the app and cause webpack to split the bundle into multiple chunks.

To make sure webpack produces a single bundle, use the following steps:

  1. Open webpack.config.js, and add the optimize module to the webpack import:

    - const { DefinePlugin } = require("webpack");
    + const { DefinePlugin, optimize } = require("webpack");
    DIFF
  2. In webpack.config.js, add the chunk limit before buildDevConfig is called. Modify this plugins definition(opens in a new tab or window):

    plugins: [
    new DefinePlugin({
    BACKEND_HOST: JSON.stringify(backendHost),
    }),
    + new optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
    ],
    DIFF

Phase 2: Updating your app

This phase explains how to update your app to support localization.

Step 1: Add the dependency

Add @canva/app-i18n-kit to your app. You don't need to pass messages or locale into AppI18nProvider, because it internally detects the user's locale and loads the appropriate translated messages. In addition, it also lets you test localization in a later step.

// index.tsx
import { AppUiProvider } from "@canva/app-ui-kit";
import { createRoot } from "react-dom/client";
import { App } from "./app";
import "@canva/app-ui-kit/styles.css";
import { AppI18nProvider } from "@canva/app-i18n-kit";
const root = createRoot(document.getElementById("root") as Element);
function render() {
root.render(
<AppI18nProvider>
<AppUiProvider>
<App />
</AppUiProvider>
</AppI18nProvider>
);
}
TS

Don't use IntlProvider from react-intl, since AppI18nProvider is a Canva-specific replacement for IntlProvider.

Step 2: Update UI strings

The react-intl package lets you use FormattedMessage or the useIntl() hook. We recommend using FormattedMessage wherever possible.

In your app’s code, update the UI strings to use FormattedMessage. For example:

<Text>
<FormattedMessage
description="Label text for a button. Explains that the image will rotate when pressed"
defaultMessage="Rotate the image"
/>
</Text>
TS

If using the useIntl() hook:

const intl = useIntl();
// ...
<Button
variant="primary"
icon={RotateIcon}
ariaLabel={intl.formatMessage({
defaultMessage: "Rotate the image",
description:
"Label text for a button. Explains that the image will rotate when pressed",
})}
/>
TS

Phase 3: Testing and release

This phase explains how to test localization in your app and prepare it for release.

Step 1: Testing localization

The @canva/app-i18n-kit package lets you test your app using pseudolocalization and lets you change the locale using the Developer menu. This gives you a preview of what your app could look like in a different locale.

The text lets you read your original English text, but inserts non-ASCII characters and adds width to each string to simulate lengthier languages.

This approach is only compatible with our recommended tooling.

  1. Enable pseudolocalization in the Developer menu. This shows you a preview of how different characters and text lengths affect the UI:

    Developer menu with the pseudolocalize option selected

    • Check that any custom components respect the left-to-right and right-to-left setting.
    • Make sure there are no rendering issues or truncated text.

You can’t preview or test actual translations, since these are only available after your app has been approved. However, you can then preview the translations before releasing your app.

Step 2: Generate the JSON file

This process extracts all the FormattedMessage strings in your code and saves them to a JSON file. You can then upload this file to Canva for translation.

The recommended tooling automatically generates files in the required format. To generate the messages_en.json file, run the following command:

npm run build
SHELL

Once this processed has finished, you should see a messages_en.json file appear in the dist directory, alongside your app.js. If this doesn't appear, re-run this step.

Step 3: Upload the JSON file

As part of the app submission process, you can upload the JSON file for translation:

  1. Locate your app in the Developer Portal.
  2. Upload the messages_en.json file, using the Translations file input.

Next steps

Canva reviews your app and identifies which locales should be supported. Canva then performs the translation of the supplied strings. You’ll be automatically notified once Canva has finished the translation process. You can then preview your app in the supported locales.

More information