Skip to content

Commit

Permalink
Fix getting and setting the locale via useLocale hook
Browse files Browse the repository at this point in the history
Change the LocaleObserver to not store the locale locally and instead
always rely on useLocale. This means useLocale is the new source of
truth for the current active locale for the whole application.

Therefore ensure that useLocale returns the applied locale. Either fromi
the store when the user is logged in or as a fallback directly from i18n
when the user is not logged in or the locale has not been dispatched to
the store yet.
  • Loading branch information
bjoernricks committed May 7, 2024
1 parent 828105c commit 5f05eb5
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 20 deletions.
22 changes: 10 additions & 12 deletions src/web/components/observer/localeobserver.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, {useEffect, useState} from 'react';
import React, {useEffect} from 'react';

import {onLanguageChange} from 'gmp/locale/lang';
import {onLanguageChange, getLocale} from 'gmp/locale/lang';

import {isDefined} from 'gmp/utils/identity';

Expand All @@ -28,23 +28,21 @@ import useLocale from 'web/hooks/useLocale';
* re-renders its children whenever the locale changed
*/
const LocaleObserver = ({children}) => {
const [locale, setLocaleState] = useState();
const [, setLocale] = useLocale();
const [locale, setLocale] = useLocale();

useEffect(() => {
const unsubscribeFromLanguageChange = onLanguageChange(newLocale => {
setLocaleState(newLocale);
setLocale(newLocale);
});
const unsubscribeFromLanguageChange = onLanguageChange(setLocale);
return unsubscribeFromLanguageChange;
}, [setLocale, setLocaleState]);
}, [setLocale]);

if (!isDefined(locale)) {
// don't render if no locale has been detected yet
const currentLocale = locale || getLocale();

if (!isDefined(currentLocale)) {
// don't render if no locale has been set yet
return null;
}

return <React.Fragment key={locale}>{children}</React.Fragment>;
return <React.Fragment key={currentLocale}>{children}</React.Fragment>;
};

export default LocaleObserver;
12 changes: 6 additions & 6 deletions src/web/hooks/__tests__/useLocale.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,29 @@ const TestComponent = () => {
};

describe('useLocale Tests', () => {
test('should render the locale from the store', async () => {
test('should render the locale from the store', () => {
const {store, render} = rendererWith({store: true});

store.dispatch(setLocale('de'));

render(<TestComponent />);

const element = await screen.getByTestId('locale');
const element = screen.getByTestId('locale');
expect(element).toHaveTextContent('de');
});

test('should allow to change the locale in the store', async () => {
test('should allow to change the locale in the store', () => {
const {store, render} = rendererWith({store: true});

store.dispatch(setLocale('de'));

render(<TestComponent />);

const element = await screen.getByTestId('locale');
const element = screen.getByTestId('locale');
expect(element).toHaveTextContent('de');

const button = await screen.getByTestId('changeLocale');
await fireEvent.click(button);
const button = screen.getByTestId('changeLocale');
fireEvent.click(button);

expect(element).toHaveTextContent('en');
});
Expand Down
28 changes: 26 additions & 2 deletions src/web/hooks/useLocale.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,43 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {useCallback} from 'react';

import {useSelector, useDispatch} from 'react-redux';

import {setLocale as setGlobalLocale} from 'gmp/locale/lang';

import {setLocale} from 'web/store/usersettings/actions';
import {getLocale} from 'web/store/usersettings/selectors';

async function wait(ms = 0) {
new Promise(resolve => {
setTimeout(resolve, ms);
});
}
/**
* Hook to get current locale from the store and change it
* Hook to get current locale and allow to change it
*
* @returns Array of the current locale and a function to change the locale
*/
const useLocale = () => {
const dispatch = useDispatch();
return [useSelector(getLocale), locale => dispatch(setLocale(locale))];
const currentLocale = useSelector(getLocale);

const changeLocale = useCallback(
async newLocale => {
if (currentLocale !== newLocale) {
dispatch(setLocale(newLocale));

// allow the locale to be set in the store before the next render
await wait();

setGlobalLocale(newLocale);
}
},
[currentLocale, dispatch],
);
return [currentLocale, changeLocale];
};

export default useLocale;

0 comments on commit 5f05eb5

Please sign in to comment.