<TranslatableFields>

You may have fields which are translated in multiple languages and want users to verify each translation. To display them, you can use the <TranslatableFields> component.

Usage

<TranslatableFields> expects the translatable values of a record to have the following structure:

const record = {
    id: 123,
    title: {
        en: 'Doctors Without Borders',
        fr: 'Médecins sans frontières',
    },
    description: {
        en:
            'International humanitarian medical non-governmental organisation of French origin',
        fr:
            "Organisation non gouvernementale (ONG) médicale humanitaire internationale d'origine française fondée en 1971 à Paris",
    }
}

To display translatable values, wrap the fields you want to render with <TranslatableFields>, like so:

import {
  Show,
  SimpleShowLayout,
  TextField,
  TranslatableFields,
} from "react-admin";

export const OrganizationShow = () => (
  <Show>
      <SimpleShowLayout>
        <TranslatableFields locales={['en', 'fr']}>
            <TextField source="title" />
            <TextField source="description" />
        </TranslatableFields>
      </SimpleShowLayout>
  </Show>
);

<TranslatableFields> lets users select a locale using Material UI tabs with the locale code as their labels.

You may override the tabs labels using translation keys following this format: ra.locales.[locale_code]. For instance, ra.locales.en or ra.locales.fr.

Tip: If you want to display only one translation, you don’t need <TranslatableFields>. Just use a regular field with a path as source:

{/* always display the English title */}
<TextField source="title.en" />

defaultLocale

React-admin uses the user locale as the default locale in this field. You can override this setting using the defaultLocale prop.

<TranslatableFields locales={['en', 'fr']} defaultLocale="fr">
    <TextField source="title" />
    <TextField source="description" />
</TranslatableFields>

groupKey

If you have multiple TranslatableFields on the same page, you should specify a groupKey so that react-admin can create unique identifiers for accessibility.

<TranslatableFields locales={['en', 'fr']} groupKey="essential-fields">
    <TextField source="name" />
    <TextField source="description" />
</TranslatableFields>

selector

You may override the language selector using the selector prop, which accepts a React element:

// in src/NgoShow.tsx
import {
  Show,
  SimpleShowLayout,
  TextField,
  TranslatableFields,
  useTranslatableContext,
} from "react-admin";

const Selector = () => {
  const { locales, selectLocale, selectedLocale } = useTranslatableContext();

  const handleChange = (event) => {
    selectLocale(event.target.value);
  };

  return (
    <select
      aria-label="Select the locale"
      onChange={handleChange}
      value={selectedLocale}
    >
      {locales.map((locale) => (
        <option
          key={locale}
          value={locale}
          // This allows to correctly link the containers for each locale to their labels
          id={`translatable-header-${locale}`}
        >
          {locale}
        </option>
      ))}
    </select>
  );
};

export const NgoShow = () => (
  <Show>
    <SimpleShowLayout>
      <TranslatableFields locales={["en", "fr"]} selector={<Selector />}>
        <TextField source="title" />
        <TextField source="description" />
      </TranslatableFields>
    </SimpleShowLayout>
  </Show>
);

Using Translatable Fields In List Views

The TranslatableFields component is not meant to be used inside a List as you probably don’t want to have tabs inside multiple lines. The simple solution to display a translatable value would be to specify its source like this: name.en. However, you may want to display its translation for the current admin locale.

In this case, you’ll have to get the current locale through the useLocaleState hook and set the translatable field source dynamically.

import { List, Datagrid, TextField, ReferenceArrayField, SingleFieldList, ChipField, useLocaleState } from 'react-admin';

const PostList = () => {
    const [locale] = useLocaleState();

    return (
        <List>
            <Datagrid>
                <TextField source={`name.${locale}`} />
                <ReferenceArrayField
                    label="Tags"
                    reference="tags"
                    source="tags"
                    sortBy="tags.name"
                    sort={{ field: `name.${locale}`, order: 'ASC' }}
                >
                    <SingleFieldList>
                        <ChipField source={`name.${locale}`} size="small" />
                    </SingleFieldList>
                </ReferenceArrayField>
            </Datagrid>
        </List>
    )
}

Note that you can’t have an optimized Datagrid when doing so, as changing the locale wouldn’t trigger a render of its children.

The same pattern applies to show views when you don’t want to display all translations: get the locale from the useLocale hook and dynamically set the source prop of the translatable fields.