diff --git a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.js b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.js
index 5ed35873c32..3074a0f94d2 100644
--- a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.js
+++ b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.js
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { Account } from '@toolpad/core/Account';
const demoSession = {
@@ -31,17 +31,15 @@ export default function AccountCustomLocaleText() {
}, []);
return (
-
-
- {/* preview-start */}
-
- {/* preview-end */}
-
-
+
+ {/* preview-start */}
+
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx
index c74db6789c7..9fd5e3f3783 100644
--- a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx
+++ b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx
@@ -1,9 +1,5 @@
import * as React from 'react';
-import {
- AuthenticationContext,
- Session,
- SessionContext,
-} from '@toolpad/core/AppProvider';
+import { AppProvider, type Session } from '@toolpad/core/AppProvider';
import { Account } from '@toolpad/core/Account';
const demoSession = {
@@ -35,17 +31,15 @@ export default function AccountCustomLocaleText() {
}, []);
return (
-
-
- {/* preview-start */}
-
- {/* preview-end */}
-
-
+
+ {/* preview-start */}
+
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx.preview b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx.preview
index 882b98486be..1a812378926 100644
--- a/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx.preview
+++ b/docs/data/toolpad/core/components/account/AccountCustomLocaleText.tsx.preview
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/account/AccountCustomSlotProps.js b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.js
index 3995c7e1345..4c3544e3cec 100644
--- a/docs/data/toolpad/core/components/account/AccountCustomSlotProps.js
+++ b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import Logout from '@mui/icons-material/Logout';
import { Account } from '@toolpad/core/Account';
-import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
const demoSession = {
user: {
@@ -32,36 +32,34 @@ export default function AccountCustomSlotProps() {
}, []);
return (
-
-
- {/* preview-start */}
- ,
- },
- preview: {
- variant: 'expanded',
- slotProps: {
- avatarIconButton: {
- sx: {
- width: 'fit-content',
- margin: 'auto',
- },
- },
- avatar: {
- variant: 'rounded',
+
+ {/* preview-start */}
+ ,
+ },
+ preview: {
+ variant: 'expanded',
+ slotProps: {
+ avatarIconButton: {
+ sx: {
+ width: 'fit-content',
+ margin: 'auto',
},
},
+ avatar: {
+ variant: 'rounded',
+ },
},
- }}
- />
- {/* preview-end */}
-
-
+ },
+ }}
+ />
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx
index fcfa8bd028c..abfcf8085fe 100644
--- a/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx
+++ b/docs/data/toolpad/core/components/account/AccountCustomSlotProps.tsx
@@ -1,11 +1,7 @@
import * as React from 'react';
import Logout from '@mui/icons-material/Logout';
import { Account } from '@toolpad/core/Account';
-import {
- AuthenticationContext,
- SessionContext,
- Session,
-} from '@toolpad/core/AppProvider';
+import { AppProvider, Session } from '@toolpad/core/AppProvider';
const demoSession = {
user: {
@@ -36,36 +32,34 @@ export default function AccountCustomSlotProps() {
}, []);
return (
-
-
- {/* preview-start */}
- ,
- },
- preview: {
- variant: 'expanded',
- slotProps: {
- avatarIconButton: {
- sx: {
- width: 'fit-content',
- margin: 'auto',
- },
- },
- avatar: {
- variant: 'rounded',
+
+ {/* preview-start */}
+ ,
+ },
+ preview: {
+ variant: 'expanded',
+ slotProps: {
+ avatarIconButton: {
+ sx: {
+ width: 'fit-content',
+ margin: 'auto',
},
},
+ avatar: {
+ variant: 'rounded',
+ },
},
- }}
- />
- {/* preview-end */}
-
-
+ },
+ }}
+ />
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountCustomUserDetails.js b/docs/data/toolpad/core/components/account/AccountCustomUserDetails.js
index f9ebd5d6e1a..b43327cd9d3 100644
--- a/docs/data/toolpad/core/components/account/AccountCustomUserDetails.js
+++ b/docs/data/toolpad/core/components/account/AccountCustomUserDetails.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Account } from '@toolpad/core/Account';
-import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { UserOrg } from '../UserOrg';
const demoSession = {
@@ -30,16 +30,14 @@ export default function AccountCustomUserDetails() {
}, []);
return (
-
-
- {/* preview-start */}
-
- {/* preview-end */}
-
-
+
+ {/* preview-start */}
+
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountCustomUserDetails.tsx b/docs/data/toolpad/core/components/account/AccountCustomUserDetails.tsx
index 11aecc9f557..b2fc8ee9705 100644
--- a/docs/data/toolpad/core/components/account/AccountCustomUserDetails.tsx
+++ b/docs/data/toolpad/core/components/account/AccountCustomUserDetails.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
import { Account } from '@toolpad/core/Account';
-import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { UserOrg, CustomSession } from '../UserOrg';
const demoSession: CustomSession = {
@@ -32,16 +32,14 @@ export default function AccountCustomUserDetails() {
}, []);
return (
-
-
- {/* preview-start */}
-
- {/* preview-end */}
-
-
+
+ {/* preview-start */}
+
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.js b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.js
index 5f8ea177353..d9602d020d6 100644
--- a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.js
+++ b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.js
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { Account } from '@toolpad/core/Account';
const demoSession = {
@@ -24,12 +24,10 @@ export default function AccountDemoSignedIn() {
}, []);
return (
-
-
- {/* preview-start */}
-
- {/* preview-end */}
-
-
+
+ {/* preview-start */}
+
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx
index 718b4ed60fa..de5de86d2b5 100644
--- a/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx
+++ b/docs/data/toolpad/core/components/account/AccountDemoSignedIn.tsx
@@ -1,9 +1,5 @@
import * as React from 'react';
-import {
- AuthenticationContext,
- SessionContext,
- type Session,
-} from '@toolpad/core/AppProvider';
+import { AppProvider, type Session } from '@toolpad/core/AppProvider';
import { Account } from '@toolpad/core/Account';
const demoSession = {
@@ -28,12 +24,10 @@ export default function AccountDemoSignedIn() {
}, []);
return (
-
-
- {/* preview-start */}
-
- {/* preview-end */}
-
-
+
+ {/* preview-start */}
+
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountDemoSignedOut.js b/docs/data/toolpad/core/components/account/AccountDemoSignedOut.js
index c63ddabcf9a..673420426a4 100644
--- a/docs/data/toolpad/core/components/account/AccountDemoSignedOut.js
+++ b/docs/data/toolpad/core/components/account/AccountDemoSignedOut.js
@@ -1,5 +1,9 @@
import * as React from 'react';
-import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider';
+import {
+ AuthenticationContext,
+ LocalizationProvider,
+ SessionContext,
+} from '@toolpad/core/AppProvider';
import { Account } from '@toolpad/core/Account';
const demoSession = {
@@ -24,10 +28,14 @@ export default function AccountDemoSignedOut() {
}, []);
return (
-
-
-
-
-
+ // preview-start
+
+
+
+
+
+
+
+ // preview-end
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountDemoSignedOut.tsx b/docs/data/toolpad/core/components/account/AccountDemoSignedOut.tsx
index 1ca57c63430..a0a8d01e75e 100644
--- a/docs/data/toolpad/core/components/account/AccountDemoSignedOut.tsx
+++ b/docs/data/toolpad/core/components/account/AccountDemoSignedOut.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import {
AuthenticationContext,
+ LocalizationProvider,
SessionContext,
type Session,
} from '@toolpad/core/AppProvider';
@@ -28,10 +29,14 @@ export default function AccountDemoSignedOut() {
}, []);
return (
-
-
-
-
-
+ // preview-start
+
+
+
+
+
+
+
+ // preview-end
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountDemoSignedOut.tsx.preview b/docs/data/toolpad/core/components/account/AccountDemoSignedOut.tsx.preview
index b0489ce20ca..f3af4f1a296 100644
--- a/docs/data/toolpad/core/components/account/AccountDemoSignedOut.tsx.preview
+++ b/docs/data/toolpad/core/components/account/AccountDemoSignedOut.tsx.preview
@@ -1,5 +1,7 @@
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.js b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.js
index c938fa78e17..b0b354e8e8e 100644
--- a/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.js
+++ b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.js
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { AuthenticationContext, SessionContext } from '@toolpad/core/AppProvider';
+import { AppProvider } from '@toolpad/core/AppProvider';
import { Account } from '@toolpad/core/Account';
import CustomMenu from './CustomMenu';
@@ -25,16 +25,14 @@ export default function AccountSlotsAccountSwitcher() {
}, []);
return (
-
-
- {/* preview-start */}
-
- {/* preview-end */}
-
-
+
+ {/* preview-start */}
+
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx
index 7e89f9663a3..2558e0c1d22 100644
--- a/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx
+++ b/docs/data/toolpad/core/components/account/AccountSlotsAccountSwitcher.tsx
@@ -1,9 +1,5 @@
import * as React from 'react';
-import {
- AuthenticationContext,
- SessionContext,
- type Session,
-} from '@toolpad/core/AppProvider';
+import { AppProvider, type Session } from '@toolpad/core/AppProvider';
import { Account } from '@toolpad/core/Account';
import CustomMenu from './CustomMenu';
@@ -29,16 +25,14 @@ export default function AccountSlotsAccountSwitcher() {
}, []);
return (
-
-
- {/* preview-start */}
-
- {/* preview-end */}
-
-
+
+ {/* preview-start */}
+
+ {/* preview-end */}
+
);
}
diff --git a/docs/data/toolpad/core/components/account/account.md b/docs/data/toolpad/core/components/account/account.md
index c506aca73b7..cfc30537fbd 100644
--- a/docs/data/toolpad/core/components/account/account.md
+++ b/docs/data/toolpad/core/components/account/account.md
@@ -26,7 +26,7 @@ If a `session` object is present, the component is rendered as a dropdown contai
When signed out, the component renders as an inline sign in button within the dashboard layout.
-{{"demo": "AccountDemoSignedOut.js", "bg": "outlined", "defaultCodeOpen": false }}
+{{"demo": "AccountDemoSignedOut.js", "bg": "outlined", "defaultCodeOpen": false}}
## Customization
@@ -61,7 +61,7 @@ You can build advanced menus – such as a tenant switcher – by passing in a c
{{"demo": "AccountSlotsAccountSwitcher.js", "bg": "outlined"}}
-### Labels
+### Localization
You can pass in custom labels – including of different languages – using the `localeText` prop.
diff --git a/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.js b/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.js
new file mode 100644
index 00000000000..0e5ff86e899
--- /dev/null
+++ b/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.js
@@ -0,0 +1,36 @@
+import * as React from 'react';
+import { AppProvider } from '@toolpad/core/AppProvider';
+import { SignInPage } from '@toolpad/core/SignInPage';
+import hiIN from '@toolpad/core/locales/hiIN';
+
+const providers = [
+ { id: 'github', name: 'GitHub' },
+ { id: 'google', name: 'Google' },
+ { id: 'credentials', name: 'Email and Password' },
+];
+
+const signIn = async (provider) => {
+ const promise = new Promise((resolve) => {
+ setTimeout(() => {
+ console.log(`Sign in with ${provider.id}`);
+ resolve({ error: 'This is a mock error message.' });
+ }, 500);
+ });
+ return promise;
+};
+
+export default function LocaleSignInPage() {
+ return (
+ // preview-start
+
+
+
+ // preview-end
+ );
+}
diff --git a/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.tsx b/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.tsx
new file mode 100644
index 00000000000..cc64d7cc187
--- /dev/null
+++ b/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.tsx
@@ -0,0 +1,42 @@
+import * as React from 'react';
+import { AppProvider } from '@toolpad/core/AppProvider';
+import {
+ SignInPage,
+ type AuthProvider,
+ type AuthResponse,
+} from '@toolpad/core/SignInPage';
+import hiIN from '@toolpad/core/locales/hiIN';
+
+const providers = [
+ { id: 'github', name: 'GitHub' },
+ { id: 'google', name: 'Google' },
+ { id: 'credentials', name: 'Email and Password' },
+];
+
+const signIn: (provider: AuthProvider) => void | Promise = async (
+ provider,
+) => {
+ const promise = new Promise((resolve) => {
+ setTimeout(() => {
+ console.log(`Sign in with ${provider.id}`);
+ resolve({ error: 'This is a mock error message.' });
+ }, 500);
+ });
+ return promise;
+};
+
+export default function LocaleSignInPage() {
+ return (
+ // preview-start
+
+
+
+ // preview-end
+ );
+}
diff --git a/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.tsx.preview b/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.tsx.preview
new file mode 100644
index 00000000000..49d7fc1ee6f
--- /dev/null
+++ b/docs/data/toolpad/core/components/sign-in-page/LocaleSignInPage.tsx.preview
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/docs/data/toolpad/core/components/sign-in-page/sign-in-page.md b/docs/data/toolpad/core/components/sign-in-page/sign-in-page.md
index 9abf4d7bef6..32fffc5edea 100644
--- a/docs/data/toolpad/core/components/sign-in-page/sign-in-page.md
+++ b/docs/data/toolpad/core/components/sign-in-page/sign-in-page.md
@@ -1,7 +1,7 @@
---
productId: toolpad-core
title: Sign-in Page
-components: SignInPage, Account, NotificationsProvider
+components: SignInPage, Account, NotificationsProvider, LocalizationProvider
---
# Sign-in Page
@@ -14,6 +14,26 @@ If this is your first time using Toolpad Core, it's recommended to read about th
The `SignInPage` component is a quick way to generate a ready-to-use authentication page with multiple OAuth providers, or a credentials form.
+## Basic Usage
+
+```tsx
+import { AppProvider } from '@toolpad/core/AppProvider';
+import { SignInPage } from '@toolpad/core/SignInPage';
+
+export default function App() {
+ return (
+
+ {
+ // Your sign in logic
+ }}
+ />
+
+ );
+}
+```
+
## OAuth
The `SignInPage` component can be set up with an OAuth provider by passing in a list of providers in the `providers` prop, along with a `signIn` function that accepts the `provider` as a parameter.
@@ -264,6 +284,12 @@ You can use the `slotProps` prop to pass props to the underlying components of e
{{"demo": "SlotPropsSignIn.js", "iframe": true, "height": 600 }}
+### Localization
+
+Beyond the [global localization options](/toolpad/core/introduction/base-concepts/#localization) possible, you can customize the labels of the `SignInPage` using the `localeText` prop:
+
+{{"demo": "LocaleSignInPage.js", "iframe": true, "height": 700 }}
+
### 🚧 Layouts
The `SignInPage` component has versions with different layouts for authentication - one column, two column and others such. The APIs of these components are identical. This is in progress.
diff --git a/docs/data/toolpad/core/introduction/base-concepts.md b/docs/data/toolpad/core/introduction/base-concepts.md
index ae008ad48dc..72602c3c2ba 100644
--- a/docs/data/toolpad/core/introduction/base-concepts.md
+++ b/docs/data/toolpad/core/introduction/base-concepts.md
@@ -105,3 +105,124 @@ In this example:
- The `slots` prop allows you to replace entire parts of the component.
- The `slotProps` prop lets you pass additional props to specific slots.
+
+## Localization
+
+Toolpad components support translations between languages. You can modify text and translations inside Toolpad components in several ways.
+
+The default locale is English (United States). To use other locales, follow the instructions below.
+
+### Set translations globally
+
+#### Using the theme
+
+To translate all your Toolpad components, you can provide translations through the theme:
+
+```tsx
+import { createTheme, ThemeProvider } from '@mui/material/styles';
+import hiIN from '@toolpad/core/locales/hiIN';
+
+const theme = createTheme({
+ {
+ palette: {
+ primary: { main: '#1976d2' },
+ },
+ },
+ hiIN,
+});
+
+ // ...
+ {children};
+
+```
+
+#### Using the `localeText` prop
+
+If you want to pass language translations without using `createTheme`, you can directly provide them through the `localeText` prop on the `AppProvider`:
+
+```tsx
+import { AppProvider } from '@toolpad/core/AppProvider';
+import hiIN from '@toolpad/core/locales/hiIN';
+
+function App({ children }) {
+ return (
+
+ {children}
+
+ );
+}
+```
+
+If you are not using the `AppProvider` in your app, you can just use the `LocalizationProvider`:
+
+```tsx
+import { LocalizationProvider } from '@toolpad/core/AppProvider';
+import hiIN from '@toolpad/core/locales/hiIN';
+
+function App({ children }) {
+ return (
+
+ {children}
+
+ );
+}
+```
+
+### Set translations locally
+
+If you want to customize some translations on a specific component, you can use the `localeText` prop exposed by all components.
+
+```tsx
+
+```
+
+### Priority order
+
+The localization system follows a specific priority order when applying translations:
+
+1. `localeText` prop provided directly to a specific component (highest priority)
+2. `localeText` prop provided directly to `AppProvider`
+3. Translations provided through the theme
+4. Default English translations (lowest priority)
+
+:::info
+If you pass a locale text through the `AppProvider` or the theme, and you provide translation keys through the `localeText` prop of a component at the same time, then the latter will override the former to the extent of the keys which it has available:
+
+
+
+```tsx
+
+
+
+```
+
+
+
+This will produce the following result:
+
+- `SignInPage` title with text **Sign In** taken from the `AppProvider` `localeText` prop
+- `Account` with title **Compte** overridden by the `Account` `localeText` prop
+
+:::
+
+### Access localization keys
+
+You can access your localization keys in custom components using the `useLocaleText()` hook.
+
+```tsx
+import { useLocaleText } from '@toolpad/core/AppProvider';
+
+function CustomMenu() {
+ // ...
+ const localeText = useLocaleText();
+ // ...
+}
+```
diff --git a/docs/data/toolpad/core/pagesApi.js b/docs/data/toolpad/core/pagesApi.js
index c36b8e189db..94f6be469bd 100644
--- a/docs/data/toolpad/core/pagesApi.js
+++ b/docs/data/toolpad/core/pagesApi.js
@@ -6,6 +6,7 @@ module.exports = [
{ pathname: '/toolpad/core/api/app-provider' },
{ pathname: '/toolpad/core/api/dashboard-layout' },
{ pathname: '/toolpad/core/api/dialogs-provider' },
+ { pathname: '/toolpad/core/api/localization-provider' },
{ pathname: '/toolpad/core/api/notifications-provider' },
{ pathname: '/toolpad/core/api/page-container' },
{ pathname: '/toolpad/core/api/page-header' },
diff --git a/docs/pages/toolpad/core/api/account.json b/docs/pages/toolpad/core/api/account.json
index b762be1d771..0abec963639 100644
--- a/docs/pages/toolpad/core/api/account.json
+++ b/docs/pages/toolpad/core/api/account.json
@@ -3,7 +3,7 @@
"localeText": {
"type": {
"name": "shape",
- "description": "{ iconButtonAriaLabel?: string, signInLabel?: string, signOutLabel?: string }"
+ "description": "{ accountPreviewIconButtonLabel?: string, accountPreviewTitle?: string, accountSignInLabel?: string, accountSignOutLabel?: string }"
}
},
"slotProps": {
diff --git a/docs/pages/toolpad/core/api/app-provider.json b/docs/pages/toolpad/core/api/app-provider.json
index 6918852f106..a1d5185667b 100644
--- a/docs/pages/toolpad/core/api/app-provider.json
+++ b/docs/pages/toolpad/core/api/app-provider.json
@@ -12,6 +12,12 @@
},
"default": "null"
},
+ "localeText": {
+ "type": {
+ "name": "shape",
+ "description": "{ accountPreviewIconButtonLabel?: string, accountPreviewTitle?: string, accountSignInLabel?: string, accountSignOutLabel?: string, alert?: string, cancel?: string, close?: string, confirm?: string, delete?: string, email?: string, loading?: string, magicLinkSignInTitle?: string, oauthSignInTitle?: string, ok?: string, or?: string, passkey?: string, passkeySignInTitle?: string, password?: string, save?: string, signInRememberMe?: string, signInSubtitle?: string, signInTitle?: string, to?: string, username?: string, with?: string }"
+ }
+ },
"navigation": {
"type": {
"name": "arrayOf",
diff --git a/docs/pages/toolpad/core/api/dashboard-layout.json b/docs/pages/toolpad/core/api/dashboard-layout.json
index 6cb95c85aa3..77c901b54ae 100644
--- a/docs/pages/toolpad/core/api/dashboard-layout.json
+++ b/docs/pages/toolpad/core/api/dashboard-layout.json
@@ -25,7 +25,7 @@
"slotProps": {
"type": {
"name": "shape",
- "description": "{ appTitle?: { branding?: { homeUrl?: string, logo?: node, title?: string } }, sidebarFooter?: { mini: bool }, toolbarAccount?: { localeText?: { iconButtonAriaLabel?: string, signInLabel?: string, signOutLabel?: string }, slotProps?: { popover?: object, popoverContent?: object, preview?: object, signInButton?: object, signOutButton?: object }, slots?: { popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }"
+ "description": "{ appTitle?: { branding?: { homeUrl?: string, logo?: node, title?: string } }, sidebarFooter?: { mini: bool }, toolbarAccount?: { localeText?: { accountPreviewIconButtonLabel?: string, accountPreviewTitle?: string, accountSignInLabel?: string, accountSignOutLabel?: string }, slotProps?: { popover?: object, popoverContent?: object, preview?: object, signInButton?: object, signOutButton?: object }, slots?: { popover?: elementType, popoverContent?: elementType, preview?: elementType, signInButton?: elementType, signOutButton?: elementType } }, toolbarActions?: object }"
},
"default": "{}"
},
diff --git a/docs/pages/toolpad/core/api/localization-provider.js b/docs/pages/toolpad/core/api/localization-provider.js
new file mode 100644
index 00000000000..0c8377e375e
--- /dev/null
+++ b/docs/pages/toolpad/core/api/localization-provider.js
@@ -0,0 +1,23 @@
+import * as React from 'react';
+import ApiPage from 'docs/src/modules/components/ApiPage';
+import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
+import jsonPageContent from './localization-provider.json';
+
+export default function Page(props) {
+ const { descriptions, pageContent } = props;
+ return ;
+}
+
+Page.getInitialProps = () => {
+ const req = require.context(
+ 'docs-toolpad/translations/api-docs/localization-provider',
+ false,
+ /\.\/localization-provider.*.json$/,
+ );
+ const descriptions = mapApiPageTranslations(req);
+
+ return {
+ descriptions,
+ pageContent: jsonPageContent,
+ };
+};
diff --git a/docs/pages/toolpad/core/api/localization-provider.json b/docs/pages/toolpad/core/api/localization-provider.json
new file mode 100644
index 00000000000..fc9e679f9e5
--- /dev/null
+++ b/docs/pages/toolpad/core/api/localization-provider.json
@@ -0,0 +1,18 @@
+{
+ "props": {
+ "localeText": {
+ "type": {
+ "name": "shape",
+ "description": "{ accountPreviewIconButtonLabel?: string, accountPreviewTitle?: string, accountSignInLabel?: string, accountSignOutLabel?: string, alert?: string, cancel?: string, close?: string, confirm?: string, delete?: string, email?: string, loading?: string, magicLinkSignInTitle?: string, oauthSignInTitle?: string, ok?: string, or?: string, passkey?: string, passkeySignInTitle?: string, password?: string, save?: string, signInRememberMe?: string, signInSubtitle?: string, signInTitle?: string, to?: string, username?: string, with?: string }"
+ }
+ }
+ },
+ "name": "LocalizationProvider",
+ "imports": ["import { LocalizationProvider } from '@toolpad/core/AppProvider';"],
+ "classes": [],
+ "muiName": "LocalizationProvider",
+ "filename": "/packages/toolpad-core/src/AppProvider/LocalizationProvider.tsx",
+ "inheritance": null,
+ "demos": "",
+ "cssComponent": false
+}
diff --git a/docs/pages/toolpad/core/api/sign-in-page.json b/docs/pages/toolpad/core/api/sign-in-page.json
index 132fb1607bc..e4b378b3898 100644
--- a/docs/pages/toolpad/core/api/sign-in-page.json
+++ b/docs/pages/toolpad/core/api/sign-in-page.json
@@ -1,5 +1,11 @@
{
"props": {
+ "localeText": {
+ "type": {
+ "name": "shape",
+ "description": "{ email?: string, or?: string, passkey?: string, password?: string, signInRememberMe?: string, signInSubtitle?: string, signInTitle?: string, to?: string, with?: string }"
+ }
+ },
"providers": {
"type": { "name": "arrayOf", "description": "Array<{ id: string, name: string }>" },
"default": "[]"
diff --git a/docs/translations/api-docs/app-provider/app-provider.json b/docs/translations/api-docs/app-provider/app-provider.json
index 7450a9fa118..7e60f68b465 100644
--- a/docs/translations/api-docs/app-provider/app-provider.json
+++ b/docs/translations/api-docs/app-provider/app-provider.json
@@ -4,6 +4,7 @@
"authentication": { "description": "Authentication methods." },
"branding": { "description": "Branding options for the app." },
"children": { "description": "The content of the app provider." },
+ "localeText": { "description": "Locale text for components" },
"navigation": { "description": "Navigation definition for the app." },
"router": { "description": "Router implementation used inside Toolpad components." },
"session": { "description": "Session info about the current user." },
diff --git a/docs/translations/api-docs/localization-provider/localization-provider.json b/docs/translations/api-docs/localization-provider/localization-provider.json
new file mode 100644
index 00000000000..4b8c01172af
--- /dev/null
+++ b/docs/translations/api-docs/localization-provider/localization-provider.json
@@ -0,0 +1,5 @@
+{
+ "componentDescription": "",
+ "propDescriptions": { "localeText": { "description": "Locale for components texts" } },
+ "classDescriptions": {}
+}
diff --git a/docs/translations/api-docs/sign-in-page/sign-in-page.json b/docs/translations/api-docs/sign-in-page/sign-in-page.json
index de36472fb55..d72225d77de 100644
--- a/docs/translations/api-docs/sign-in-page/sign-in-page.json
+++ b/docs/translations/api-docs/sign-in-page/sign-in-page.json
@@ -1,6 +1,7 @@
{
"componentDescription": "",
"propDescriptions": {
+ "localeText": { "description": "The labels for the account component." },
"providers": { "description": "The list of authentication providers to display." },
"signIn": {
"description": "Callback fired when a user signs in.",
diff --git a/packages/toolpad-core/src/Account/Account.tsx b/packages/toolpad-core/src/Account/Account.tsx
index c63be63b1ff..3f09101dba2 100644
--- a/packages/toolpad-core/src/Account/Account.tsx
+++ b/packages/toolpad-core/src/Account/Account.tsx
@@ -10,7 +10,16 @@ import { AccountPreview, AccountPreviewProps } from './AccountPreview';
import { AccountPopoverHeader } from './AccountPopoverHeader';
import { AccountPopoverFooter } from './AccountPopoverFooter';
import { SessionContext, AuthenticationContext } from '../AppProvider/AppProvider';
-import { LocaleProvider, useLocaleText } from '../shared/locales/LocaleContext';
+import { useLocaleText, type LocaleText } from '../AppProvider/LocalizationProvider';
+import { AccountLocaleContext } from './AccountLocaleContext';
+
+interface AccountLocaleText {
+ accountSignInLabel: string;
+ accountSignOutLabel: string;
+
+ accountPreviewIconButtonLabel: string;
+ accountPreviewTitle: string;
+}
export interface AccountSlots {
/**
@@ -58,9 +67,16 @@ export interface AccountProps {
/**
* The labels for the account component.
*/
- localeText?: Partial>;
+ localeText?: Partial;
}
+const defaultAccountLocaleText: Pick = {
+ accountPreviewIconButtonLabel: 'Current User',
+ accountPreviewTitle: 'Account',
+ accountSignInLabel: 'Sign in',
+ accountSignOutLabel: 'Sign out',
+};
+
/**
*
* Demos:
@@ -74,7 +90,12 @@ export interface AccountProps {
* - [Account API](https://mui.com/toolpad/core/api/account)
*/
function Account(props: AccountProps) {
- const { localeText } = props;
+ const { localeText: propsLocaleText } = props;
+ const globalLocaleText = useLocaleText();
+ const localeText = React.useMemo(
+ () => ({ ...defaultAccountLocaleText, ...globalLocaleText, ...propsLocaleText }),
+ [globalLocaleText, propsLocaleText],
+ );
const { slots, slotProps } = props;
const [anchorEl, setAnchorEl] = React.useState(null);
const session = React.useContext(SessionContext);
@@ -93,88 +114,92 @@ function Account(props: AccountProps) {
return null;
}
+ let accountContent = null;
+
if (!session?.user) {
- return (
-
- {slots?.signInButton ? (
-
+ accountContent = slots?.signInButton ? (
+
+ ) : (
+
+ );
+ } else {
+ accountContent = (
+
+ {slots?.preview ? (
+
) : (
-
+
)}
-
+ {slots?.popover ? (
+
+ ) : (
+
+ )}
+
);
}
return (
-
- {slots?.preview ? (
-
- ) : (
-
- )}
- {slots?.popover ? (
-
- ) : (
-
- )}
-
+
+ {accountContent}
+
);
}
@@ -187,9 +212,10 @@ Account.propTypes /* remove-proptypes */ = {
* The labels for the account component.
*/
localeText: PropTypes.shape({
- iconButtonAriaLabel: PropTypes.string,
- signInLabel: PropTypes.string,
- signOutLabel: PropTypes.string,
+ accountPreviewIconButtonLabel: PropTypes.string,
+ accountPreviewTitle: PropTypes.string,
+ accountSignInLabel: PropTypes.string,
+ accountSignOutLabel: PropTypes.string,
}),
/**
* The props used for each slot inside.
diff --git a/packages/toolpad-core/src/Account/AccountLocaleContext.tsx b/packages/toolpad-core/src/Account/AccountLocaleContext.tsx
new file mode 100644
index 00000000000..4479c4b2b66
--- /dev/null
+++ b/packages/toolpad-core/src/Account/AccountLocaleContext.tsx
@@ -0,0 +1,7 @@
+import * as React from 'react';
+import type { LocaleText } from '../AppProvider/LocalizationProvider';
+
+/**
+ * @ignore - internal component.
+ */
+export const AccountLocaleContext = React.createContext | null>(null);
diff --git a/packages/toolpad-core/src/Account/AccountPreview.tsx b/packages/toolpad-core/src/Account/AccountPreview.tsx
index 0875eca70f4..a5f41386680 100644
--- a/packages/toolpad-core/src/Account/AccountPreview.tsx
+++ b/packages/toolpad-core/src/Account/AccountPreview.tsx
@@ -8,7 +8,8 @@ import Stack from '@mui/material/Stack';
import IconButton, { IconButtonProps } from '@mui/material/IconButton';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import { SessionContext } from '../AppProvider';
-import { useLocaleText } from '../shared/locales/LocaleContext';
+import { useLocaleText } from '../AppProvider/LocalizationProvider';
+import { AccountLocaleContext } from './AccountLocaleContext';
export type AccountPreviewVariant = 'condensed' | 'expanded';
@@ -79,7 +80,9 @@ export interface AccountPreviewProps {
function AccountPreview(props: AccountPreviewProps) {
const { slots, variant = 'condensed', slotProps, open, handleClick, sx } = props;
const session = React.useContext(SessionContext);
- const localeText = useLocaleText();
+ const globalLocaleText = useLocaleText();
+ const accountLocaleText = React.useContext(AccountLocaleContext);
+ const localeText = { ...globalLocaleText, ...accountLocaleText };
if (!session || !session.user) {
return null;
@@ -128,14 +131,14 @@ function AccountPreview(props: AccountPreviewProps) {
}
return (
-
+
{slots?.avatarIconButton ? (
) : (
- {localeText?.signInLabel || 'Sign In'}
+ {localeText?.accountSignInLabel}
);
}
diff --git a/packages/toolpad-core/src/Account/SignOutButton.tsx b/packages/toolpad-core/src/Account/SignOutButton.tsx
index e9d8e1701b8..45add1b6fec 100644
--- a/packages/toolpad-core/src/Account/SignOutButton.tsx
+++ b/packages/toolpad-core/src/Account/SignOutButton.tsx
@@ -2,8 +2,9 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import Button, { ButtonProps } from '@mui/material/Button';
import LogoutIcon from '@mui/icons-material/Logout';
-import { AuthenticationContext } from '../AppProvider/AppProvider';
-import { useLocaleText } from '../shared/locales/LocaleContext';
+import { AuthenticationContext } from '../AppProvider';
+import { useLocaleText } from '../AppProvider/LocalizationProvider';
+import { AccountLocaleContext } from './AccountLocaleContext';
export type SignOutButtonProps = ButtonProps;
@@ -19,7 +20,9 @@ export type SignOutButtonProps = ButtonProps;
*/
function SignOutButton(props: SignOutButtonProps) {
const authentication = React.useContext(AuthenticationContext);
- const localeText = useLocaleText();
+ const globalLocaleText = useLocaleText();
+ const accountLocaleText = React.useContext(AccountLocaleContext);
+ const localeText = { ...globalLocaleText, ...accountLocaleText };
return (
}
{...props}
>
- {localeText.signOutLabel}
+ {localeText?.accountSignOutLabel}
);
}
diff --git a/packages/toolpad-core/src/AppProvider/AppProvider.tsx b/packages/toolpad-core/src/AppProvider/AppProvider.tsx
index 0de8c74eec7..da4be530390 100644
--- a/packages/toolpad-core/src/AppProvider/AppProvider.tsx
+++ b/packages/toolpad-core/src/AppProvider/AppProvider.tsx
@@ -11,6 +11,7 @@ import {
WindowContext,
} from '../shared/context';
import { AppThemeProvider } from './AppThemeProvider';
+import { LocalizationProvider, type LocaleText } from './LocalizationProvider';
export interface NavigateOptions {
history?: 'auto' | 'push' | 'replace';
@@ -103,6 +104,10 @@ export interface AppProviderProps {
* @default null
*/
router?: Router;
+ /**
+ * Locale text for components
+ */
+ localeText?: Partial;
/**
* Session info about the current user.
* @default null
@@ -147,6 +152,7 @@ function AppProvider(props: AppProviderProps) {
theme = createTheme(),
branding = null,
navigation = [],
+ localeText,
router = null,
authentication = null,
session = null,
@@ -159,15 +165,17 @@ function AppProvider(props: AppProviderProps) {
-
-
-
-
- {children}
-
-
-
-
+
+
+
+
+
+ {children}
+
+
+
+
+
@@ -202,6 +210,36 @@ AppProvider.propTypes /* remove-proptypes */ = {
* The content of the app provider.
*/
children: PropTypes.node,
+ /**
+ * Locale text for components
+ */
+ localeText: PropTypes.shape({
+ accountPreviewIconButtonLabel: PropTypes.string,
+ accountPreviewTitle: PropTypes.string,
+ accountSignInLabel: PropTypes.string,
+ accountSignOutLabel: PropTypes.string,
+ alert: PropTypes.string,
+ cancel: PropTypes.string,
+ close: PropTypes.string,
+ confirm: PropTypes.string,
+ delete: PropTypes.string,
+ email: PropTypes.string,
+ loading: PropTypes.string,
+ magicLinkSignInTitle: PropTypes.string,
+ oauthSignInTitle: PropTypes.string,
+ ok: PropTypes.string,
+ or: PropTypes.string,
+ passkey: PropTypes.string,
+ passkeySignInTitle: PropTypes.string,
+ password: PropTypes.string,
+ save: PropTypes.string,
+ signInRememberMe: PropTypes.string,
+ signInSubtitle: PropTypes.string,
+ signInTitle: PropTypes.string,
+ to: PropTypes.string,
+ username: PropTypes.string,
+ with: PropTypes.string,
+ }),
/**
* Navigation definition for the app.
* @default []
diff --git a/packages/toolpad-core/src/AppProvider/LocalizationProvider.tsx b/packages/toolpad-core/src/AppProvider/LocalizationProvider.tsx
new file mode 100644
index 00000000000..dc05312cae3
--- /dev/null
+++ b/packages/toolpad-core/src/AppProvider/LocalizationProvider.tsx
@@ -0,0 +1,132 @@
+'use client';
+import * as React from 'react';
+import PropTypes from 'prop-types';
+import { useTheme } from '@mui/material/styles';
+import DEFAULT_LOCALE from '../locales/en';
+
+export interface LocaleText {
+ // Account
+ accountSignInLabel: string;
+ accountSignOutLabel: string;
+
+ // AccountPreview
+ accountPreviewIconButtonLabel: string;
+ accountPreviewTitle: string;
+
+ // SignInPage
+ signInTitle: string;
+ signInSubtitle: string;
+ oauthSignInTitle: string;
+ passkeySignInTitle: string;
+ magicLinkSignInTitle: string;
+ signInRememberMe: string;
+
+ // Common authentication labels
+ email: string;
+ passkey: string;
+ username: string;
+ password: string;
+
+ // Common action labels
+ or: string;
+ to: string;
+ with: string;
+ save: string;
+ cancel: string;
+ ok: string;
+ close: string;
+ delete: string;
+ alert: string;
+ confirm: string;
+ loading: string;
+}
+
+export interface LocalizationProviderProps {
+ children?: React.ReactNode;
+ /**
+ * Locale for components texts
+ */
+ localeText?: Partial;
+}
+
+export const LocalizationContext = React.createContext>({});
+
+const LocalizationProvider = function LocalizationProvider(props: LocalizationProviderProps) {
+ const { localeText: propsLocaleText, children } = props;
+
+ const theme = useTheme();
+ // @ts-ignore
+ const themeLocaleText = theme?.components?.MuiLocalizationProvider?.defaultProps?.localeText;
+
+ const defaultLocaleText =
+ DEFAULT_LOCALE.components.MuiLocalizationProvider.defaultProps.localeText;
+
+ /* The order of overrides is:
+ * 1. The `localeText` prop of the `AppProvider` supersedes
+ * 2. The localeText provided as an argument to the `createTheme` function, which supersedes
+ * 3. The default locale text
+ */
+
+ const localeText = React.useMemo(
+ () => ({ ...defaultLocaleText, ...themeLocaleText, ...propsLocaleText }),
+ [defaultLocaleText, themeLocaleText, propsLocaleText],
+ );
+
+ return {children};
+};
+
+LocalizationProvider.propTypes /* remove-proptypes */ = {
+ // ┌────────────────────────────── Warning ──────────────────────────────┐
+ // │ These PropTypes are generated from the TypeScript type definitions. │
+ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
+ // └─────────────────────────────────────────────────────────────────────┘
+ /**
+ * @ignore
+ */
+ children: PropTypes.node,
+ /**
+ * Locale for components texts
+ */
+ localeText: PropTypes.shape({
+ accountPreviewIconButtonLabel: PropTypes.string,
+ accountPreviewTitle: PropTypes.string,
+ accountSignInLabel: PropTypes.string,
+ accountSignOutLabel: PropTypes.string,
+ alert: PropTypes.string,
+ cancel: PropTypes.string,
+ close: PropTypes.string,
+ confirm: PropTypes.string,
+ delete: PropTypes.string,
+ email: PropTypes.string,
+ loading: PropTypes.string,
+ magicLinkSignInTitle: PropTypes.string,
+ oauthSignInTitle: PropTypes.string,
+ ok: PropTypes.string,
+ or: PropTypes.string,
+ passkey: PropTypes.string,
+ passkeySignInTitle: PropTypes.string,
+ password: PropTypes.string,
+ save: PropTypes.string,
+ signInRememberMe: PropTypes.string,
+ signInSubtitle: PropTypes.string,
+ signInTitle: PropTypes.string,
+ to: PropTypes.string,
+ username: PropTypes.string,
+ with: PropTypes.string,
+ }),
+} as any;
+
+export { LocalizationProvider };
+/**
+ *
+ * Demos:
+ *
+ * - [Sign-in Page](https://mui.com/toolpad/core/react-sign-in-page/)
+ *
+ * API:
+ *
+ * - [LocalizationProvider API](https://mui.com/toolpad/core/api/localization-provider)
+ */
+export function useLocaleText() {
+ return React.useContext(LocalizationContext);
+}
diff --git a/packages/toolpad-core/src/AppProvider/index.ts b/packages/toolpad-core/src/AppProvider/index.ts
index bd2c0cdccb2..007fe400d13 100644
--- a/packages/toolpad-core/src/AppProvider/index.ts
+++ b/packages/toolpad-core/src/AppProvider/index.ts
@@ -1 +1,2 @@
export * from './AppProvider';
+export * from './LocalizationProvider';
diff --git a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
index 9146f9a53b0..8661491a1c8 100644
--- a/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
+++ b/packages/toolpad-core/src/DashboardLayout/DashboardLayout.tsx
@@ -546,9 +546,10 @@ DashboardLayout.propTypes /* remove-proptypes */ = {
}),
toolbarAccount: PropTypes.shape({
localeText: PropTypes.shape({
- iconButtonAriaLabel: PropTypes.string,
- signInLabel: PropTypes.string,
- signOutLabel: PropTypes.string,
+ accountPreviewIconButtonLabel: PropTypes.string,
+ accountPreviewTitle: PropTypes.string,
+ accountSignInLabel: PropTypes.string,
+ accountSignOutLabel: PropTypes.string,
}),
slotProps: PropTypes.shape({
popover: PropTypes.object,
diff --git a/packages/toolpad-core/src/SignInPage/SignInPage.tsx b/packages/toolpad-core/src/SignInPage/SignInPage.tsx
index 5a5c489d992..ea865d530e9 100644
--- a/packages/toolpad-core/src/SignInPage/SignInPage.tsx
+++ b/packages/toolpad-core/src/SignInPage/SignInPage.tsx
@@ -37,6 +37,7 @@ import KeycloakIcon from './icons/Keycloak';
import OktaIcon from './icons/Okta';
import FusionAuthIcon from './icons/FusionAuth';
import { BrandingContext, RouterContext } from '../shared/context';
+import { useLocaleText, type LocaleText } from '../AppProvider/LocalizationProvider';
const mergeSlotSx = (defaultSx: SxProps, slotProps?: { sx?: SxProps }) => {
if (Array.isArray(slotProps?.sx)) {
@@ -135,6 +136,18 @@ const IconProviderMap = new Map([
['fusionauth', ],
]);
+interface SignInPageLocaleText {
+ signInTitle: string;
+ signInSubtitle: string;
+ signInRememberMe: string;
+ email: string;
+ password: string;
+ or: string;
+ with: string;
+ passkey: string;
+ to: string;
+}
+
export interface AuthProvider {
/**
* The unique identifier of the authentication provider.
@@ -260,8 +273,24 @@ export interface SignInPageProps {
* The prop used to customize the styles on the `SignInPage` container
*/
sx?: SxProps;
+ /**
+ * The labels for the account component.
+ */
+ localeText?: Partial;
}
+const defaultLocaleText: Pick = {
+ signInTitle: 'Sign in',
+ signInSubtitle: 'Please sign in to continue',
+ signInRememberMe: 'Remember me',
+ email: 'Email',
+ password: 'Password',
+ or: 'or',
+ with: 'with',
+ passkey: 'Passkey',
+ to: 'to',
+};
+
/**
*
* Demos:
@@ -273,10 +302,13 @@ export interface SignInPageProps {
* - [SignInPage API](https://mui.com/toolpad/core/api/sign-in-page)
*/
function SignInPage(props: SignInPageProps) {
- const { providers, signIn, slots, slotProps, sx } = props;
+ const { providers, signIn, slots, slotProps, sx, localeText: propsLocaleText } = props;
const theme = useTheme();
const branding = React.useContext(BrandingContext);
const router = React.useContext(RouterContext);
+ const globalLocaleText = useLocaleText();
+ const localeText = { ...defaultLocaleText, ...globalLocaleText, ...propsLocaleText };
+
const passkeyProvider = providers?.find((provider) => provider.id === 'passkey');
const credentialsProvider = providers?.find((provider) => provider.id === 'credentials');
const emailProvider = providers?.find((provider) => provider.id === 'nodemailer');
@@ -339,14 +371,15 @@ function SignInPage(props: SignInPageProps) {
fontWeight: 600,
}}
>
- Sign in {branding?.title ? `to ${branding.title}` : null}
+ {localeText.signInTitle}{' '}
+ {branding?.title ? `${localeText.to?.toLocaleLowerCase()} ${branding.title}` : null}
)}
{slots?.subtitle ? (
) : (
- Welcome, please sign in to continue
+ {localeText?.signInSubtitle}
)}
@@ -391,7 +424,11 @@ function SignInPage(props: SignInPageProps) {
textTransform: 'capitalize',
}}
>
- Sign in with {provider.name}
+
+ {localeText.oauthSignInTitle ||
+ `${localeText.signInTitle} ${localeText.with}`}{' '}
+ {provider.name}
+
);
@@ -400,7 +437,9 @@ function SignInPage(props: SignInPageProps) {
{passkeyProvider ? (
- {singleProvider ? null : or}
+ {singleProvider ? null : (
+ {localeText.or}
+ )}
{error && selectedProviderId === 'passkey' ? (
{error}
@@ -429,7 +468,7 @@ function SignInPage(props: SignInPageProps) {
) : (
- Sign in with {passkeyProvider.name || 'Passkey'}
+ {localeText.passkeySignInTitle ||
+ `${localeText.signInTitle} ${localeText.with}`}{' '}
+ {passkeyProvider.name || localeText.passkey}
)}
@@ -468,7 +509,9 @@ function SignInPage(props: SignInPageProps) {
{emailProvider ? (
- {singleProvider ? null : or}
+ {singleProvider ? null : (
+ {localeText.or}
+ )}
{error && selectedProviderId === 'nodemailer' ? (
{error}
@@ -503,7 +546,7 @@ function SignInPage(props: SignInPageProps) {
) : (
- Sign in with Email
+ {localeText.magicLinkSignInTitle ||
+ `${localeText.signInTitle} ${localeText.with}`}{' '}
+ {localeText.email}
)}
@@ -542,7 +587,9 @@ function SignInPage(props: SignInPageProps) {
{credentialsProvider ? (
- {singleProvider ? null : or}
+ {singleProvider ? null : (
+ {localeText.or}
+ )}
{error && selectedProviderId === 'credentials' ? (
{error}
@@ -576,7 +623,7 @@ function SignInPage(props: SignInPageProps) {
) : (
}
- label="Remember me"
+ label={localeText.signInRememberMe}
{...slotProps?.rememberMe}
slotProps={{
typography: {
@@ -657,7 +704,7 @@ function SignInPage(props: SignInPageProps) {
}}
{...slotProps?.submitButton}
>
- Sign in
+ {localeText.signInTitle}
)}
@@ -681,6 +728,20 @@ SignInPage.propTypes /* remove-proptypes */ = {
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
+ /**
+ * The labels for the account component.
+ */
+ localeText: PropTypes.shape({
+ email: PropTypes.string,
+ or: PropTypes.string,
+ passkey: PropTypes.string,
+ password: PropTypes.string,
+ signInRememberMe: PropTypes.string,
+ signInSubtitle: PropTypes.string,
+ signInTitle: PropTypes.string,
+ to: PropTypes.string,
+ with: PropTypes.string,
+ }),
/**
* The list of authentication providers to display.
* @default []
diff --git a/packages/toolpad-core/src/locales/en.tsx b/packages/toolpad-core/src/locales/en.tsx
new file mode 100644
index 00000000000..0f55d82cd64
--- /dev/null
+++ b/packages/toolpad-core/src/locales/en.tsx
@@ -0,0 +1,41 @@
+import type { LocaleText } from '../AppProvider';
+import { getLocalization } from './getLocalization';
+
+const en: LocaleText = {
+ // Account
+ accountSignInLabel: 'Sign In',
+ accountSignOutLabel: 'Sign Out',
+
+ // AccountPreview
+ accountPreviewTitle: 'Account',
+ accountPreviewIconButtonLabel: 'Current User',
+
+ // SignInPage
+ signInTitle: 'Sign In',
+ signInSubtitle: 'Welcome user, please sign in to continue',
+ signInRememberMe: 'Remember Me',
+ oauthSignInTitle: 'Sign in with OAuth',
+ passkeySignInTitle: 'Sign in with Passkey',
+ magicLinkSignInTitle: 'Sign in with Magic Link',
+
+ // Common authentication labels
+ email: 'Email',
+ password: 'Password',
+ username: 'Username',
+ passkey: 'Passkey',
+
+ // Common action labels
+ save: 'Save',
+ cancel: 'Cancel',
+ ok: 'Ok',
+ or: 'Or',
+ to: 'To',
+ with: 'With',
+ close: 'Close',
+ delete: 'Delete',
+ alert: 'Alert',
+ confirm: 'Confirm',
+ loading: 'Loading...',
+};
+
+export default getLocalization(en);
diff --git a/packages/toolpad-core/src/locales/getLocalization.ts b/packages/toolpad-core/src/locales/getLocalization.ts
new file mode 100644
index 00000000000..a99a3b5d418
--- /dev/null
+++ b/packages/toolpad-core/src/locales/getLocalization.ts
@@ -0,0 +1,13 @@
+import type { LocaleText } from '../AppProvider';
+
+export const getLocalization = (translations: Partial) => {
+ return {
+ components: {
+ MuiLocalizationProvider: {
+ defaultProps: {
+ localeText: { ...translations },
+ },
+ },
+ },
+ };
+};
diff --git a/packages/toolpad-core/src/locales/hiIN.tsx b/packages/toolpad-core/src/locales/hiIN.tsx
new file mode 100644
index 00000000000..16175c45125
--- /dev/null
+++ b/packages/toolpad-core/src/locales/hiIN.tsx
@@ -0,0 +1,41 @@
+import type { LocaleText } from '../AppProvider';
+import { getLocalization } from './getLocalization';
+
+const hiINLabels: Partial = {
+ // Account
+ accountSignInLabel: 'साइन इन करें',
+ accountSignOutLabel: 'साइन आउट करें',
+
+ // AccountPreview
+ accountPreviewTitle: 'खाता',
+ accountPreviewIconButtonLabel: 'वर्तमान उपयोगकर्ता',
+
+ // SignInPage
+ signInTitle: 'साइन इन करें',
+ signInSubtitle: 'स्वागत है उपयोगकर्ता, कृपया जारी रखने के लिए साइन इन करें',
+ signInRememberMe: 'मुझे याद रखें',
+ oauthSignInTitle: 'साइन इन विकल्प',
+ passkeySignInTitle: 'साइन इन विकल्प',
+ magicLinkSignInTitle: 'साइन इन विकल्प',
+
+ // Common authentication labels
+ email: 'ईमेल',
+ password: 'पासवर्ड',
+ username: 'उपयोगकर्ता नाम',
+ passkey: 'पासकी',
+
+ // Common action labels
+ save: 'सहेजें',
+ cancel: 'रद्द करें',
+ ok: 'ठीक है',
+ or: 'या',
+ to: 'को',
+ with: 'के साथ',
+ close: 'बंद करें',
+ delete: 'हटाएं',
+ alert: 'सूचना',
+ confirm: 'पुष्टि करें',
+ loading: 'लोड हो रहा है...',
+};
+
+export default getLocalization(hiINLabels);
diff --git a/packages/toolpad-core/src/shared/locales/LocaleContext.tsx b/packages/toolpad-core/src/shared/locales/LocaleContext.tsx
deleted file mode 100644
index abfb38d6d5d..00000000000
--- a/packages/toolpad-core/src/shared/locales/LocaleContext.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-'use client';
-
-import * as React from 'react';
-import DEFAULT_LOCALE_TEXT from './en';
-
-export type LocaleContextType = {
- // Account
- signInLabel?: string;
- signOutLabel?: string;
- // Account Preview
- iconButtonAriaLabel?: string;
-};
-
-export const LocaleContext = React.createContext(DEFAULT_LOCALE_TEXT);
-
-export interface LocaleProviderProps {
- localeText?: Partial;
- children: React.ReactNode;
-}
-
-/**
- * @ignore - internal component.
- */
-export function LocaleProvider({ localeText, children }: LocaleProviderProps) {
- const mergedLocaleText = React.useMemo(
- () => ({ ...DEFAULT_LOCALE_TEXT, ...localeText }),
- [localeText],
- );
-
- return {children};
-}
-
-/**
- * @ignore - internal hook.
- */
-
-export function useLocaleText() {
- return React.useContext(LocaleContext);
-}
diff --git a/packages/toolpad-core/src/shared/locales/en.tsx b/packages/toolpad-core/src/shared/locales/en.tsx
deleted file mode 100644
index 9717c8b3a3c..00000000000
--- a/packages/toolpad-core/src/shared/locales/en.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-const TOOLPAD_CORE_DEFAULT_LOCALE_TEXT = {
- // Account
- signInLabel: 'Sign In',
- signOutLabel: 'Sign Out',
- // Account Preview
- iconButtonAriaLabel: 'Current User',
-};
-
-export default TOOLPAD_CORE_DEFAULT_LOCALE_TEXT;
diff --git a/packages/toolpad-core/src/useDialogs/useDialogs.tsx b/packages/toolpad-core/src/useDialogs/useDialogs.tsx
index 192b20aac0a..f84417c42c0 100644
--- a/packages/toolpad-core/src/useDialogs/useDialogs.tsx
+++ b/packages/toolpad-core/src/useDialogs/useDialogs.tsx
@@ -11,6 +11,21 @@ import { useNonNullableContext } from '@toolpad/utils/react';
import invariant from 'invariant';
import * as React from 'react';
import { DialogsContext } from './DialogsContext';
+import { useLocaleText, type LocaleText } from '../AppProvider/LocalizationProvider';
+
+interface DialogsProviderLocaleText {
+ alert: string;
+ confirm: string;
+ cancel: string;
+ ok: string;
+}
+
+const defaultLocaleText: Pick = {
+ alert: 'Alert',
+ confirm: 'Confirm',
+ cancel: 'Cancel',
+ ok: 'Ok',
+};
export interface OpenDialogOptions {
/**
@@ -192,14 +207,16 @@ export interface AlertDialogPayload extends AlertOptions {
export interface AlertDialogProps extends DialogProps {}
export function AlertDialog({ open, payload, onClose }: AlertDialogProps) {
+ const globalLocaleText = useLocaleText();
+ const localeText = { ...defaultLocaleText, ...globalLocaleText };
const okButtonProps = useDialogLoadingButton(() => onClose());
return (
@@ -213,18 +230,20 @@ export interface ConfirmDialogPayload extends ConfirmOptions {
export interface ConfirmDialogProps extends DialogProps {}
export function ConfirmDialog({ open, payload, onClose }: ConfirmDialogProps) {
+ const globalLocaleText = useLocaleText();
+ const localeText = { ...defaultLocaleText, ...globalLocaleText };
const cancelButtonProps = useDialogLoadingButton(() => onClose(false));
const okButtonProps = useDialogLoadingButton(() => onClose(true));
return (
@@ -238,6 +257,8 @@ export interface PromptDialogPayload extends PromptOptions {
export interface PromptDialogProps extends DialogProps {}
export function PromptDialog({ open, payload, onClose }: PromptDialogProps) {
+ const globalLocaleText = useLocaleText();
+ const localeText = { ...defaultLocaleText, ...globalLocaleText };
const [input, setInput] = React.useState('');
const cancelButtonProps = useDialogLoadingButton(() => onClose(null));
@@ -266,7 +287,7 @@ export function PromptDialog({ open, payload, onClose }: PromptDialogProps) {
},
}}
>
- {payload.title ?? 'Confirm'}
+ {payload.title ?? localeText.confirm}
{payload.msg}
- {payload.cancelText ?? 'Cancel'}
+ {payload.cancelText ?? localeText.cancel}
- {payload.okText ?? 'Ok'}
+ {payload.okText ?? localeText.ok}
diff --git a/packages/toolpad-core/src/useNotifications/NotificationsProvider.tsx b/packages/toolpad-core/src/useNotifications/NotificationsProvider.tsx
index 8a7ab8cabb2..12a35999c42 100644
--- a/packages/toolpad-core/src/useNotifications/NotificationsProvider.tsx
+++ b/packages/toolpad-core/src/useNotifications/NotificationsProvider.tsx
@@ -19,8 +19,7 @@ import type {
ShowNotification,
ShowNotificationOptions,
} from './useNotifications';
-
-const closeText = 'Close';
+import { useLocaleText, type LocaleText } from '../AppProvider/LocalizationProvider';
export interface NotificationsProviderSlotProps {
snackbar: SnackbarProps;
@@ -36,6 +35,14 @@ export interface NotificationsProviderSlots {
const RootPropsContext = React.createContext(null);
+interface NotificationsProviderLocaleText {
+ close: string;
+}
+
+const defaultLocaleText: Pick = {
+ close: 'Close',
+};
+
interface NotificationProps {
notificationKey: string;
badge: string | null;
@@ -45,6 +52,8 @@ interface NotificationProps {
}
function Notification({ notificationKey, open, message, options, badge }: NotificationProps) {
+ const globalLocaleText = useLocaleText();
+ const localeText = { ...defaultLocaleText, ...globalLocaleText };
const { close } = useNonNullableContext(NotificationsContext);
const { severity, actionText, onAction, autoHideDuration } = options;
@@ -68,8 +77,8 @@ function Notification({ notificationKey, open, message, options, badge }: Notifi
) : null}