Skip to content

Commit 322f98a

Browse files
committed
♻️(frontend) Refactor language-related code
- Refactors "useTranslationsCustomizer" to "useCustomTranslations" - Refactors "useLanguageSynchronizer" to "useSynchronizedLanguage" - Refactors "LanguagePicker" to better reflect its component role - Refactors "LanguagePicker" to use "useSynchronizedLangue" - Removes unused "useChangeUserLanguage" - To change the user language, use "useAuthMutation" instead Signed-off-by: Robin Weber <[email protected]>
1 parent 0054f0a commit 322f98a

File tree

13 files changed

+118
-229
lines changed

13 files changed

+118
-229
lines changed

src/frontend/apps/impress/src/features/auth/api/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ export interface User {
1111
email: string;
1212
full_name: string;
1313
short_name: string;
14-
language: string;
14+
language?: string;
1515
}

src/frontend/apps/impress/src/features/header/components/Header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import IconDocs from '@/assets/icons/icon-docs.svg';
55
import { Box, StyledLink } from '@/components/';
66
import { useCunninghamTheme } from '@/cunningham';
77
import { ButtonLogin } from '@/features/auth';
8-
import { LanguagePicker } from '@/features/language';
8+
import { LanguagePicker } from '@/features/language/components';
99
import { useResponsiveStore } from '@/stores';
1010

1111
import { HEADER_HEIGHT } from '../conf';

src/frontend/apps/impress/src/features/home/components/HomeHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Box } from '@/components';
66
import { useCunninghamTheme } from '@/cunningham';
77
import { ButtonTogglePanel, Title } from '@/features/header/';
88
import { LaGaufre } from '@/features/header/components/LaGaufre';
9-
import { LanguagePicker } from '@/features/language';
9+
import { LanguagePicker } from '@/features/language/components';
1010
import { useResponsiveStore } from '@/stores';
1111

1212
export const HEADER_HEIGHT = 91;

src/frontend/apps/impress/src/features/language/api/useChangeUserLanguage.tsx

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/frontend/apps/impress/src/features/language/LanguagePicker.tsx renamed to src/frontend/apps/impress/src/features/language/components/LanguagePicker.tsx

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,33 @@
1-
import { Settings } from 'luxon';
21
import { useMemo } from 'react';
32
import { useTranslation } from 'react-i18next';
43
import { css } from 'styled-components';
54

65
import { DropdownMenu, Icon, Text } from '@/components/';
76
import { useConfig } from '@/core';
87
import { useAuthQuery } from '@/features/auth';
9-
10-
import { useLanguageSynchronizer } from './hooks/useLanguageSynchronizer';
11-
import { getMatchingLocales } from './utils/locale';
8+
import {
9+
getMatchingLocales,
10+
useSynchronizedLanguage,
11+
} from '@/features/language';
1212

