> Shopify Plus:
> [Checkout UI extensions](/docs/api/checkout-ui-extensions) that render on the information and shipping and payment steps in checkout are available only to stores on a [Shopify Plus](https://www.shopify.com/plus) plan.
In this tutorial, you'll use JavaScript API functions to localize an extension that displays a customer's loyalty point balance. You'll localize the extension text, the number format of the loyalty points balance, and the monetary value of the points. You'll also provide translations for singular and plural values. You can use what you learn here to localize other extensions.
## What you'll learn
In this tutorial, you'll learn how to do the following tasks:
- Create a checkout UI extension that renders some text in the checkout flow with some basic localization.
- Run the extension locally and test it on a development store.
- Define translation data and localize the following elements:
- Numbers using a `formatNumber` function similar to the [`Intl` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)
- Currency using a `formatCurrency` `Intl` object
- Singular and plural values
- Deploy your extension code to Shopify.
## Requirements
- You've created a [Partner account](https://www.shopify.com/partners).
- You've created a new [development store](/docs/api/development-stores) with the following:
- [Generated test data](/docs/api/development-stores/generated-test-data)
- [Checkout and Customer Accounts Extensibility](/docs/api/developer-previews#checkout-and-customer-accounts-extensibility-developer-preview) developer preview enabled
- You've [created an app that uses Shopify CLI 3.0 or higher](/docs/apps/build/scaffold-app).
- You're familiar with [how localization works for checkout UI extensions](/docs/apps/build/checkout/localized-checkout-ui-extensions).
- [You've added and published a second language](https://help.shopify.com/en/manual/markets/languages/manage-languages) to your development store.
- [You've activated the language](https://help.shopify.com/en/manual/markets/languages/manage-languages#manage-languages-for-markets) in your development store's primary market.
## Sample code
You can copy and paste the following code into your `index` file and add a few example values to get the extension to render in the browser.
The rest of the tutorial walks through this sample code step-by-step.
```jsx?title: 'src/Checkout.jsx'
import React from "react";
import {
reactExtension,
Text,
Banner,
useApi,
useTranslate,
} from "@shopify/ui-extensions-react/checkout";
/* This example assumes that you have the following contents in your `locales/en.default.json` file. You'll add this later in the tutorial:
{
"loyaltyPoints": {
"one": "You have {{formattedPoints}} loyalty point",
"other": "You have {{formattedPoints}} loyalty points"
},
"balanceRemaining": "Your balance is {{formattedBalance}}"
}
*/
// Set the extension target
export default reactExtension("purchase.checkout.block.render", () => );
function App() {
// Use i18n provided by the extension API
const { i18n } = useApi();
// Use the translate function
const translate = useTranslate();
const balance = 9.99;
// Localize the currency based on the current locale
const formattedBalance = i18n.formatCurrency(balance);
// Translate the balance remaining message
const balanceRemainingMsg = translate("balanceRemaining", {
// Provide a property/value that can be used in the translated message
formattedBalance,
});
const points = 10000;
// Localize the number based on the current locale
const formattedPoints = i18n.formatNumber(points);
// Translate the loyalty points message, using pluralization to differentiate messages
const loyaltyPointsMsg = translate("loyaltyPoints", {
// Specify a count property so that the correct pluralization key (in this example, "many", "one" or "other") will be resolved
count: points,
// Provide a property/value that can be used in the translated message
formattedPoints,
});
// Render the components
return (
{balanceRemainingMsg}
);
};
```
```js?title:'src/Checkout.js'
import { extension, Banner, Text } from "@shopify/ui-extensions/checkout";
/* This example assumes that you have the following contents in your `locales/en.default.json` file. You'll add this later in the tutorial:
{
"loyaltyPoints": {
"one": "You have {{formattedPoints}} loyalty point",
"other": "You have {{formattedPoints}} loyalty points"
},
"balanceRemaining": "Your balance is {{formattedBalance}}"
}
*/
// Set the extension target
export default extension("purchase.checkout.block.render", (root, { i18n }) => {
// Use i18n provided by the extension API
const balance = 9.99;
// Localize the currency based on the current locale
const formattedBalance = i18n.formatCurrency(balance);
// Translate the balance remaining message
const balanceRemainingMsg = i18n.translate("balanceRemaining", {
// Provide a property/value that can be used in the translated message
formattedBalance,
});
const points = 10000;
// Localize the number based on the current locale
const formattedPoints = i18n.formatNumber(points);
// Translate the loyalty points message, using pluralization to differentiate messages
const loyaltyPointsMsg = i18n.translate("loyaltyPoints", {
// Specify a count property so that the correct pluralization key (in this example, "many", "one" or "other") will be resolved
count: points,
// Provide a property/value that can be used in the translated message
formattedPoints,
});
// Render the components
const app = root.createComponent(Banner, { title: loyaltyPointsMsg });
app.appendChild(root.createComponent(Text, {}, balanceRemainingMsg));
root.appendChild(app);
});
```
```toml?title: 'shopify.extension.toml'
api_version = "2023-04"
[[extensions]]
name = "my-checkout-ui-extension"
description = "A UI extension"
handle = "my-checkout-ui-extension"
type = "ui_extension"
[[extensions.targeting]]
target = "purchase.checkout.block.render"
module = "./src/Checkout.jsx" # React OR "./src/Checkout.js" for JavaScript
```
## Step 1: Create a UI extension
> Note:
> If you already have a checkout UI extension that you want to localize, then you can skip to [step 2](#step-2-define-translations).
To create a checkout UI extension, you can use Shopify CLI, which generates starter code for building your extension and automates common development tasks.
1. Navigate to your app directory:
1. Run the following command to create a new checkout UI extension:
1. Select a language for your extension. You can choose from TypeScript, JavaScript, TypeScript React, or JavaScript React.
> Tip:
> TypeScript or JavaScript is suitable for smaller projects that require a more straightforward API. TypeScript React or JavaScript React is suitable when you want an easy model for mapping state updates to UI updates. With JavaScript or TypeScript, you need to map state updates yourself. This process is similar to writing an application targeting the DOM, versus using `react-dom`.
You should now have a new extension directory in your app's directory. The extension directory includes the extension script at `src/index.{file-extension}`. The following is an example directory structure:
1. Start your development server to build and preview your app:
To learn about the processes that are executed when you run `dev`, refer to the [Shopify CLI command reference](/docs/api/shopify-cli/app/app-dev).
1. Press `p` to open the developer console. In the developer console page, click on the preview link for your extension.
## Step 2: Define translations
To define translations, you'll adjust the `[locale].json` files in the `extensions//locales` folder within your app.
> Tip:
> In this tutorial, you'll keep French (`fr`, non-regional) as an available locale. However, you can also create translations for additional locales.
### Set the default locale
Your default locale specifies which locale Shopify should use when no other appropriate locale can be matched. In this example, English (`en`) is already the default locale. However, you can set any locale to be your default locale.
To change your default locale, go to the `locales` folder and change the `[locale].json` filename to `[locale].default.json`.
### Add translation strings
In this step, you'll add translations for different plural forms. You'll set translations for the `many`, `one` and `other` plural rules needed for French (`fr`), but you can specify any pluralization key that [`Intl.PluralRules.select()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/select) supports and that's appropriate for the locale.
In subsequent steps, you'll define [`balance`](#step-5-translate-the-balance-remaining-message) and [`points`](#step-6-translate-the-loyalty-points-message-with-plural-values) using a placeholder.
1. In `en.default.json`, add the following code:
```json
{
"loyaltyPoints": {
"one": "You have {{formattedPoints}} loyalty point",
"other": "You have {{formattedPoints}} loyalty points"
},
"balanceRemaining": "Your balance is {{formattedBalance}}"
}
```
1. In the `fr.json` file, add the translated content:
```json
{
"loyaltyPoints": {
"many": "Vous avez {{formattedPoints}} de points de fidélité",
"one": "Vouz avez {{formattedPoints}} point de fidélité",
"other": "Vous avez {{formattedPoints}} points de fidélité"
},
"balanceRemaining": "Votre solde est de {{formattedBalance}}"
}
```
## Step 3: Localize the currency
Now that you've defined translations, you'll learn how to localize currency.
You'll add the `formatCurrency` function provided by `i18n`. The function wraps the standard [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) object.
```jsx?title: 'src/Checkout.jsx'
...
function App() {
// Use i18n provided by the extension API
const { i18n } = useApi();
// Use the translate function
const translate = useTranslate();
const balance = 9.99;
// Localize the currency based on the current locale
const formattedBalance = i18n.formatCurrency(balance);
...
};
```
```js?title: 'src/Checkout.js'
...
export default extension("purchase.checkout.block.render", (root, { i18n }) => {
// Use i18n provided by the extension API
const balance = 9.99;
// Localize the currency based on the current locale
const formattedBalance = i18n.formatCurrency(balance);
...
});
```
Depending on the current locale, `9.99` will now resolve to the following localized currency formats:
- **`en`**: `$9.99`
- **`fr`**: `9,99`
## Step 4: Localize numbers
In this step, you'll learn how to resolve localized numbers.
You'll localize number formatting using the `formatNumber` function provided by `i18n`. The function wraps the standard [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) object.
```jsx?title: 'src/Checkout.jsx'
...
function App() {
...
const points = 10000;
// Localize the number based on the current locale
const formattedPoints = i18n.formatNumber(points);
...
};
```
```js?title: 'src/Checkout.js'
...
export default extension("purchase.checkout.block.render", (root, { i18n }) => {
...
const points = 10000;
// Localize the number based on the current locale
const formattedPoints = i18n.formatNumber(points);
...
});
```
Depending on the current locale, `10000` will resolve to one of the following localized number formats:
- **`en`**: `10,000`
- **`fr`**: `10 000`
## Step 5: Translate the balance remaining message
In this step, you'll learn how to translate the balance remaining message using a placeholder.
You'll use a placeholder for `formattedBalance`. You'll also call the `translate` function, which sends the `formattedBalance` variable so that it can be used in the translation string.
> Note:
> When using React, and [UI components as placeholders](/docs/api/checkout-ui-extensions/latest/apis), you should use the provided [`useTranslate`](/docs/api/checkout-ui-extensions/latest/react-hooks/localization/usetranslate) hook. This will make sure the [required unique key](https://reactjs.org/docs/lists-and-keys.html#keys) is added to each UI component in the returned array.
```jsx?title: 'src/Checkout.jsx'
...
function App() {
...
// Use the translate function
const translate = useTranslate();
const balance = 9.99;
// Localize the currency based on the current locale
const formattedBalance = i18n.formatCurrency(balance);
// Translate the balance remaining message
const balanceRemainingMsg = translate("balanceRemaining", {
// Provide a property/value that can be used in the translated message
formattedBalance,
});
...
};
```
```js?title: 'src/Checkout.js'
...
export default extension("purchase.checkout.block.render", (root, { i18n }) => {
const balance = 9.99;
// Localize the currency based on the current locale
const formattedBalance = i18n.formatCurrency(balance);
// Translate the balance remaining message
const balanceRemainingMsg = i18n.translate("balanceRemaining", {
// Provide a property/value that can be used in the translated message
formattedBalance,
});
...
};
```
## Step 6: Translate the loyalty points message with plural values
In this step, you'll learn how to translate the `loyaltyPoints` message, which supports pluralization.
You'll use the `translate` function to pass in the `count` of how many points are available. You'll use `formattedPoints` to render the points in the current locale.
> Note:
> When working with translation keys with pluralization, you must provide the `count` property. This allows the `translate` function to determine which pluralization to use, according to [Intl Pluralization Rules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/select).
```jsx?title: 'src/Checkout.jsx'
...
function App() {
...
// Use the translate function
const translate = useTranslate();
...
const points = 10000;
// Localize the number based on the current locale
const formattedPoints = i18n.formatNumber(points);
// Translate the loyalty points message, using pluralization to differentiate messages
const loyaltyPointsMsg = translate("loyaltyPoints", {
// Specify a count property so that the correct pluralization key (in this example, "many", "one" or "other") will be resolved
count: points,
// Provide a property/value that can be used in the translated message
formattedPoints,
});
...
};
```
```js?title: 'src/Checkout.js'
...
export default extension("purchase.checkout.block.render", (root, { i18n }) => {
...
const points = 10000;
// Localize the number based on the current locale
const formattedPoints = i18n.formatNumber(points);
// Translate the loyalty points message, using pluralization to differentiate messages
const loyaltyPointsMsg = i18n.translate("loyaltyPoints", {
// Specify a count property so that the correct pluralization key (in this example, "many", "one" or "other") will be resolved
count: points,
// Provide a property/value that can be used in the translated message
formattedPoints,
});
...
};
```
The checkout UI extension should now render localized checkout content for `en` and `fr`:
To test the extension, [preview the language](https://help.shopify.com/en/manual/markets/languages/manage-languages) from your development store admin.
## Step 7: Deploy the UI extension
When you're ready to release your changes to users, you can create and release an [app version](/docs/apps/launch/deployment/app-versions). An app version is a snapshot of your app configuration and all extensions.
You can have up to 50 checkout UI extensions in an app version.
1. Navigate to your app directory.
2. Run the following command.
Optionally, you can provide a name or message for the version using the `--version` and `--message` flags.
Releasing an app version replaces the current active version that's served to stores that have your app installed. It might take several minutes for app users to be upgraded to the new version.
> Tip:
> If you want to create a version, but avoid releasing it to users, then run the `deploy` command with a `--no-release` flag.
>
> You can release the unreleased app version using Shopify CLI's [`release`](/docs/api/shopify-cli/app/app-release) command, or through the Partner Dashboard.
## Troubleshooting
This section describes how to solve some potential errors when you run `dev` for an app that contains a checkout UI extension.
### Property token error
If you receive the error `ShopifyCLI:AdminAPI requires the property token to be set`, then you'll need to use the [`--checkout-cart-url` flag](/docs/api/shopify-cli/app/app-dev#flags) to direct Shopify CLI to open a checkout session for you.
### Missing checkout link
If you don't receive the test checkout URL when you run `dev`, then verify the following:
- You have a development store populated with products.
- You're logged in to the correct Partners organization and development store. To verify, check your app info using the following command:
Otherwise, you can manually create a checkout with the following steps:
1. From your development store's storefront, add some products to your cart.
1. From the cart, click **Checkout**.
1. From directory of the app that contains your extension, run `dev` to preview your app:
1. On the checkout page for your store, change the URL by appending the `?dev=https://{tunnel_url}/extensions` query string and reload the page. The `tunnel_url` parameter allows your app to be accessed using a unique HTTPS URL.
You should now see a rendered order note that corresponds to the code in your project template.
## Next steps
- Use [JavaScript APIs](/docs/api/checkout-ui-extensions/latest/apis) to access translations for localizing checkout UI extensions.