Internationalization
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, and 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, useLocaleState).
Weāll use a bit of custom vocabulary in this section:
- āi18nā is a shorter way to write āinternationalizationā (an āiā followed by 18 letters followed by ānā)
- ālocaleā is a concept similar to language, but it also includes the concept of country. For instance, there are several English locales (like
en_usanden_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. - ātranslation keyā is a string that is used to identify a piece of text in your application, e.g. āra.action.saveā for the
<SaveButton>label
Anatomy Of An i18nProvider
Just like for data fetching and authentication, react-admin is agnostic to your translation backend. It relies on a provider for internationalization. Itās called the i18nProvider, and it manages translation and language changes.
It should be an object with the following methods:
// in src/i18nProvider.ts
export const i18nProvider = {
// required
translate: (key, options) => string,
changeLocale: locale => Promise<void>,
getLocale: () => string,
// optional
getLocales: () => [{ locale: string, name: string }],
}
Use the <Admin i18nProvider> prop to define the i18nProvider of a react-admin app:
import { i18nProvider } from './i18nProvider';
const App = () => (
<Admin
dataProvider={dataProvider}
i18nProvider={i18nProvider}
>
{/* ... */}
</Admin>
);
If you want to add or update translations, youāll have to provide your own i18nProvider.
Translation Keys
React-admin components use translation keys for their text and rely on the i18nProvider to translate them.
For instance, the <SaveButton> renders the word āSaveā in English and āEnregistrerā in French. This is because the button actually renders the return value of the i18nProvider.translate('ra.action.save') method:
import { Button, useTranslate } from 'react-admin';
const SaveButton = ({ doSave }) => {
const translate = useTranslate(); // returns the i18nProvider.translate() method
return (
<Button onClick={doSave}>
{translate('ra.action.save')}
</Button>
);
};
If you build an app for users from several countries, you should do the same: always use translation keys instead of plain text in your own components:
// in src/MyHelloButton.js
import * as React from "react";
import { useTranslate } from 'react-admin';
export const MyHelloButton = () => {
const translate = useTranslate();
const handleClick = () => {
/* ... */
};
return (
<button>{translate('myroot.hello.world')}</button>
);
};
Check the Translating the UI for example usage of the useTranslate hook.
ra-i18n-polyglot
Although you can build an i18nProvider from scratch, react-admin provides a package called ra-i18n-polyglot that leverages the Polyglot.js library to build an i18nProvider based on a dictionary of translations.
// in src/i18nProvider.js
import polyglotI18nProvider from 'ra-i18n-polyglot';
import en from 'ra-language-english';
import fr from 'ra-language-french';
const translations = { en, fr };
export const i18nProvider = polyglotI18nProvider(
locale => translations[locale],
'en', // default locale
[{ locale: 'en', name: 'English' }, { locale: 'fr', name: 'FranƧais' }],
);
// in src/App.js
import { Admin } from 'react-admin';
import { i18nProvider } from './i18nProvider';
const App = () => (
<Admin
i18nProvider={i18nProvider}
dataProvider={dataProvider}
>
...
</Admin>
);
Check the translation setup documentation for details about ra-i18n-polyglot and how to configure it.
ra-i18n-i18next
React-admin also provides a package called ra-i18n-i18next that leverages the i18next library to build an i18nProvider based on a dictionary of translations.
You might prefer this package over ra-i18n-polyglot when:
- you already use i18next services such as locize
- you want more control on how you organize translations, leveraging multiple files and namespaces
- you want more control on how you load translations
- you want to use features not available in Polyglot such as:
// in src/i18nProvider.js
import i18n from 'i18next';
import { useI18nextProvider, convertRaTranslationsToI18next } from 'ra-i18n-i18next';
const i18nInstance = i18n.use(
resourcesToBackend(language => {
if (language === 'fr') {
return import(
`ra-language-french`
).then(({ default: messages }) =>
convertRaTranslationsToI18next(messages)
);
}
return import(`ra-language-english`).then(({ default: messages }) =>
convertRaTranslationsToI18next(messages)
);
})
);
export const useMyI18nProvider = () => useI18nextProvider({
i18nInstance,
availableLocales: [
{ locale: 'en', name: 'English' },
{ locale: 'fr', name: 'French' },
],
});
// in src/App.tsx
import { Admin } from 'react-admin';
import { useMyI18nProvider } from './i18nProvider';
const App = () => {
const i18nProvider = useMyI18nProvider();
if (!i18nProvider) return null;
return (
<Admin
i18nProvider={i18nProvider}
dataProvider={dataProvider}
>
...
</Admin>
);
};
Check the ra-i18n-i18next documentation for details.
Translation Files
ra-i18n-polyglot relies on JSON objects for translations. This means that the only thing required to add support for a new language is a JSON file.
Translation files match a translation key to a translated text. They look like the following:
const englishMessages = {
// react-admin components
ra: {
action: {
cancel: 'Cancel',
clone: 'Clone',
confirm: 'Confirm',
create: 'Create',
delete: 'Delete',
edit: 'Edit',
export: 'Export',
list: 'List',
refresh: 'Refresh',
save: 'Save',
},
boolean: {
true: 'Yes',
false: 'No',
null: ' ',
},
/* ...*/
},
// resources and fields
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',
}
}
/* ...*/
},
// custom components
acme: {
buttons: {
allow: 'Allow',
deny: 'Deny',
},
notifications: {
error: 'An error occurred',
success: 'Success',
},
/* ...*/
}
};
Tip: The default (English) messages are available in the ra-language-english package source.
When building an internationalized app with react-admin, the usual workflow is therefore to let developers write the main translation file. Then, pass this file to a team of translators, with the task to translate it. They can use any software they want for that (even software using Gettext/PO files, as itās possible to convert POT to and from JSON). Finally, aggregate all the translations into an i18nProvider.
Check the translation setup documentation to understand how to build your own translation file, the list of available translations to find a translation for your language, and Translating the UI to understand how to translate react-admin commponents.
Translating Values
Beyond the UX, you may need to translate values in your data. For instance, you may want to display a translated label for a status field, or a translated name for a category.
This implies that your data model stores the translations in a way that can be used by the UI. The advised solution is to store the translations as a JSON object in the data itself. For example, a Category resource could have a name field that contains the translations for each locale:
{
"id": 1,
"name": {
"en": "Shoes",
"fr": "Chaussures"
}
}
If you follow this data structure, you can use special fields and inputs to display and edit the translated values.
-
<TranslatableField>lets you display all the translations for a field in a single component.<TranslatableFields locales={['en', 'fr']}> <TextField source="title" /> <TextField source="description" /> </TranslatableFields> -
<TranslatableInputs>lets you edit all the translations for a field in a single component.<TranslatableInputs locales={['en', 'fr']}> <TextInput source="name" /> <RichTextInput source="description" /> </TranslatableInputs>
Check out the documentation for Translatable Fields and Translatable Inputs for more details.
Localization
For numeric and temporal values, react-admin benefits from the Single-Page Application architecture. As the application executes in the browser, it uses the browserās locale by default to format numbers and dates.
For instance, the <DateField> renders the date in the userās locale, using the Intl.DateTimeFormat API.
<DateField source="published_at" />
// renders the record { id: 1234, published_at: new Date('2017-04-23') } as
<span>4/23/2017</span>
You can force a specific locale by passing the locale prop to the field:
<DateField source="published_at" locale="fr-FR" />
// renders the record { id: 1234, published_at: new Date('2017-04-23') } as
<span>23/04/2017</span>
The following components take advantage of browser localization:
