Skip to content

Commit d2ca7ad

Browse files
committed
Allow resource specific translations for pages and <CreateButton>
1 parent a99880b commit d2ca7ad

24 files changed

+797
-35
lines changed

docs/Buttons.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ const CommentList = () => (
694694
| Prop | Required | Type | Default | Description |
695695
| ------------- | -------- | --------------- | ------------------ | -------------------------------------------- |
696696
| `resource` | Optional | `string` | - | Target resource, e.g. 'posts' |
697-
| `label` | Optional | `string` | 'ra.action.create' | label or translation message to use |
697+
| `label` | Optional | `string` | - | label or translation message to use |
698698
| `icon` | Optional | `ReactElement` | - | iconElement, e.g. `<CommentIcon />` |
699699
| `scrollToTop` | Optional | `boolean` | `true` | Scroll to top after link |
700700

@@ -704,6 +704,39 @@ It also supports [all the other `<Button>` props](#button).
704704

705705
**Tip:** To allow users to create a record without leaving the current view, use the [`<CreateInDialogButton>`](./CreateInDialogButton.md) component.
706706

707+
### `label`
708+
709+
By default, the label for the `<CreateButton>` is the translation key `ra.action.create` that translates to "Create".
710+
711+
You can customize this title by providing a resource specific translation with the key `resources.RESOURCE.action.create` (e.g. `resources.posts.action.create`):
712+
713+
```js
714+
// in src/i18n/en.js
715+
import englishMessages from 'ra-language-english';
716+
717+
export const en = {
718+
...englishMessages,
719+
resources: {
720+
posts: {
721+
name: 'Post |||| Posts',
722+
action: {
723+
create: 'New post'
724+
}
725+
},
726+
},
727+
...
728+
};
729+
```
730+
731+
You can also customize this label by specifying a custom `label` prop:
732+
733+
734+
```jsx
735+
export const PostCreateButton = () => (
736+
<CreateButton label="New post" />
737+
);
738+
```
739+
707740
### `scrollToTop`
708741

709742
By default, `<CreateButton>` scrolls the page to the top after redirecting. You can disable it as follows:

docs/Create.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,29 @@ To override the style of all instances of `<Create>` components using the [appli
342342

343343
## `title`
344344

345-
By default, the title for the `Create` view is "Create [resource_name]".
345+
By default, the title for the `Create` view is the translation key `ra.page.create` that translates to "Create [resource_name]" as we also pass the translation of the [resource name](./TranslationTranslating.md#translating-resource-and-field-names) in its singular form.
346346

347-
You can customize this title by specifying a custom `title` prop:
347+
You can customize this title by providing a resource specific translation with the key `resources.RESOURCE.page.create` (e.g. `resources.posts.page.create`):
348+
349+
```js
350+
// in src/i18n/en.js
351+
import englishMessages from 'ra-language-english';
352+
353+
export const en = {
354+
...englishMessages,
355+
resources: {
356+
posts: {
357+
name: 'Post |||| Posts',
358+
page: {
359+
create: 'New post'
360+
}
361+
},
362+
},
363+
...
364+
};
365+
```
366+
367+
You can also customize this title by specifying a custom `title` prop:
348368

349369
```jsx
350370
export const PostCreate = () => (

docs/Edit.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,29 @@ To override the style of all instances of `<Edit>` components using the [applica
571571

572572
## `title`
573573

574-
By default, the title for the Edit view is “Edit [resource_name] [record representation]”. Check the [`<Resource recordRepresentation>`](./Resource.md#recordrepresentation) prop for more details.
574+
By default, the title for the Edit view is the translation key `ra.page.edit` that translates to “Edit [resource_name] [record representation]”. Check the [`<Resource recordRepresentation>`](./Resource.md#recordrepresentation) prop for more details.
575575

576-
You can customize this title by specifying a custom `title` string:
576+
You can customize this title by providing a resource specific translation with the key `resources.RESOURCE.page.edit` (e.g. `resources.posts.page.edit`):
577+
578+
```js
579+
// in src/i18n/en.js
580+
import englishMessages from 'ra-language-english';
581+
582+
export const en = {
583+
...englishMessages,
584+
resources: {
585+
posts: {
586+
name: 'Post |||| Posts',
587+
page: {
588+
edit: 'Update post "%{recordRepresentation}"'
589+
}
590+
},
591+
},
592+
...
593+
};
594+
```
595+
596+
You can also customize this title by specifying a custom `title` string:
577597

578598
```jsx
579599
export const PostEdit = () => (

docs/List.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,11 +976,31 @@ const Admin = () => {
976976

977977
## `title`
978978

979-
The default title for a list view is the plural name of the resource (e.g. "Posts").
979+
The default title for a list view is the translation key `ra.page.list` that translates to [the plural name of the resource](./TranslationTranslating.md#translating-resource-and-field-names) (e.g. "Posts").
980980

981981
![List title](./img/list-title.png)
982982

983-
Use the `title` prop to customize the List view title:
983+
You can customize this title by providing a resource specific translation with the key `resources.RESOURCE.page.list` (e.g. `resources.posts.page.list`):
984+
985+
```js
986+
// in src/i18n/en.js
987+
import englishMessages from 'ra-language-english';
988+
989+
export const en = {
990+
...englishMessages,
991+
resources: {
992+
posts: {
993+
name: 'Post |||| Posts',
994+
page: {
995+
list: 'Post list'
996+
}
997+
},
998+
},
999+
...
1000+
};
1001+
```
1002+
1003+
You can also customize this title by specifying a custom `title` prop:
9841004

9851005
```jsx
9861006
export const PostList = () => (

docs/Show.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,29 @@ To override the style of all instances of `<Show>` using the [application-wide s
385385

386386
## `title`
387387

388-
By default, the title for the Show view is "[resource_name] [record representation]". Check the [`<Resource recordRepresentation>`](./Resource.md#recordrepresentation) prop for more details.
388+
By default, the title for the Show view is the translation key `ra.page.show` that translates to "[resource_name] [record representation]". Check the [`<Resource recordRepresentation>`](./Resource.md#recordrepresentation) prop for more details.
389+
390+
You can customize this title by providing a resource specific translation with the key `resources.RESOURCE.page.show` (e.g. `resources.posts.page.show`):
391+
392+
```js
393+
// in src/i18n/en.js
394+
import englishMessages from 'ra-language-english';
395+
396+
export const en = {
397+
...englishMessages,
398+
resources: {
399+
posts: {
400+
name: 'Post |||| Posts',
401+
page: {
402+
show: 'Details of post "%{recordRepresentation}"'
403+
}
404+
},
405+
},
406+
...
407+
};
408+
```
389409

390-
You can customize this title by specifying a custom `title` string:
410+
You can also customize this title by specifying a custom `title` string:
391411

392412
```jsx
393413
export const PostShow = () => (

packages/ra-core/src/controller/create/CreateBase.spec.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { screen, render, waitFor, fireEvent } from '@testing-library/react';
55
import { testDataProvider } from '../../dataProvider';
66
import {
77
AccessControl,
8+
DefaultTitle,
89
NoAuthProvider,
910
WithAuthProviderNoAccessControl,
1011
} from './CreateBase.stories';
@@ -268,4 +269,18 @@ describe('CreateBase', () => {
268269
resolveCanAccess!(true);
269270
await screen.findByText('save');
270271
});
272+
273+
it('should provide a default title', async () => {
274+
render(<DefaultTitle translations="default" />);
275+
await screen.findByText('Create Post (en)');
276+
fireEvent.click(screen.getByText('FR'));
277+
await screen.findByText('Créer Article (fr)');
278+
});
279+
280+
it('should allow resource specific default title', async () => {
281+
render(<DefaultTitle translations="resource specific" />);
282+
await screen.findByText('Create an article (en)');
283+
fireEvent.click(screen.getByText('FR'));
284+
await screen.findByText('Créer un article (fr)');
285+
});
271286
});

packages/ra-core/src/controller/create/CreateBase.stories.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,93 @@
11
import * as React from 'react';
2+
import englishMessages from 'ra-language-english';
3+
import frenchMessages from 'ra-language-french';
4+
import polyglotI18nProvider from 'ra-i18n-polyglot';
25
import {
36
AuthProvider,
47
CoreAdminContext,
58
CreateBase,
69
CreateBaseProps,
710
DataProvider,
11+
I18nProvider,
12+
mergeTranslations,
813
SaveHandlerCallbacks,
914
testDataProvider,
15+
useCreateContext,
16+
useLocaleState,
1017
useSaveContext,
1118
} from '../..';
1219

1320
export default {
1421
title: 'ra-core/controller/CreateBase',
1522
};
1623

24+
const defaultI18nProvider = polyglotI18nProvider(
25+
locale =>
26+
locale === 'fr'
27+
? mergeTranslations(frenchMessages, {
28+
resources: {
29+
posts: {
30+
name: 'Article |||| Articles',
31+
},
32+
},
33+
})
34+
: englishMessages,
35+
'en'
36+
);
37+
38+
const customI18nProvider = polyglotI18nProvider(
39+
locale =>
40+
locale === 'fr'
41+
? mergeTranslations(frenchMessages, {
42+
resources: {
43+
posts: {
44+
page: {
45+
create: 'Créer un article',
46+
},
47+
},
48+
},
49+
})
50+
: mergeTranslations(englishMessages, {
51+
resources: {
52+
posts: {
53+
page: {
54+
create: 'Create an article',
55+
},
56+
},
57+
},
58+
}),
59+
'en'
60+
);
61+
62+
export const DefaultTitle = ({
63+
translations = 'default',
64+
i18nProvider = translations === 'default'
65+
? defaultI18nProvider
66+
: customI18nProvider,
67+
}: {
68+
i18nProvider?: I18nProvider;
69+
translations?: 'default' | 'resource specific';
70+
}) => (
71+
<CoreAdminContext
72+
dataProvider={defaultDataProvider}
73+
i18nProvider={i18nProvider}
74+
>
75+
<CreateBase {...defaultProps}>
76+
<Title />
77+
</CreateBase>
78+
</CoreAdminContext>
79+
);
80+
81+
DefaultTitle.args = {
82+
translations: 'default',
83+
};
84+
DefaultTitle.argTypes = {
85+
translations: {
86+
options: ['default', 'resource specific'],
87+
control: { type: 'radio' },
88+
},
89+
};
90+
1791
export const NoAuthProvider = ({
1892
dataProvider = defaultDataProvider,
1993
callTimeOptions,
@@ -102,3 +176,19 @@ const Child = ({
102176

103177
return <button onClick={handleClick}>save</button>;
104178
};
179+
180+
const Title = () => {
181+
const { defaultTitle } = useCreateContext();
182+
const [locale, setLocale] = useLocaleState();
183+
return (
184+
<div>
185+
<strong>
186+
{defaultTitle} ({locale})
187+
</strong>
188+
<div>
189+
<button onClick={() => setLocale('en')}>EN</button>
190+
<button onClick={() => setLocale('fr')}>FR</button>
191+
</div>
192+
</div>
193+
);
194+
};

packages/ra-core/src/controller/create/useCreateController.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,10 @@ export const useCreateController = <
190190
);
191191

192192
const getResourceLabel = useGetResourceLabel();
193-
const defaultTitle = translate('ra.page.create', {
194-
name: getResourceLabel(resource, 1),
193+
const defaultTitle = translate(`resources.${resource}.page.create`, {
194+
_: translate('ra.page.create', {
195+
name: getResourceLabel(resource, 1),
196+
}),
195197
});
196198

197199
return {

packages/ra-core/src/controller/edit/EditBase.spec.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
55
import { testDataProvider } from '../../dataProvider';
66
import {
77
AccessControl,
8+
DefaultTitle,
89
NoAuthProvider,
910
WithAuthProviderNoAccessControl,
1011
} from './EditBase.stories';
@@ -361,4 +362,18 @@ describe('EditBase', () => {
361362
resolveCanAccess!(true);
362363
await screen.findByText('Hello');
363364
});
365+
366+
it('should provide a default title', async () => {
367+
render(<DefaultTitle translations="default" />);
368+
await screen.findByText('Post Hello (en)');
369+
fireEvent.click(screen.getByText('FR'));
370+
await screen.findByText('Article Hello (fr)');
371+
});
372+
373+
it('should allow resource specific default title', async () => {
374+
render(<DefaultTitle translations="resource specific" />);
375+
await screen.findByText('Update article Hello (en)');
376+
fireEvent.click(screen.getByText('FR'));
377+
await screen.findByText("Modifier l'article Hello (fr)");
378+
});
364379
});

0 commit comments

Comments
 (0)