Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Translatable inputs and fields #5810

Merged
merged 27 commits into from
Jan 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d64a168
Introduce TranslatableInputs and TranslatableFields
djhi Jan 22, 2021
36fa107
Update simple example with translatable tags names
djhi Jan 22, 2021
a8854ff
Fix build
djhi Jan 22, 2021
e7d5436
Refactor and fix tabcontent accessibility
djhi Jan 22, 2021
492f144
Add TranslatableFields tests
djhi Jan 22, 2021
8e26012
Added TranslatableInputs tests
djhi Jan 22, 2021
e9f7aa6
Add selector customization test for TranslatableFields
djhi Jan 22, 2021
715316d
Rename getLabel function type
djhi Jan 22, 2021
04be265
Remove children render function
djhi Jan 22, 2021
de9042a
Use locale instead of language
djhi Jan 22, 2021
14585db
Fix example
djhi Jan 22, 2021
6e4b722
Introduce group prefix foe fields & ensure each field is wrapped in a…
djhi Jan 22, 2021
f91f9a7
More tests
djhi Jan 25, 2021
186ac8b
Uniformize group prefix as groupKey
djhi Jan 26, 2021
5ed5ce5
Update PostShow example to showcase dynamic display dependant on curr…
djhi Jan 26, 2021
85231cd
Initial documentation
djhi Jan 26, 2021
2429b03
Add note about displaying single translations
djhi Jan 26, 2021
ca97d14
Apply suggestions from code review
djhi Jan 26, 2021
44f1ab7
Apply review on documentation
djhi Jan 26, 2021
f2687ac
Ensure styles are overridable
djhi Jan 26, 2021
7fb4b09
Fix typo
djhi Jan 26, 2021
08c56ad
Add validation test
djhi Jan 26, 2021
fac67a2
Documentation for useTranslatableContext
djhi Jan 26, 2021
feedb0d
Documentation Fix display
djhi Jan 26, 2021
b7942ad
Add Section In Translation Documentation
djhi Jan 26, 2021
da4003f
Update Translation.md
fzaninotto Jan 26, 2021
74b79d4
Fix linter warning
djhi Jan 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
133 changes: 133 additions & 0 deletions docs/Fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,139 @@ export const PostShow = (props) => (
);
```

## Translatable Fields

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, which expects the translatable values to have the following structure:

```js
{
name: {
en: 'The english value',
fr: 'The french value',
tlh: 'The klingon value',
},
description: {
en: 'The english value',
fr: 'The french value',
tlh: 'The klingon value',
}
}
djhi marked this conversation as resolved.
Show resolved Hide resolved
```

This is how to use it:

```jsx
<TranslatableFields locales={['en', 'fr']}>
<TextField source="name">
<TextField source="description">
</TranslatableFields>
```

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

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

By default, `<TranslatableFields>` will allow users to select the displayed 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`.

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

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

const handleChange = (event): void => {
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>
);
};

<TranslatableFields
record={record}
resource="products"
basePath="/products"
locales={['en', 'fr']}
selector={<Selector />}
>
<TextField source="name" />
<TextField source="description" />
</TranslatableFields>
```

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

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

### Using Translatable Fields In List or Show 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 `useLocale` hook and set the translatable field `source` dynamically.

{% raw %}
```jsx
const PostList = (props) => {
const locale = useLocale();

return (
<List {...props}>
<Datagrid>
<TextField source={`name.${locale}`}>
<ReferenceArrayField
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ReferenceField here dilutes the example. Could you simply use a TextField in the tags list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to show the sort field

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure you want to remove it?

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>
)
}
```
{% endraw %}

Note that you can't have an [optimized](https://marmelab.com/react-admin/List.html#performance) 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.

## Recipes

### Styling Fields
Expand Down
105 changes: 105 additions & 0 deletions docs/Inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,111 @@ export default ArtistEdit;

Check [the `ra-relationships` documentation](https://marmelab.com/ra-enterprise/modules/ra-relationships#referencemanytomanyinput) for more details.

## Translatable Inputs

You may have inputs which are translated in multiple languages and want users to edit translations for each language separately. To display them, you can use the `<TranslatableInputs>` component, which expects the translatable values to have the following structure:

```js
{
name: {
en: 'The english value',
fr: 'The french value',
tlh: 'The klingon value',
},
description: {
en: 'The english value',
fr: 'The french value',
tlh: 'The klingon value',
}
}
djhi marked this conversation as resolved.
Show resolved Hide resolved
```

This is how to use it:

```jsx
<TranslatableInputs locales={['en', 'fr']}>
<TextInput source="name">
<RichTextInput source="description">
</TranslatableInputs>
```

djhi marked this conversation as resolved.
Show resolved Hide resolved
React-admin uses the user locale as the default locale in this field. You can override this setting using the `defaultLocale` prop.

```jsx
<TranslatableInputs locales={['en', 'fr']} defaultLocale="fr">
<TextInput source="name">
<RichTextInput source="description">
</TranslatableInputs>
```

By default, `<TranslatableInputs>` will allow users to select the displayed 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`.

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

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

const handleChange = (event): void => {
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>
);
};

<TranslatableInputs
record={record}
resource="products"
basePath="/products"
locales={['en', 'fr']}
selector={<Selector />}
>
<TextInput source="name" />
<RichTextInput source="description" />
</TranslatableInputs>
```

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

```jsx
<TranslatableInputs locales={['en', 'fr']} groupKey="essential-fields">
<TextInput source="name">
<RichTextInput source="description">
</TranslatableInputs>
```

You can add validators to any of the inputs inside a `TranslatableInputs`. If an input has some validation error, the label of its parent tab will be highlighted as invalid:

```jsx
<TranslatableInputs locales={['en', 'fr']}>
<TextInput source="name" validate={[required()]}>
<RichTextInput source="description" validate={[maxLength(100)]}>
</TranslatableInputs>
```

## Recipes

### Transforming Input Value to/from Record
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should have a section about TranslatableFields and Inputs in the i18n documentation as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean a very simple one redirecting to the fields and inputs pages?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolutely!

Expand Down
24 changes: 24 additions & 0 deletions docs/Translation.md
Original file line number Diff line number Diff line change
Expand Up @@ -697,3 +697,27 @@ const i18nProvider = polyglotI18nProvider(locale =>
**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.

## Translating Record Fields

Some of your records may contain fields that are translated in multiple languages. It's common, in such cases, to offer an interface allowing admin users to see and edit each translation. React-admin provides 2 components for that:

- To display translatable fields, use the [`<TranslatableFields>`](/Fields.html#translatable-fields) component
- To edit translatable fields, use the [`<TranslatableInputs>`](/Inputs.html#translatable-inputs) component

They both expect the translatable values to have the following structure:

```js
{
name: {
en: 'The english value',
fr: 'The french value',
tlh: 'The klingon value',
},
description: {
en: 'The english value',
fr: 'The french value',
tlh: 'The klingon value',
}
}
```