> ## 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.
# Bad practices
Common localization mistakes to avoid and how to fix them.
Bad localization practices can result in a confusing mix of languages, inconsistent user experiences and reduced user engagement with your app. This page outlines common mistakes (bad practices) when localizing your app, and how to avoid them to make sure a consistent, high-quality experience for all users.
## Bad practices when localizing app UI strings
When App UI strings aren't properly localized, it often results in unexpected untranslated content like shown in the image below. This section outlines the most common bad practices.
### Not setting up i18n linting
**Don't** skip setting up the recommended i18n linting configuration or ignore linting errors.**Do** set up the recommended i18n linting and fix all linting errors. See [Step 2: Configure ESLint](https://www.canva.dev/docs/apps/localization/migrate-an-existing-app/#step-2-configure-eslint).
### Unformatted strings
**Don't** render strings without localizing them first.
```jsx
Welcome to My App
```
**Do** use `intl.formatMessage` or `` for any user-facing text.
```jsx
```
If you need to make an exception (e.g. brand names), see [Excluding text](https://www.canva.dev/docs/apps/localization/#excluding-text).
### Use dynamic `id` or `defaultMessage` values
**Don't** use dynamic id values in your messages. `@formatjs/cli` won't extract these messages for translation.
```jsx
// OR
intl.formatMessage({ id: menuItemKey })
intl.formatMessage({ id: `menu.${menuItemKey}` })
```
**Don't** use dynamic defaultMessage values in your messages. `@formatjs/cli` won't extract these messages for translation.
```jsx
// OR
intl.formatMessage({ defaultMessage: menuItemLabel })
intl.formatMessage({ defaultMessage: `Menu: ${menuItemLabel}` })
```
**Do** use predefined messages and select the message based on a dynamic value. `@formatjs/cli` will correctly extract these messages for translation.
```jsx
const menuMessages = defineMessages({
edit: {
defaultMessage: "Edit"
},
view: {
defaultMessage: "View"
},
// Add more menu items ...
});
function getMenuMessage(menuItemKey) {
switch (menuItemKey) {
case "edit":
return menuMessages.edit;
case "view":
return menuMessages.view;
// Add more menu items ...
default:
throw new Error(`Unknown menu item: ${menuItemKey}`);
}
}
```
### Render strings returned from the backend
**Avoid** rendering untranslated strings returned directly from your backend. Instead, map backend responses to predefined messages on your frontend, so you can use translations provided by Canva.
```jsx
{response.errorMessage}
```
**Do** map backend responses to predefined messages on the frontend.
```jsx
const messages = defineMessages({
inappropriateContent: {
defaultMessage: "Inappropriate content detected."
},
// Add more messages ...
unknownError: {
defaultMessage: "An unknown error occurred. Modify your request and try again."
}
});
function getErrorMessage(errorCode) {
switch (errorCode) {
case "INAPPROPRIATE_CONTENT":
return messages.inappropriateContent;
// Add more messages ...
default:
return messages.unknownError;
}
}
```
See [Preferred: Frontend localization](https://www.canva.dev/docs/apps/localization/backend-responses/#preferred-frontend-localization) for a more complete example.
### Define placeholder-only messages
**Avoid** defining messages that only contain variable placeholders. This is problematic, because once this message is extracted into your `messages_en.json` file, it will only contain the variable placeholder without any other text, and as such nothing will be translated.
```jsx
```
**Do** define a static message for each possible value, so that each value is translated.
```jsx
const optionMessages = defineMessages({
apple: {
defaultMessage: "Apple"
},
banana: {
defaultMessage: "Banana"
}
// ...
});
function getOptionMessage(optionKey) {
switch (optionKey) {
case "apple":
return optionMessages.apple;
case "banana":
return optionMessages.banana;
// ...
default:
throw new Error(`Unknown option: ${optionKey}`);
}
}
```
For guidance on when and how to use interpolation, follow [this guide](https://www.canva.dev/docs/apps/localization/icu-syntax/#interpolation).