--- layout: default title: "Translation" --- # Translation The react-admin user interface uses English as the default language. But you can also display the UI and content in other languages, allow changing language at runtime, even lazy-loading optional languages to avoid increasing the bundle size with all translations. You will use translation features mostly via the `i18nProvider`, and a set of hooks (`useTranslate`, `useLocale`, `useSetLocale`). **Tip**: We'll use a bit of custom vocabulary in this chapter: - "i18n" is a shorter way to write "internationalization" (an i followed by 18 letters followed by n) - "locale" is a concept similar to languages, but it also includes the concept of country. For instance, there are several English locales (like `en_us` and `en_gb`) because US and UK citizens don't use exactly the same language. For react-admin, the "locale" is just a key for your i18nProvider, so it can have any value you want. ## Introducing the `i18nProvider` Just like for data fetching and authentication, react-admin relies on a simple object for translations. It's called the `i18nProvider`, and it manages translation and language change using two methods: ```js const i18nProvider = { translate: (key, options) => string, changeLocale: locale => Promise, getLocale: () => string, } ``` And just like for the `dataProvider` and the `authProvider`, you can *inject* the `i18nProvider` to your react-admin app using the `` component: ```jsx import i18nProvider from './i18n/i18nProvider'; const App = () => ( // ... ``` If you want to add or update translations, you'll have to provide your own `i18nProvider`. React-admin components use translation keys for their labels, and rely on the `i18nProvider` to translate them. For instance: ```jsx const SaveButton = ({ doSave }) => { const translate = useTranslate(); // returns the i18nProvider.translate() method return ( ); }; ``` ## Using Polyglot.js Here is the simplest possible implementation for an `i18nProvider` with English and French messages: ```js import lodashGet from 'lodash/get'; const englishMessages = { ra: { notification: { http_error: 'Network error. Please retry', }, action: { save: 'Save', delete: 'Delete', }, }, }; const frenchMessages = { ra: { notification: { http_error: 'Erreur réseau, veuillez réessayer', }, action: { save: 'Enregistrer', delete: 'Supprimer', }, }, }; let messages = englishMessages; let locale = 'en'; const i18nProvider = { translate: key => lodashGet(messages, key), changeLocale: newLocale => { messages = (newLocale === 'fr') ? frenchMessages : englishMessages; locale = newLocale; return Promise.resolve(); }, getLocale: () => locale }; ``` But this is too naive: react-admin expects that i18nProviders support string interpolation for translation, and asynchronous message loading for locale change. That's why react-admin bundles an `i18nProvider` *factory* called `polyglotI18nProvider`. This factory relies on [polyglot.js](http://airbnb.io/polyglot.js/), which uses JSON files for translations. It only expects one argument: a function returning a list of messages based on a locale passed as argument. So the previous provider can be written as: ```js import polyglotI18nProvider from 'ra-i18n-polyglot'; const englishMessages = { ra: { notification: { http_error: 'Network error. Please retry', }, action: { save: 'Save', delete: 'Delete', }, }, }; const frenchMessages = { ra: { notification: { http_error: 'Erreur réseau, veuillez réessayer', }, action: { save: 'Enregistrer', delete: 'Supprimer', }, }, }; const i18nProvider = polyglotI18nProvider(locale => locale === 'fr' ? frenchMessages : englishMessages, 'en' // Default locale ); ``` ## Changing The Default Locale The default react-admin locale is `en`, for English. If you want to display the interface in another language by default, you'll have to install a third-party package. For instance, to change the interface to French, you must install the `ra-language-french` npm package, then use it in a custom `i18nProvider`, as follows: ```jsx import React from 'react'; import { Admin, Resource } from 'react-admin'; import polyglotI18nProvider from 'ra-i18n-polyglot'; import frenchMessages from 'ra-language-french'; const i18nProvider = polyglotI18nProvider(() => frenchMessages, 'fr'); const App = () => ( ... ); export default App; ``` ## Available Locales You can find translation packages for the following languages: - Arabic (`ar`): [developerium/ra-language-arabic](https://github.com/developerium/ra-language-arabic) - Bulgarian (`bg`): [ptodorov0/ra-language-bulgarian](https://github.com/ptodorov0/ra-language-bulgarian) - Catalan (`ca`): [sergioedo/ra-language-catalan](https://github.com/sergioedo/ra-language-catalan) - Chinese (`zh-TW`): [areyliu6/ra-language-chinese-traditional](https://github.com/areyliu6/ra-language-chinese-traditional) - Chinese (`zh`): [chen4w/ra-language-chinese](https://github.com/chen4w/ra-language-chinese) - Czech (`cs`): [binao/ra-language-czech](https://github.com/binao/ra-language-czech) - Danish (`da`): [nikri/ra-language-danish](https://github.com/nikri/ra-language-danish) - Dutch (`nl`): [nickwaelkens/ra-language-dutch](https://github.com/nickwaelkens/ra-language-dutch) - English (`en`): [marmelab/ra-language-english](https://github.com/marmelab/react-admin/tree/master/packages/ra-language-english) - Farsi (`fa`): [hamidfzm/ra-language-farsi](https://github.com/hamidfzm/ra-language-farsi) - Finnish (`fi`): [aikain/ra-language-finnish](https://github.com/aikain/ra-language-finnish) - French (`fr`): [marmelab/ra-language-french](https://github.com/marmelab/react-admin/tree/master/packages/ra-language-french) - German (`de`): [greenbananaCH/ra-language-german](https://github.com/greenbananaCH/ra-language-german) (tree translation: [straurob/ra-tree-language-german](https://github.com/straurob/ra-tree-language-german)) - Hebrew (`he`): [ak-il/ra-language-hebrew](https://github.com/ak-il/ra-language-hebrew) - Hungarian (`hu`): [phelion/ra-language-hungarian](https://github.com/phelion/ra-language-hungarian) - Indonesian (`id`): [ronadi/ra-language-indonesian](https://github.com/ronadi/ra-language-indonesian) - Italian (`it`): [stefsava/ra-italian](https://github.com/stefsava/ra-italian) - Japanese (`ja`): [bicstone/ra-language-japanese](https://github.com/bicstone/ra-language-japanese) - Norwegian (`no`): [jon-harald/ra-language-norwegian](https://github.com/jon-harald/ra-language-norwegian) - Polish (`pl`): [tskorupka/ra-language-polish](https://github.com/tskorupka/ra-language-polish) - Portuguese (`pt`): [marquesgabriel/ra-language-portuguese](https://github.com/marquesgabriel/ra-language-portuguese) - Russian (`ru`): [klucherev/ra-language-russian](https://github.com/klucherev/ra-language-russian) - Slovak (`sk`): [zavadpe/ra-language-slovak](https://github.com/zavadpe/ra-language-slovak) - Spanish (`es`): [blackboxvision/ra-language-spanish](https://github.com/BlackBoxVision/ra-language-spanish) - Swedish (`sv`): [jolixab/ra-language-swedish](https://github.com/jolixab/ra-language-swedish) - Turkish (`tr`): [KamilGunduz/ra-language-turkish](https://github.com/KamilGunduz/ra-language-turkish) - Ukrainian (`ua`): [koresar/ra-language-ukrainian](https://github.com/koresar/ra-language-ukrainian) - Vietnamese (`vi`): [hieunguyendut/ra-language-vietnamese](https://github.com/hieunguyendut/ra-language-vietnamese) In addition, the previous version of react-admin, called admin-on-rest, was translated in the following languages: - Chinese (Traditional) (`cht`): [leesei/aor-language-chinese-traditional](https://github.com/leesei/aor-language-chinese-traditional) - Croatian (`hr`): [ariskemper/aor-language-croatian](https://github.com/ariskemper/aor-language-croatian) - Greek (`el`): [zifnab87/aor-language-greek](https://github.com/zifnab87/aor-language-greek) - Slovenian (`sl`): [ariskemper/aor-language-slovenian](https://github.com/ariskemper/aor-language-slovenian) - Thai (`th`): [liverbool/aor-language-thai](https://github.com/liverbool/aor-language-thai) These packages are not directly interoperable with react-admin, but the upgrade is straightforward; rename the root key from "aor" to "ra". We invite the authors of the packages listed above to republish their translations for react-admin, using a different package name. If you want to contribute a new translation, feel free to submit a pull request to update [this page](https://github.com/marmelab/react-admin/blob/master/docs/Translation.md) with a link to your package. ## `useSetLocale`: Changing Locale At Runtime If you want to offer the ability to change locale at runtime, you must provide an `i18nProvider` that contains the messages for all possible locales: ```jsx import React from 'react'; import { Admin, Resource } from 'react-admin'; import polyglotI18nProvider from 'ra-i18n-polyglot'; import englishMessages from 'ra-language-english'; import frenchMessages from 'ra-language-french'; const messages = { fr: frenchMessages, en: englishMessages, }; const i18nProvider = polyglotI18nProvider(locale => messages[locale]); const App = () => ( ... ); export default App; ``` Then, use the `useSetLocale` hook to change locale. For instance, the following component allows the user to switch the interface language between English and French: ```jsx import React from 'react'; import Button from '@material-ui/core/Button'; import { useSetLocale } from 'react-admin'; const LocaleSwitcher = () => { const setLocale = useSetLocale(); return (
Language
); }; export default LocaleSwitcher; ``` ## `useLocale`: Getting The Current Locale Your language switcher component probably needs to know the current locale, in order to disable/transform the button for the current language. The `useLocale` hook returns the current locale: ```jsx import React, { Component } from 'react'; import Button from '@material-ui/core/Button'; import { useLocale, useSetLocale } from 'react-admin'; const LocaleSwitcher = () => { const locale = useLocale(); const setLocale = useSetLocale(); return (
Language
); }; export default LocaleSwitcher; ``` ## Lazy-Loading Locales Bundling all the possible locales in the `i18nProvider` is a great recipe to increase your bundle size, and slow down the initial application load. Fortunately, the `i18nProvider` returns a *promise* for locale change calls to load secondary locales on demand. And the `polyglotI18nProvider` accepts when its argument function returns a Promise, too. For example: ```js import polyglotI18nProvider from 'ra-i18n-polyglot'; import englishMessages from '../en.js'; const i18nProvider = polyglotI18nProvider(locale => { if (locale === 'en') { // initial call, must return synchronously return englishMessages; } if (locale === 'fr') { return import('../i18n/fr.js').then(messages => messages.default); } }, 'en'); const App = () => ( ... ); ``` ## Using The Browser Locale React-admin provides a helper function named `resolveBrowserLocale()`, which detects the user's browser locale. To use it, simply pass the function as the `initialLocale` argument of `polyglotI18nProvider`. ```jsx import React from 'react'; import { Admin, Resource, resolveBrowserLocale, } from 'react-admin'; import polyglotI18nProvider from 'ra-i18n-polyglot'; import englishMessages from 'ra-language-english'; import frenchMessages from 'ra-language-french'; const messages = { fr: frenchMessages, en: englishMessages, }; const i18nProvider = polyglotI18nProvider( locale => messages[locale] ? messages[locale] : messages.en, resolveBrowserLocale() ); const App = () => ( ... ); export default App; ``` Beware that users from all around the world may use your application, so make sure the `i18nProvider` returns default messages even for unknown locales? ## Translation Messages The `message` returned by the `polyglotI18nProvider` function argument should be a dictionary where the keys identify interface components, and values are the translated string. This dictionary is a simple JavaScript object looking like the following: ```js { ra: { action: { delete: 'Delete', show: 'Show', list: 'List', save: 'Save', create: 'Create', edit: 'Edit', cancel: 'Cancel', }, ... }, } ``` All core translations are in the `ra` namespace, in order to prevent collisions with your own custom translations. The root key used at runtime is determined by the value of the `locale` prop. The default messages are available [here](https://github.com/marmelab/react-admin/blob/master/packages/ra-language-english/index.js). ## Translating Resource and Field Names By default, React-admin uses resource names ("post", "comment", etc) and field names ("title", "first_name", etc) everywhere in the interface. It simply "humanizes" the technical identifiers to make them look better (e.g. "first_name" becomes "First name"). However, before humanizing names, react-admin checks the `messages` dictionary for a possible translation, with the following keys: - `resources.${resourceName}.name` for resource names (used for the menu and page titles) - `resources.${resourceName}.fields.${fieldName}` for field names (used for datagrid header and form input labels) This lets you translate your own resource and field names by passing a `messages` object with a `resources` key: ```js { resources: { shoe: { name: 'Shoe |||| Shoes', fields: { model: 'Model', stock: 'Nb in stock', color: 'Color', }, }, customer: { name: 'Customer |||| Customers', fields: { first_name: 'First name', last_name: 'Last name', dob: 'Date of birth', } } }, ... } ``` As you can see, [polyglot pluralization](http://airbnb.io/polyglot.js/#pluralization) is used here, but it is optional. Using `resources` keys is an alternative to using the `label` prop in Field and Input components, with the advantage of supporting translation. ## Mixing Interface and Domain Translations When translating an admin, interface messages (e.g. "List", "Page", etc.) usually come from a third-party package, while your domain messages (e.g. "Shoe", "Date of birth", etc.) come from your own code. That means you need to combine these messages before passing them to ``. The recipe for combining messages is to use ES6 destructuring: ```jsx import { Admin } from 'react-admin'; import polyglotI18nProvider from 'ra-i18n-polyglot'; // interface translations import englishMessages from 'ra-language-english'; import frenchMessages from 'ra-language-french'; // domain translations import * as domainMessages from './i18n'; const messages = { fr: { ...frenchMessages, ...domainMessages.fr }, en: { ...englishMessages, ...domainMessages.en }, }; const i18nProvider = polyglotI18nProvider(locale => messages[locale]); const App = () => ( ... ); ``` ## `useTranslate` Hook If you need to translate messages in your own components, React-admin provides a `useTranslate` hook, which returns the `translate` function: ```jsx // in src/MyHelloButton.js import React from 'react'; import { useTranslate } from 'react-admin'; const MyHelloButton = () => { const translate = useTranslate(); return ( ); }; export default MyHelloButton; ``` **Tip**: For your message identifiers, choose a different root name than `ra` and `resources`, which are reserved. **Tip**: Don't use `useTranslate` for Field and Input labels, or for page titles, as they are already translated: ```jsx // don't do this // do this instead // or even better, use the default translation key // and translate the `resources.customers.fields.first_name` key ``` ## `withTranslate` HOC If you're stuck with class components, react-admin also exports a `withTranslate` higher-order component, which injects the `translate` function as prop. ```jsx // in src/MyHelloButton.js import React, { Component } from 'react'; import { withTranslate } from 'react-admin'; class MyHelloButton extends Component { render() { const { translate } = this.props; return ( ); } }; export default withTranslate(MyHelloButton); ``` ## Using Specific Polyglot Features Polyglot.js is a fantastic library: in addition to being small, fully maintained, and totally framework agnostic, it provides some nice features such as interpolation and pluralization, that you can use in react-admin. ```js const messages = { 'hello_name': 'Hello, %{name}', 'count_beer': 'One beer |||| %{smart_count} beers', }; // interpolation translate('hello_name', { name: 'John Doe' }); => 'Hello, John Doe.' // pluralization translate('count_beer', { smart_count: 1 }); => 'One beer' translate('count_beer', { smart_count: 2 }); => '2 beers' // default value translate('not_yet_translated', { _: 'Default translation' }); => 'Default translation' ``` To find more detailed examples, please refer to [http://airbnb.io/polyglot.js/](http://airbnb.io/polyglot.js/) ## Translating Validation Errors In Create and Edit views, forms can use custom validators. These validator functions should return translation keys rather than translated messages. React-admin automatically passes these identifiers to the translation function: ```js // in validators/required.js const required = () => (value, allValues, props) => value ? undefined : 'myroot.validation.required'; // in i18n/en.json export default { myroot: { validation: { required: 'Required field', } } }; ``` If the translation depends on a variable, the validator can return an object rather than a translation identifier: ```js // in validators/minLength.js const minLength = (min) => (value, allValues, props) => value.length >= min ? undefined : { message: 'myroot.validation.minLength', args: { min } }; // in i18n/en.js export default { myroot: { validation: { minLength: 'Must be %{min} characters at least', } } }; ``` ## Translating Notification Messages By default, react-admin translates the notification messages. You can pass variables for polyglot interpolation with custom notifications. For example: ```js notify('myroot.hello.world', 'info', { messageArgs: { name: 'Planet Earth' } }); ``` Assuming you have the following in your custom messages: ```js // in src/App.js const messages = { en: { myroot: { hello: { world: 'Hello, %{name}!', }, }, }, }; ``` ## Silencing Translation Warnings By default, the `polyglotI18nProvider` logs a warning in the console each time it is called with a message that can't be found in the current translations. This is a Polyglot feature that helps tracking missing translation messages. But you may want to avoid this for some messages, e.g. error messages from a data source you don't control (like a web server). The fastest way to do so is to use the third parameter of the `polyglotI18nProvider` function to pass the `allowMissing` option to Polyglot at initialization: ```diff import polyglotI18nProvider from 'ra-i18n-polyglot'; import englishMessages from './i18n/englishMessages'; import frenchMessages from './i18n/frenchMessages'; const i18nProvider = polyglotI18nProvider(locale => locale === 'fr' ? frenchMessages : englishMessages, 'en', // Default locale + { + allowMissing: true + } ); ``` **Tip**: Check [the Polyglot documentation](https://airbnb.io/polyglot.js/#options-overview) for a list of options you can pass to Polyglot at startup. This solution is all-or-nothing: you can't silence only *some* missing translation warnings. An alternative solution consists of passing a default translation using the `_` translation option, as explained in the [Using Specific Polyglot Features section](#using-specific-polyglot-features) above.