> ## Documentation Index > Fetch the complete documentation index at: https://www.canva.dev/docs/apps/llms.txt > Use this file to discover all available pages before exploring further. # ICU syntax Canva supports a subset of International Components for Unicode (ICU). The Canva translation process supports a subset of ICU `MessageFormat` syntax. For more information about ICU, see the official [FormatJS documentation](https://formatjs.github.io/docs/core-concepts/icu-syntax). NOTE: Canva's translation process doesn't support the entire ICU syntax, and uploaded files will be rejected if they contain any unsupported syntax. For more information, see [Unsupported syntax](https://www.canva.dev/#unsupported-syntax). ## ICU basics This section describes the supported ICU syntax, with recommendations and examples. Follow these examples and guidelines so that your app can be translated more accurately and efficiently. ### Basic messages How to do basic message localization with React, using the `FormattedMessage` component from `react-intl`. * The `defaultMessage` string is the source for translation text. It's displayed to users that either have an English locale, or a locale with no translations available. * Use the `description` string to give as much context as possible to a human translator. For guidance on writing translator notes, see [Add notes for translators](https://www.canva.dev/docs/apps/localization/#add-notes-for-translators). For example: ```tsx ```
Do
Ensure that `defaultMessage` is clear and written in US English.
Include a detailed `description` that gives context to translators.
### Interpolation You can use dynamic values (like `firstName`) in the `defaultMessage` prop of `FormattedMessage`. This process is known as *interpolation*. This section explains how to use interpolation for localized messages in React, using the `FormattedMessage` component from `react-intl`. For example: ```tsx const name = "John" // ... ``` This renders as: "Welcome to the world of AI creativity, John!"
Do
When inserting dynamic content (such as user-specific data), use placeholders like `{firstName}`.
`"Hello {name}"`
Don't
Don't manually combine strings and variables, using techniques like the `+` operator. This can lead to incorrect and confusing sentences when translations are combined. This is because languages will reorder words in various ways. For more information, see this [blog post](https://www.linkedin.com/pulse/internationalization-localization-tips-concatenation-daniel-neumann).
`"Hello " + name`
**Tip:** There are some cases where [select](https://www.canva.dev/docs/apps/localization/icu-syntax/#select) is preferable to interpolation. For more information, see [Using select or interpolation](https://www.canva.dev/docs/apps/localization/icu-syntax/#using-select-or-interpolation). ### Plurals How to handle plurals in localized messages in React. For example: ```tsx export const CreditUsage = ({ creditsCost, remainingCredits, }: { creditsCost: number; remainingCredits: number; }) => ( ); ``` ```tsx ``` This would render as: * "Use 5 of 50 credits" * "Use 1 of 1 credit"
Do
**Consider edge cases:** Ensure that pluralization handles various scenarios, such as zero, one, and other amounts.
### Numbers To localize numbers for the user's locale, ICU needs them to be formatted with a specific argument. NOTE: If you are only presenting a number by itself, you can use [`FormattedNumber`](https://formatjs.github.io/docs/react-intl/components/#formattednumber) from the `react-intl` library to localize the number. If you are presenting a number in a sentence with other words, you should use the ICU syntax, as shown below. This example shows how to format numbers in localized messages in React, using the `FormattedMessage` component from `react-intl`. ```tsx ``` Assuming the locale uses this percentage format, this can render as: "Image generation is 75% complete."
Do
Use ICU number formatting for numbers, such as percentages.
`Members ({(members, number, integer)})` - `123456 members` displays as `Members (123,456)` in English, `Membres (123 456)` in French, and `Thành viên (123.456)` in Vietnamese.
Don't
Avoid formatting numbers manually. Instead, you can use an ICU formatting library like `react-intl`.
`Members ({members})` - The number doesn't respect the user's locale and always displays 123456 as the number, without separators.
#### Currency Automatically adjusts currency symbols and formats based on the user's locale. This function does not convert currency into other denominations, and does not consider exchange rates at any point. * `::currency/CODE`: Specifies the currency code for number formatting (such as `USD`, `EUR`). In this example, the number `1234.56` is formatted as currency in USD, displaying "$1,234.56" for locales using the dollar sign format. The `::currency/USD` syntax configures the number formatting for the specified currency. ```tsx ``` Rendered result: `The current temperature is 23.5 degrees.` #### Percentage Automatically adjusts percentage formats based on the user's locale. * `{progress, number, ::percent}`: Formats a number as a percentage. This example shows how to display task completion progress. The `{progress, number, ::percent}` syntax formats the number `0.71` as "71%", representing the progress in whole percentage terms. ```tsx ``` Rendered result: `Task completion is at 71%.` ### Dates To localize date values for the user's locale, ICU needs them to be formatted with a specific argument. NOTE: If you are only presenting a date by itself, you can use [`FormattedDate`](https://formatjs.github.io/docs/react-intl/components/#formatteddate) from the `react-intl` library to localize the date. If you are presenting a date in a sentence with other words, you should use the ICU syntax, as shown below. This example demonstrates how to format dates and times in localized messages in React, using the `FormattedMessage` component from `react-intl`. ```tsx ``` Depending on the user's locale, this can render as: `Credits refresh on: 11/24/23 at 3:00 PM"`.
Do
`Expires {(expiryDate, date, short)}` - When formatted with a date value (such as `new Date('2024-09-25')` in JavaScript), this will correctly display as: * `Expires 9/25/2024` for US English (en-US). * `Verfällt am 25.09.2024` for German (de-DE).
Don't
`Expires {expiryDate}` - The number doesn't respect the user’s locale and always displays the date as 2024-09-25. For example: (`Verfällt am 2024-09-25`)
### Time To localize time values for the user's locale, ICU needs them to be formatted with a specific argument. NOTE: If you are only presenting a time by itself, you can use [`FormattedTime`](https://formatjs.github.io/docs/react-intl/components/#formattedtime) from the `react-intl` library to localize the time. If you are presenting a time in a sentence with other words, you should use the ICU syntax, as shown below.
Do
`Current time: {(currentTime, time, short)}` - This displays as `Current time: 3:45 PM` in English.
#### Relative time This section provides guidelines for displaying relative time. ```tsx const LastGeneratedMessage = ({ lastGeneratedTime, }: { lastGeneratedTime: Date; }) => { const intl = useIntl(); const [generatedTimeAgoInSeconds, setGeneratedTimeAgoInSeconds] = React.useState(0); // ... return ( ); }; ```
Do
Use functions or libraries that handle relative time calculations and formatting, such as `intl.formatRelativeTime` of `react-intl`, or [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat)
Don't
Avoid hardcoding custom logic for relative time calculations, as this might not adapt well across different locales.
### Select
Do
`Hello, {role, select, admin {Administrator} user {User} guest {Guest} other {Visitor}}!` * If `role` is `'admin'`, it renders: `Hello, Administrator!` * If `role` is `'user'`, it renders: `Hello, User!` * If `role` is `'guest'`, it renders: `Hello, Guest!` * If `role` is anything else or missing, it renders: `Hello, Visitor!`
#### Using select or interpolation If your message needs to adapt to a specific, categorical value (such as *status* or *day*) and an obvious fallback value, then [select](https://www.canva.dev/docs/apps/localization/icu-syntax/#select) might be easier to implement than [interpolation](https://www.canva.dev/docs/apps/localization/icu-syntax/#interpolation). Select lets you define the various message strings directly within the message format, making your code more concise and easier to maintain. While select might appear simpler and preferable to implement at first, it can become more complex once you start nesting ICU elements. Interpolation is more familiar to JavaScript and TypeScript developers and is a flexible and powerful option, especially when you need to handle multiple dynamic values. The following table summarizes the use cases for select and interpolation. | Feature | Select | Interpolation | | ------------------ | ----------------------------------------------------- | ------------------------------------------- | | Purpose | Conditional selection based on predefined categories. | Dynamic value insertion. | | Example Input | "male", "female", "other". | "Jane", "123", "New York". | | Use case | Gender, user roles, statuses. | Names, numbers, locations, dynamic content. | | Fallback required? | Yes (other case). | No, just inserts the value. | These examples compare how select and interpolation would handle a dynamic greeting: **Select:** When using select, you can create a message that changes based the document type: ```json Here are your stats for the year. {doctype, select, presentation{Your slides are the talk of the town!} social {Your followers love your work!} other {Way to go!}} ``` **Interpolation:** When using string interpolation instead of select, you need to define logic outside the message to choose the appropriate string for the doctype. Here's and example of how the interpolation code might look: ```javascript import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ presentation: { defaultMessage: "Your slides are the talk of the town!", description: "Message for presentation documents" }, social: { defaultMessage: "Your followers love your work!", description: "Message for social media posts" }, other: { defaultMessage: "Way to go!", description: "Fallback message" }, }); const interpolateSelect = (key, intl) => { return intl.formatMessage(messages[key] || messages.other); }; const DocumentMessage = ({ doctype }) => { const intl = useIntl(); const message = interpolateSelect(doctype, intl); return

{message}

; }; // Usage example ``` ### Rich text How to format rich text in localized messages in React, using the `FormattedMessage` component from `react-intl`. For example: ```tsx ( openExternalUrl(DOCS_URL)} > {chunks} ), callToAction: (chunks) => {chunks}, }} > {(chunks) => {chunks}} ``` This can render as: "Discover stunning AI-generated example images in our [gallery](https://www.canva.dev/#rich-text) and **start exploring now!**"
Do
**Use functions for rich text:** To ensure dynamic content is properly rendered, you can wrap parts of your message in React components by passing functions in the `values` prop.
Don't
**Hardcode HTML Elements:** Avoid embedding raw HTML within your `defaultMessage`.
### Message as string type How to use localized strings as button labels in React, using `formatMessage` from `react-intl`. For example: ```tsx ``` This renders a button with the label "Generate image", and the text is localized based on the user's locale.
Do
For consistency with the rest of the app's localization, use `formatMessage` to localize user-facing text that is required to be a string type (like button labels).
Don't
Avoid hardcoding button text, since it will be left untranslated.
### Non-visible text for accessibility How to use non-visible text for accessibility purposes (such as screen reader descriptions) in React, using `ariaLabel` and `formatMessage` from `react-intl`. For example: ```tsx