1313
export const LanguagePicker = () => {
1414
const { t, i18n } = useTranslation();
1515
const { data: conf } = useConfig();
1616
const { data: user } = useAuthQuery();
17-
const { synchronizeLanguage } = useLanguageSynchronizer();
18-
const language = i18n.languages[0];
19-
Settings.defaultLocale = language;
17+
const { changeLanguageSynchronized } = useSynchronizedLanguage();
18+
const language = i18n.language;
2019

2120
// Compute options for dropdown
2221
const optionsPicker = useMemo(() => {
2322
const backendOptions = conf?.LANGUAGES ?? [[language, language]];
24-
return backendOptions.map(([backendLocale, label]) => {
25-
// Determine if the option is selected
26-
const isSelected =
27-
getMatchingLocales([backendLocale], [language]).length > 0;
28-
// Define callback for updating both frontend and backend languages
29-
const callback = () => {
30-
i18n
31-
.changeLanguage(backendLocale)
32-
.then(() => {
33-
if (conf?.LANGUAGES && user) {
34-
synchronizeLanguage(conf.LANGUAGES, user, 'toBackend');
35-
}
36-
})
37-
.catch((err) => {
38-
console.error('Error changing language', err);
39-
});
23+
return backendOptions.map(([backendLocale, backendLabel]) => {
24+
return {
25+
label: backendLabel,
26+
isSelected: getMatchingLocales([backendLocale], [language]).length > 0,
27+
callback: () => changeLanguageSynchronized(backendLocale, user),
4028
};
41-
return { label, isSelected, callback };
4229
});
43-
}, [conf?.LANGUAGES, i18n, language, synchronizeLanguage, user]);
30+
}, [changeLanguageSynchronized, conf?.LANGUAGES, language, user]);
4431

4532
// Extract current language label for display
4633
const currentLanguageLabel =
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './LanguagePicker';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './useSynchronizedLanguage';
2+
export * from './useCustomTranslations';
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Resource } from 'i18next';
2+
import { useCallback } from 'react';
3+
import { useTranslation } from 'react-i18next';
4+
5+
export const useCustomTranslations = () => {
6+
const { i18n } = useTranslation();
7+
8+
// Overwrite translations with a resource
9+
const customizeTranslations = useCallback(
10+
(currentCustomTranslations: Resource) => {
11+
Object.entries(currentCustomTranslations).forEach(([lng, namespaces]) => {
12+
Object.entries(namespaces).forEach(([ns, value]) => {
13+
i18n.addResourceBundle(lng, ns, value, true, true);
14+
});
15+
});
16+
// trigger re-render
17+
if (Object.entries(currentCustomTranslations).length > 0) {
18+
void i18n.changeLanguage(i18n.language);
19+
}
20+
},
21+
[i18n],
22+
);
23+
24+
return {
25+
customizeTranslations,
26+
};
27+
};

src/frontend/apps/impress/src/features/language/hooks/useLanguageSynchronizer.ts

Lines changed: 0 additions & 70 deletions
This file was deleted.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { useCallback, useMemo, useRef } from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
4+
import { useUserUpdate } from '@/core/api/useUserUpdate';
5+
import { useConfig } from '@/core/config/api/useConfig';
6+
import { User } from '@/features/auth';
7+
import { getMatchingLocales } from '@/features/language/utils/locale';
8+
9+
export const useSynchronizedLanguage = () => {
10+
const { i18n } = useTranslation();
11+
const { mutateAsync: updateUser } = useUserUpdate();
12+
const { data: config } = useConfig();
13+
const isSynchronizingLanguage = useRef(false);
14+
15+
const availableFrontendLanguages = useMemo(
16+
() => Object.keys(i18n?.options?.resources || { en: '<- fallback' }),
17+
[i18n?.options?.resources],
18+
);
19+
const availableBackendLanguages = useMemo(
20+
() => config?.LANGUAGES?.map(([locale]) => locale) || [],
21+
[config?.LANGUAGES],
22+
);
23+
24+
const changeBackendLanguage = useCallback(
25+
async (language: string, user?: User) => {
26+
const closestBackendLanguage = getMatchingLocales(
27+
availableBackendLanguages,
28+
[language],
29+
)[0];
30+
31+
if (user && user.language !== closestBackendLanguage) {
32+
await updateUser({ id: user.id, language: closestBackendLanguage });
33+
}
34+
},
35+
[availableBackendLanguages, updateUser],
36+
);
37+
38+
const changeFrontendLanguage = useCallback(
39+
async (language: string) => {
40+
const closestFrontendLanguage = getMatchingLocales(
41+
availableFrontendLanguages,
42+
[language],
43+
)[0];
44+
if (
45+
i18n.isInitialized &&
46+
i18n.resolvedLanguage !== closestFrontendLanguage
47+
) {
48+
await i18n.changeLanguage(closestFrontendLanguage);
49+
}
50+
},
51+
[availableFrontendLanguages, i18n],
52+
);
53+
54+
const changeLanguageSynchronized = useCallback(
55+
async (language: string, user?: User) => {
56+
if (!isSynchronizingLanguage.current) {
57+
isSynchronizingLanguage.current = true;
58+
await changeFrontendLanguage(language);
59+
await changeBackendLanguage(language, user);
60+
isSynchronizingLanguage.current = false;
61+
}
62+
},
63+
[changeBackendLanguage, changeFrontendLanguage],
64+
);
65+
66+
return {
67+
changeLanguageSynchronized,
68+
changeFrontendLanguage,
69+
changeBackendLanguage,
70+
};
71+
};

src/frontend/apps/impress/src/features/language/hooks/useTranslationsCustomizer.ts

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './locale';

0 commit comments

Comments
 (0)