--- title: Localize a checkout UI extension description: Learn how to localize your checkout UI extension for common use cases, including pluralization and number and currency formatting. source_url: html: https://shopify.dev/docs/apps/build/checkout/localized-checkout-ui-extensions/localize?extension=polaris md: https://shopify.dev/docs/apps/build/checkout/localized-checkout-ui-extensions/localize.md?extension=polaris --- # Localize a checkout UI extension Plus [Checkout UI extensions](https://shopify.dev/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 a Preact-based checkout UI 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. ![The checkout UI displaying a summary of loyalty points and currency balance, in English and French](https://cdn.shopify.com/shopifycloud/shopify-dev/production/assets/assets/images/apps/checkout/checkout-localization@2x-DfOorBeW.png) ## 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're a [user with app development permissions](https://shopify.dev/docs/apps/build/dev-dashboard/user-permissions). * You've created a new [development store](https://shopify.dev/docs/api/development-stores) with the following: * [Generated test data](https://shopify.dev/docs/api/development-stores/generated-test-data) * [Checkout and Customer Accounts Extensibility](https://shopify.dev/docs/api/developer-previews#checkout-and-customer-accounts-extensibility-developer-preview) feature preview enabled * You've [created an app that uses Shopify CLI 3.85.1 or higher](https://shopify.dev/docs/apps/build/scaffold-app). - You're familiar with [how localization works for checkout UI extensions](https://shopify.dev/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. ## Project Polaris [View on GitHub](https://github.com/Shopify/example-checkout--localize--preact) ## 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: ## Terminal ```terminal cd ``` 2. Run the following command to create a new checkout UI extension: ## Terminal ```terminal shopify app generate extension --template checkout_ui --name my-checkout-ui-extension ``` 1) Select `Checkout UI`. You should now have a new extension directory in your app's directory. The extension directory includes the extension script at `src/Checkout.jsx`. The following is an example directory structure: ## Checkout UI extension file structure ```text └── my-app └── extensions └── my-checkout-ui-extension ├── src │ └── Checkout.jsx OR Checkout.js // The index page of the checkout UI extension ├── locales │ ├── en.default.json // The default locale for the checkout UI extension │ └── fr.json // The locale file for non-regional French translations ├── shopify.extension.toml // The config file for the checkout UI extension └── package.json ``` 1. Start your development server to build and preview your app: ## Terminal ```terminal shopify app dev ``` To learn about the processes that are executed when you run `dev`, refer to the [Shopify CLI command reference](https://shopify.dev/docs/api/shopify-cli/app/app-dev). 2. Press `p` to open the developer console. In the developer console page, click on the preview link for your extension. ## 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 for `en.default.json` Add the translation strings we need to support English translations, including the `zero`, `one`, and `other` plural rules. 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 use these keys to translate [`balance`](#step-5-translate-the-balance-remaining-message) and [`points`](#step-6-translate-the-loyalty-points-message-with-plural-values). ### Add translation strings for `fr.json` Now add the translation strings we need to supoort French translations, `many`, `one` and `other` plural rules. Similar to English, 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. ## Localize ### 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. Depending on the current locale, `9.99` will now resolve to the following localized currency formats: * **`en`**: `$9.99` * **`fr`**: `9,99` ## /extensions/localize-example/src/Checkout.jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from "preact"; // 1. Export the extension export default function() { render(, document.body) } function Extension() { // Access Shopify's global i18n object const i18n = shopify.i18n; const translate = (key, replacements) => i18n.translate(key, replacements); // Example balance for demonstration const balance = 49.99; // Use i18n.formatCurrency() for proper currency formatting const formattedBalance = i18n.formatCurrency(balance); // Example points for demonstration const points = 1250; // Use i18n.formatNumber() for proper number formatting const formattedPoints = i18n.formatNumber(points); // 2. Check instructions for feature availability if (!shopify.instructions.value.attributes.canUpdateAttributes) { return ( {translate("attributeChangesAreNotSupported")} ); } // 3. Render a UI with locale-aware content return ( {/* Show formatted loyalty points */} {translate("loyaltyPoints", { count: points, formattedPoints })} {/* Show formatted currency */} {translate("balanceRemaining", { formattedBalance })} ); } ``` ### Localize 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. Depending on the current locale, `10000` will resolve to one of the following localized number formats: * **`en`**: `10,000` * **`fr`**: `10 000` ## /extensions/localize-example/src/Checkout.jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from "preact"; // 1. Export the extension export default function() { render(, document.body) } function Extension() { // Access Shopify's global i18n object const i18n = shopify.i18n; const translate = (key, replacements) => i18n.translate(key, replacements); // Example balance for demonstration const balance = 49.99; // Use i18n.formatCurrency() for proper currency formatting const formattedBalance = i18n.formatCurrency(balance); // Example points for demonstration const points = 1250; // Use i18n.formatNumber() for proper number formatting const formattedPoints = i18n.formatNumber(points); // 2. Check instructions for feature availability if (!shopify.instructions.value.attributes.canUpdateAttributes) { return ( {translate("attributeChangesAreNotSupported")} ); } // 3. Render a UI with locale-aware content return ( {/* Show formatted loyalty points */} {translate("loyaltyPoints", { count: points, formattedPoints })} {/* Show formatted currency */} {translate("balanceRemaining", { formattedBalance })} ); } ``` ### Translate the balance remaining message You'll also call the `translate` function, which sends the `formattedBalance` variable so that it can be used in the translation string. ## /extensions/localize-example/src/Checkout.jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from "preact"; // 1. Export the extension export default function() { render(, document.body) } function Extension() { // Access Shopify's global i18n object const i18n = shopify.i18n; const translate = (key, replacements) => i18n.translate(key, replacements); // Example balance for demonstration const balance = 49.99; // Use i18n.formatCurrency() for proper currency formatting const formattedBalance = i18n.formatCurrency(balance); // Example points for demonstration const points = 1250; // Use i18n.formatNumber() for proper number formatting const formattedPoints = i18n.formatNumber(points); // 2. Check instructions for feature availability if (!shopify.instructions.value.attributes.canUpdateAttributes) { return ( {translate("attributeChangesAreNotSupported")} ); } // 3. Render a UI with locale-aware content return ( {/* Show formatted loyalty points */} {translate("loyaltyPoints", { count: points, formattedPoints })} {/* Show formatted currency */} {translate("balanceRemaining", { formattedBalance })} ); } ``` ### Translate the loyalty points message with plural values 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). ## /extensions/localize-example/src/Checkout.jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from "preact"; // 1. Export the extension export default function() { render(, document.body) } function Extension() { // Access Shopify's global i18n object const i18n = shopify.i18n; const translate = (key, replacements) => i18n.translate(key, replacements); // Example balance for demonstration const balance = 49.99; // Use i18n.formatCurrency() for proper currency formatting const formattedBalance = i18n.formatCurrency(balance); // Example points for demonstration const points = 1250; // Use i18n.formatNumber() for proper number formatting const formattedPoints = i18n.formatNumber(points); // 2. Check instructions for feature availability if (!shopify.instructions.value.attributes.canUpdateAttributes) { return ( {translate("attributeChangesAreNotSupported")} ); } // 3. Render a UI with locale-aware content return ( {/* Show formatted loyalty points */} {translate("loyaltyPoints", { count: points, formattedPoints })} {/* Show formatted currency */} {translate("balanceRemaining", { formattedBalance })} ); } ``` ### Preview the extension Preview your extension to make sure that it works as expected. #### Start your server Run the Shopify CLI `dev` command to build your app and preview it on your development store. Make sure that you select a development store that has enabled the feature preview for [Checkout and Customer Accounts Extensibility](https://shopify.dev/docs/api/developer-previews#checkout-and-customer-accounts-extensibility-developer-preview). 1. In a terminal, navigate to your app directory. 2. Either start or restart your server to build and preview your app: ## Terminal ```bash shopify app dev ``` 3. Press `p` to open the developer console. 4. In the developer console page, click on the preview link for the custom header extension. The checkout opens. *** 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](https://shopify.dev/docs/api/shopify-cli/app/app-dev#flags) to direct Shopify CLI to open a checkout session for you. ## Terminal ```terminal shopify app dev --checkout-cart-url cart/{product_variant_id}:{quantity} ``` ### 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: ## Terminal ```terminal shopify app info ``` Otherwise, you can manually create a checkout with the following steps: 1. From your development store's storefront, add some products to your cart. 2. From the cart, click **Checkout**. 3. From directory of the app that contains your extension, run `dev` to preview your app: ## Terminal ```terminal shopify app dev ``` 4. 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. ### Test the extension functionality The checkout UI extension should now render localized checkout content for `en` and `fr`: ![The checkout UI extension displaying a summary of loyalty points and currency balance, in English and French](https://cdn.shopify.com/shopifycloud/shopify-dev/production/assets/assets/images/apps/checkout/checkout-localization@2x-DfOorBeW.png) To test the extension, [preview the language](https://help.shopify.com/en/manual/markets/languages/manage-languages) from your development store admin. ## Deploy the UI extension When you're ready to release your changes to users, you can create and release an [app version](https://shopify.dev/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. ## Terminal ```terminal shopify app deploy ``` 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`](https://shopify.dev/docs/api/shopify-cli/app/app-release) command, or through the Dev Dashboard. ## /extensions/localize-example/src/Checkout.jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from "preact"; // 1. Export the extension export default function() { render(, document.body) } function Extension() { // Access Shopify's global i18n object const i18n = shopify.i18n; const translate = (key, replacements) => i18n.translate(key, replacements); // Example balance for demonstration const balance = 49.99; // Use i18n.formatCurrency() for proper currency formatting const formattedBalance = i18n.formatCurrency(balance); // Example points for demonstration const points = 1250; // Use i18n.formatNumber() for proper number formatting const formattedPoints = i18n.formatNumber(points); // 2. Check instructions for feature availability if (!shopify.instructions.value.attributes.canUpdateAttributes) { return ( {translate("attributeChangesAreNotSupported")} ); } // 3. Render a UI with locale-aware content return ( {/* Show formatted loyalty points */} {translate("loyaltyPoints", { count: points, formattedPoints })} {/* Show formatted currency */} {translate("balanceRemaining", { formattedBalance })} ); } ``` ## /extensions/localize-example/locales/en.default.json ```json { "attributeChangesAreNotSupported": "Attribute changes are not supported in this checkout", "bannerHeading": "Your Rewards & Balance", "balanceRemaining": "Your balance is {{formattedBalance}}", "loyaltyPoints": { "zero": "You have no loyalty points", "one": "You have {{formattedPoints}} loyalty point", "other": "You have {{formattedPoints}} loyalty points", "many": "You have {{formattedPoints}} loyalty points" } } ``` ## /extensions/localize-example/locales/fr.json ```json { "attributeChangesAreNotSupported": "Les modifications d'attribut ne sont pas prises en charge dans cette commande", "bannerHeading": "Vos récompenses et solde", "balanceRemaining": "Votre solde est de {{formattedBalance}}", "loyaltyPoints": { "zero": "Vous n'avez aucun point de fidélité", "many": "Vous avez {{formattedPoints}} de points de fidélité", "one": "Vous avez {{formattedPoints}} point de fidélité", "other": "Vous avez {{formattedPoints}} points de fidélité" } } ``` ## Next steps [![](https://shopify.dev/images/icons/32/graphql.png)![](https://shopify.dev/images/icons/32/graphql-dark.png)](https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/localization) [Consult the Localization API](https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/localization) [Learn about the APIs used to access translations for localizing checkout UI extensions.](https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/localization)