3

I've been using React-Admin v2 and now is time to upgrade to the next major version. My app has a functionality to change the language of the login page on page load, for example depending on the url params. If there isn't any param set, the language will be English, but if the url is for example myproject.tld/login/fi, the language will be Finnish. The language is changed within the componentDidMount by dispatching the CHANGE_LOCALE action, by using the changeLocale action creator.

However, I've read the upgrade guide and it says that the translation system will not work the same way anymore, it no longer uses redux. I've been able to get familiar with the new system, and I managed to create the buttons for language change, and it worked. It was easy to create a locale switcher function and use hook useSetLocale inside it.

But, I still have situations, when the language must be changed on the fly without any user action, meaning clicking the buttons. I can live with the fact, if I can't get the login page translated based on the link user clicks and lands to the site. The default is English, and I can create language links on the top-right corner, if user wants to see the login form in other language.

The more important thing still is to change the UI language after user login. Every user has their selected language saved to the user object in the backend, and after the successful login my React app knows the language code.

The main question: How can I change the language after login and only after that load all of the content? If I have understood correctly, the hooks can't be used for example within componentDidMount. The methods for changing the language that I'm currently aware are 1. the hook useSetLocale and 2. initializing it in App.js file like this:

import * as React from "react";
import { Admin, Resource } from 'react-admin';
import polyglotI18nProvider from 'ra-i18n-polyglot';
import jsonServerProvider from 'ra-data-json-server';
import UserList from './users';
import englishMessages from 'ra-language-english';
import finnishMessages from 'ra-language-finnish';

const messages = {
    fi: finnishMessages,
    en: englishMessages,
};

const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');
const i18nProvider = polyglotI18nProvider(locale => messages[locale], "en");

const App = () => (
    <Admin
        dataProvider={dataProvider}
        i18nProvider={i18nProvider}
    >
        <Resource name="users" list={UserList} />
    </Admin>
);

export default App;

Of course that is just a test code and the code is not currently using my real backend. But is there any way to change the language after successful login and when the user chosen language is known? Is there some third way to force the language, or can I somehow re-initialize the i18nProvider that has been set to use English by default?

spexi
  • 59
  • 6

1 Answers1

5

So you're basically trying to communicate between the authProvider (getting the user prefrences) and the i18nProvider. React-admin doesn't provide any system to do so, but you can do it any way you want, e.g. using localStorage.

For instance, let's say that your authProvider gets the user locale at login. It should store it in localStorage:

const authProvider = {
    login: async ({ username, password }) => {
        // fetch the auth API and grab the user locale in the response, then
        localStorage.setItem('locale', locale);
     },
     // ...
}

Next, you need to execute an effect (changing the locale) when the app mounts. The best place to do that is in the app layout. Here is how to wrap the default layout in another component that calls i18nProvider.setLocale() on mount:

import React, { useEffect } from 'react';
import { Layout, useSetLocale } from 'react-admin';

const MyLayout = props => {
    const setLocale = useSetLocale();
    useEffect(() => {
        const locale = localStorage.getItem('locale');
        setLocale(locale);
    }, [setLocale]); // execute on mount

    return <Layout {...props} />;
}

Then you just have to plug your custom layout in the <Admin> component as follows:

const App = () => (
    <Admin
        layout={MyLayout}
        dataProvider={dataProvider}
        i18nProvider={i18nProvider}
    >
        <Resource name="users" list={UserList} />
    </Admin>
);
Dharman
  • 30,962
  • 25
  • 85
  • 135
François Zaninotto
  • 7,068
  • 2
  • 35
  • 56