diff --git a/UPGRADE.md b/UPGRADE.md index 7e0ea32f35b..d1a3da40949 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1085,7 +1085,7 @@ If you had custom reducer or sagas based on these actions, they will no longer w ## i18nProvider Signature Changed -The i18nProvider, that react-admin uses for translating UI and content, must now be an object exposing two methods: `trasnlate` and `changeLocale`. +The i18nProvider, that react-admin uses for translating UI and content, must now be an object exposing three methods: `translate`, `changeLocale` and `getLocale`. ```jsx // react-admin 2.x @@ -1094,18 +1094,25 @@ const i18nProvider = (locale) => messages[locale]; // react-admin 3.x const polyglot = new Polyglot({ locale: 'en', phrases: messages.en }); let translate = polyglot.t.bind(polyglot); +let locale = 'en'; const i18nProvider = { translate: (key, options) => translate(key, options), changeLocale: newLocale => { + locale = newLocale; return new Promise((resolve, reject) => { // load new messages and update the translate function }) + }, + getLocale: () => { + return locale; } } ``` But don't worry: react-admin v3 contains a module called `ra-i18n-polyglot`, that is a wrapper around your old `i18nProvider` to make it compatible with the new provider signature: +Besides, the `Admin` component does not accept a `locale` prop anymore as it is the `i18nProvider` provider responsibility: + ```diff import React from 'react'; import { Admin, Resource } from 'react-admin'; @@ -1118,10 +1125,11 @@ const messages = { en: englishMessages, }; -const i18nProvider = locale => messages[locale]; -+const i18nProvider = polyglotI18nProvider(locale => messages[locale]); ++const i18nProvider = polyglotI18nProvider(locale => messages[locale], 'fr); const App = () => ( - +- + ... ); @@ -293,10 +299,10 @@ const i18nProvider = polyglotI18nProvider(locale => { if (locale === 'fr') { return import('../i18n/fr.js').then(messages => messages.default); } -}); +}, 'en'); const App = () => ( - + ... ); @@ -304,7 +310,7 @@ const App = () => ( ## Using The Browser Locale -React-admin provides a helper function named `resolveBrowserLocale()`, which detects the user's browser locale. To use it, simply pass the function as `locale` prop. +React-admin provides a helper function named `resolveBrowserLocale()`, which detects the user's browser locale. To use it, simply pass the function as the `initialLocale` argument of `polyglotI18nProvider`. ```jsx import React from 'react'; @@ -321,10 +327,13 @@ const messages = { fr: frenchMessages, en: englishMessages, }; -const i18nProvider = polyglotI18nProvider(locale => messages[locale] ? messages[locale] : messages.en); +const i18nProvider = polyglotI18nProvider( + locale => messages[locale] ? messages[locale] : messages.en, + resolveBrowserLocale() +); const App = () => ( - + ... ); diff --git a/packages/ra-core/src/CoreAdmin.tsx b/packages/ra-core/src/CoreAdmin.tsx index 08705397499..bdebcbb06b8 100644 --- a/packages/ra-core/src/CoreAdmin.tsx +++ b/packages/ra-core/src/CoreAdmin.tsx @@ -97,6 +97,11 @@ const CoreAdmin: FunctionComponent = ({ 'You are using deprecated prop "appLayout", it was replaced by "layout", see https://github.com/marmelab/react-admin/issues/2918' ); } + if (locale) { + console.warn( + 'You are using deprecated prop "locale". You must now pass the initial locale to your i18nProvider' + ); + } if (loginPage === true && process.env.NODE_ENV !== 'production') { console.warn( 'You passed true to the loginPage prop. You must either pass false to disable it or a component class to customize it' @@ -105,10 +110,7 @@ const CoreAdmin: FunctionComponent = ({ return ( - + {loginPage !== false && loginPage !== true ? ( diff --git a/packages/ra-core/src/i18n/TestTranslationProvider.tsx b/packages/ra-core/src/i18n/TestTranslationProvider.tsx index a975ba05492..9a9e6d41459 100644 --- a/packages/ra-core/src/i18n/TestTranslationProvider.tsx +++ b/packages/ra-core/src/i18n/TestTranslationProvider.tsx @@ -16,6 +16,7 @@ export default ({ translate, messages, children }: any) => ( : options._ : translate, changeLocale: () => Promise.resolve(), + getLocale: () => 'en', }, }} > diff --git a/packages/ra-core/src/i18n/TranslationContext.ts b/packages/ra-core/src/i18n/TranslationContext.ts index ef9175b06fc..727d897a142 100644 --- a/packages/ra-core/src/i18n/TranslationContext.ts +++ b/packages/ra-core/src/i18n/TranslationContext.ts @@ -13,6 +13,7 @@ const TranslationContext = createContext({ i18nProvider: { translate: x => x, changeLocale: () => Promise.resolve(), + getLocale: () => 'en', }, }); diff --git a/packages/ra-core/src/i18n/TranslationProvider.tsx b/packages/ra-core/src/i18n/TranslationProvider.tsx index 862f98ce7a3..0bc9227ae41 100644 --- a/packages/ra-core/src/i18n/TranslationProvider.tsx +++ b/packages/ra-core/src/i18n/TranslationProvider.tsx @@ -22,17 +22,17 @@ interface Props { * @example * const MyApp = () => ( * - * + * * * * * ); */ const TranslationProvider: FunctionComponent = props => { - const { i18nProvider, children, locale = 'en' } = props; + const { i18nProvider, children } = props; const [state, setState] = useSafeSetState({ - locale, + locale: i18nProvider ? i18nProvider.getLocale() : 'en', i18nProvider, }); diff --git a/packages/ra-core/src/i18n/useTranslate.spec.tsx b/packages/ra-core/src/i18n/useTranslate.spec.tsx index 36b3a8ba76d..3710c0cf021 100644 --- a/packages/ra-core/src/i18n/useTranslate.spec.tsx +++ b/packages/ra-core/src/i18n/useTranslate.spec.tsx @@ -28,6 +28,7 @@ describe('useTranslate', () => { i18nProvider: { translate: () => 'hallo', changeLocale: () => Promise.resolve(), + getLocale: () => 'de', }, setLocale: () => Promise.resolve(), }} @@ -42,10 +43,10 @@ describe('useTranslate', () => { it('should use the i18n provider when using TranslationProvider', () => { const { queryAllByText } = renderWithRedux( 'bonjour', changeLocale: () => Promise.resolve(), + getLocale: () => 'fr', }} > diff --git a/packages/ra-core/src/types.ts b/packages/ra-core/src/types.ts index da71f9ce46b..0a538fa37c1 100644 --- a/packages/ra-core/src/types.ts +++ b/packages/ra-core/src/types.ts @@ -42,6 +42,7 @@ export type Translate = (key: string, options?: any) => string; export type I18nProvider = { translate: Translate; changeLocale: (locale: string, options?: any) => Promise; + getLocale: () => string; [key: string]: any; }; diff --git a/packages/ra-i18n-polyglot/src/index.ts b/packages/ra-i18n-polyglot/src/index.ts index 253b3a74cd1..27f6ce3cc72 100644 --- a/packages/ra-i18n-polyglot/src/index.ts +++ b/packages/ra-i18n-polyglot/src/index.ts @@ -24,6 +24,7 @@ export default ( initialLocale: string = 'en', polyglotOptions: any = {} ): I18nProvider => { + let locale = initialLocale; const messages = getMessages(initialLocale); if (messages instanceof Promise) { throw new Error( @@ -31,7 +32,7 @@ export default ( ); } const polyglot = new Polyglot({ - locale: initialLocale, + locale, phrases: { '': '', ...messages }, ...polyglotOptions, }); @@ -39,18 +40,20 @@ export default ( return { translate: (key: string, options: any = {}) => translate(key, options), - changeLocale: (locale: string) => + changeLocale: (newLocale: string) => new Promise(resolve => // so we systematically return a Promise for the messages // i18nProvider may return a Promise for language changes, - resolve(getMessages(locale as string)) + resolve(getMessages(newLocale as string)) ).then(messages => { + locale = newLocale; const newPolyglot = new Polyglot({ - locale, + locale: newLocale, phrases: { '': '', ...messages }, ...polyglotOptions, }); translate = newPolyglot.t.bind(newPolyglot); }), + getLocale: () => locale, }; };